blob: de6516f1065be9fb106e71894f22f5ff34b0f2f0 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +05302/*
3 * SPI flash probing
4 *
5 * Copyright (C) 2008 Atmel Corporation
6 * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik
7 * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc.
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +05308 */
9
10#include <common.h>
Simon Glass6f7d2592014-10-13 23:42:07 -060011#include <dm.h>
Simon Glassc10c3d42014-10-13 23:42:05 -060012#include <errno.h>
Chin-Ting Kuo77636df2022-08-19 17:01:09 +080013#include <linux/mtd/spi-nor.h>
Simon Glass0f2af882020-05-10 11:40:05 -060014#include <log.h>
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053015#include <malloc.h>
16#include <spi.h>
17#include <spi_flash.h>
Chin-Ting Kuo77636df2022-08-19 17:01:09 +080018#include <spi-mem.h>
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053019
Jagannadha Sutradharudu Tekiac8242d2013-09-26 16:00:15 +053020#include "sf_internal.h"
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053021
Chin-Ting Kuo77636df2022-08-19 17:01:09 +080022static int spi_nor_create_read_dirmap(struct spi_nor *nor)
23{
24 struct spi_mem_dirmap_info info = {
25 .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
26 SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),
27 SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
28 SPI_MEM_OP_DATA_IN(0, NULL, 0)),
29 .offset = 0,
30 .length = nor->mtd.size,
31 };
32 struct spi_mem_op *op = &info.op_tmpl;
33
34 /* get transfer protocols. */
35 spi_nor_setup_op(nor, op, nor->read_proto);
36 op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
37
38 /* convert the dummy cycles to the number of bytes */
39 op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8;
40 if (spi_nor_protocol_is_dtr(nor->read_proto))
41 op->dummy.nbytes *= 2;
42
43 nor->dirmap.rdesc = spi_mem_dirmap_create(nor->spi, &info);
44 if (IS_ERR(nor->dirmap.rdesc))
45 return PTR_ERR(nor->dirmap.rdesc);
46
47 return 0;
48}
49
50static int spi_nor_create_write_dirmap(struct spi_nor *nor)
51{
52 struct spi_mem_dirmap_info info = {
53 .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0),
54 SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),
55 SPI_MEM_OP_NO_DUMMY,
56 SPI_MEM_OP_DATA_OUT(0, NULL, 0)),
57 .offset = 0,
58 .length = nor->mtd.size,
59 };
60 struct spi_mem_op *op = &info.op_tmpl;
61
62 /* get transfer protocols. */
63 spi_nor_setup_op(nor, op, nor->write_proto);
64 op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
65
66 if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
67 op->addr.nbytes = 0;
68
69 nor->dirmap.wdesc = spi_mem_dirmap_create(nor->spi, &info);
70 if (IS_ERR(nor->dirmap.wdesc))
71 return PTR_ERR(nor->dirmap.wdesc);
72
73 return 0;
74}
75
Simon Glassc10c3d42014-10-13 23:42:05 -060076/**
77 * spi_flash_probe_slave() - Probe for a SPI flash device on a bus
78 *
Simon Glassc10c3d42014-10-13 23:42:05 -060079 * @flashp: Pointer to place to put flash info, which may be NULL if the
80 * space should be allocated
81 */
Jagan Tekic772eaa2015-12-06 21:44:12 +053082static int spi_flash_probe_slave(struct spi_flash *flash)
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053083{
Jagan Teki4abfb982015-12-06 21:33:32 +053084 struct spi_slave *spi = flash->spi;
Jagannadha Sutradharudu Tekibb302202013-09-24 16:01:23 +053085 int ret;
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053086
Jagannadha Sutradharudu Tekibb302202013-09-24 16:01:23 +053087 /* Setup spi_slave */
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053088 if (!spi) {
89 printf("SF: Failed to set up slave\n");
Simon Glassc10c3d42014-10-13 23:42:05 -060090 return -ENODEV;
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053091 }
92
Jagannadha Sutradharudu Tekibb302202013-09-24 16:01:23 +053093 /* Claim spi bus */
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053094 ret = spi_claim_bus(spi);
95 if (ret) {
96 debug("SF: Failed to claim SPI bus: %d\n", ret);
Simon Glassc10c3d42014-10-13 23:42:05 -060097 return ret;
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053098 }
99
Vignesh R14510412019-02-05 11:29:23 +0530100 ret = spi_nor_scan(flash);
Simon Glass07f66712016-02-24 09:14:56 -0700101 if (ret)
Jagannadha Sutradharudu Tekibb302202013-09-24 16:01:23 +0530102 goto err_read_id;
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +0530103
Chin-Ting Kuo77636df2022-08-19 17:01:09 +0800104 if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
105 ret = spi_nor_create_read_dirmap(flash);
106 if (ret)
107 return ret;
108
109 ret = spi_nor_create_write_dirmap(flash);
110 if (ret)
111 return ret;
112 }
113
Jagan Teki380cdbe2020-05-13 00:11:27 +0530114 if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
115 ret = spi_flash_mtd_register(flash);
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +0530116
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +0530117err_read_id:
118 spi_release_bus(spi);
Simon Glassc10c3d42014-10-13 23:42:05 -0600119 return ret;
120}
121
Lukasz Majewski76f442982020-06-04 23:11:53 +0800122#if !CONFIG_IS_ENABLED(DM_SPI_FLASH)
Mario Six5f4c1342018-01-15 11:08:42 +0100123struct spi_flash *spi_flash_probe(unsigned int busnum, unsigned int cs,
124 unsigned int max_hz, unsigned int spi_mode)
Simon Glassc10c3d42014-10-13 23:42:05 -0600125{
Mario Six5f4c1342018-01-15 11:08:42 +0100126 struct spi_slave *bus;
Simon Glassc10c3d42014-10-13 23:42:05 -0600127 struct spi_flash *flash;
128
Mario Six5f4c1342018-01-15 11:08:42 +0100129 bus = spi_setup_slave(busnum, cs, max_hz, spi_mode);
130 if (!bus)
131 return NULL;
132
Simon Glassc10c3d42014-10-13 23:42:05 -0600133 /* Allocate space if needed (not used by sf-uclass */
134 flash = calloc(1, sizeof(*flash));
135 if (!flash) {
136 debug("SF: Failed to allocate spi_flash\n");
137 return NULL;
138 }
139
Jagan Teki4abfb982015-12-06 21:33:32 +0530140 flash->spi = bus;
141 if (spi_flash_probe_slave(flash)) {
Simon Glassc10c3d42014-10-13 23:42:05 -0600142 spi_free_slave(bus);
143 free(flash);
144 return NULL;
145 }
146
147 return flash;
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +0530148}
149
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +0530150void spi_flash_free(struct spi_flash *flash)
151{
Chin-Ting Kuo77636df2022-08-19 17:01:09 +0800152 if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
153 spi_mem_dirmap_destroy(flash->dirmap.wdesc);
154 spi_mem_dirmap_destroy(flash->dirmap.rdesc);
155 }
156
Jagan Teki380cdbe2020-05-13 00:11:27 +0530157 if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
Marek Behúnc1fcc142021-05-26 14:08:20 +0200158 spi_flash_mtd_unregister(flash);
Jagan Teki380cdbe2020-05-13 00:11:27 +0530159
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +0530160 spi_free_slave(flash->spi);
161 free(flash);
162}
Simon Glass6f7d2592014-10-13 23:42:07 -0600163
164#else /* defined CONFIG_DM_SPI_FLASH */
165
166static int spi_flash_std_read(struct udevice *dev, u32 offset, size_t len,
167 void *buf)
168{
Simon Glassde0977b2015-03-05 12:25:20 -0700169 struct spi_flash *flash = dev_get_uclass_priv(dev);
Vignesh R14510412019-02-05 11:29:23 +0530170 struct mtd_info *mtd = &flash->mtd;
171 size_t retlen;
Simon Glass6f7d2592014-10-13 23:42:07 -0600172
Vignesh R14510412019-02-05 11:29:23 +0530173 return log_ret(mtd->_read(mtd, offset, len, &retlen, buf));
Simon Glass6f7d2592014-10-13 23:42:07 -0600174}
175
Jagan Tekic772eaa2015-12-06 21:44:12 +0530176static int spi_flash_std_write(struct udevice *dev, u32 offset, size_t len,
Mario Sixc9214d82018-01-15 11:08:40 +0100177 const void *buf)
Simon Glass6f7d2592014-10-13 23:42:07 -0600178{
Simon Glassde0977b2015-03-05 12:25:20 -0700179 struct spi_flash *flash = dev_get_uclass_priv(dev);
Vignesh R14510412019-02-05 11:29:23 +0530180 struct mtd_info *mtd = &flash->mtd;
181 size_t retlen;
Simon Glass6f7d2592014-10-13 23:42:07 -0600182
Vignesh R14510412019-02-05 11:29:23 +0530183 return mtd->_write(mtd, offset, len, &retlen, buf);
Simon Glass6f7d2592014-10-13 23:42:07 -0600184}
185
Jagan Tekic772eaa2015-12-06 21:44:12 +0530186static int spi_flash_std_erase(struct udevice *dev, u32 offset, size_t len)
Simon Glass6f7d2592014-10-13 23:42:07 -0600187{
Simon Glassde0977b2015-03-05 12:25:20 -0700188 struct spi_flash *flash = dev_get_uclass_priv(dev);
Vignesh R14510412019-02-05 11:29:23 +0530189 struct mtd_info *mtd = &flash->mtd;
190 struct erase_info instr;
191
Simon Glass3190f1e2023-05-04 16:50:47 -0600192 if (!mtd->erasesize ||
193 (offset % mtd->erasesize || len % mtd->erasesize)) {
Simon Glass03f31012020-07-19 10:15:32 -0600194 debug("SF: Erase offset/length not multiple of erase size\n");
Vignesh R14510412019-02-05 11:29:23 +0530195 return -EINVAL;
196 }
197
198 memset(&instr, 0, sizeof(instr));
199 instr.addr = offset;
200 instr.len = len;
Simon Glass6f7d2592014-10-13 23:42:07 -0600201
Vignesh R14510412019-02-05 11:29:23 +0530202 return mtd->_erase(mtd, &instr);
Simon Glass6f7d2592014-10-13 23:42:07 -0600203}
204
Simon Glass61d04642021-03-15 18:11:17 +1300205static int spi_flash_std_get_sw_write_prot(struct udevice *dev)
206{
207 struct spi_flash *flash = dev_get_uclass_priv(dev);
208
209 return spi_flash_cmd_get_sw_write_prot(flash);
210}
211
Simon Glassa104b642019-12-06 21:42:50 -0700212int spi_flash_std_probe(struct udevice *dev)
Simon Glass6f7d2592014-10-13 23:42:07 -0600213{
Simon Glassde44acf2015-09-28 23:32:01 -0600214 struct spi_slave *slave = dev_get_parent_priv(dev);
Simon Glass6f7d2592014-10-13 23:42:07 -0600215 struct spi_flash *flash;
216
Simon Glassde0977b2015-03-05 12:25:20 -0700217 flash = dev_get_uclass_priv(dev);
Simon Glass6f7d2592014-10-13 23:42:07 -0600218 flash->dev = dev;
Jagan Teki4abfb982015-12-06 21:33:32 +0530219 flash->spi = slave;
Jagan Teki4abfb982015-12-06 21:33:32 +0530220 return spi_flash_probe_slave(flash);
Simon Glass6f7d2592014-10-13 23:42:07 -0600221}
222
Boris Brezillon4be0c812018-12-02 10:54:31 +0100223static int spi_flash_std_remove(struct udevice *dev)
224{
Marek Behúnc1fcc142021-05-26 14:08:20 +0200225 struct spi_flash *flash = dev_get_uclass_priv(dev);
Pratyush Yadav46103502021-06-26 00:47:24 +0530226 int ret;
227
Chin-Ting Kuo77636df2022-08-19 17:01:09 +0800228 if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
229 spi_mem_dirmap_destroy(flash->dirmap.wdesc);
230 spi_mem_dirmap_destroy(flash->dirmap.rdesc);
231 }
232
Pratyush Yadav46103502021-06-26 00:47:24 +0530233 ret = spi_nor_remove(flash);
234 if (ret)
235 return ret;
Marek Behúnc1fcc142021-05-26 14:08:20 +0200236
Jagan Teki380cdbe2020-05-13 00:11:27 +0530237 if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
Marek Behúnc1fcc142021-05-26 14:08:20 +0200238 spi_flash_mtd_unregister(flash);
Jagan Teki380cdbe2020-05-13 00:11:27 +0530239
Boris Brezillon4be0c812018-12-02 10:54:31 +0100240 return 0;
241}
242
Simon Glass6f7d2592014-10-13 23:42:07 -0600243static const struct dm_spi_flash_ops spi_flash_std_ops = {
244 .read = spi_flash_std_read,
245 .write = spi_flash_std_write,
246 .erase = spi_flash_std_erase,
Simon Glass61d04642021-03-15 18:11:17 +1300247 .get_sw_write_prot = spi_flash_std_get_sw_write_prot,
Simon Glass6f7d2592014-10-13 23:42:07 -0600248};
249
250static const struct udevice_id spi_flash_std_ids[] = {
Vignesh R54c8a672019-02-05 11:29:22 +0530251 { .compatible = "jedec,spi-nor" },
Simon Glass6f7d2592014-10-13 23:42:07 -0600252 { }
253};
254
Walter Lozano2901ac62020-06-25 01:10:04 -0300255U_BOOT_DRIVER(jedec_spi_nor) = {
256 .name = "jedec_spi_nor",
Simon Glass6f7d2592014-10-13 23:42:07 -0600257 .id = UCLASS_SPI_FLASH,
258 .of_match = spi_flash_std_ids,
259 .probe = spi_flash_std_probe,
Boris Brezillon4be0c812018-12-02 10:54:31 +0100260 .remove = spi_flash_std_remove,
Simon Glassb33cd252020-12-19 10:40:01 -0700261 .priv_auto = sizeof(struct spi_nor),
Simon Glass6f7d2592014-10-13 23:42:07 -0600262 .ops = &spi_flash_std_ops,
Pratyush Yadav46103502021-06-26 00:47:24 +0530263 .flags = DM_FLAG_OS_PREPARE,
Simon Glass6f7d2592014-10-13 23:42:07 -0600264};
265
Simon Glassdf65db82020-12-28 20:34:57 -0700266DM_DRIVER_ALIAS(jedec_spi_nor, spansion_m25p16)
Walter Lozano48e5b042020-06-25 01:10:06 -0300267
Simon Glass6f7d2592014-10-13 23:42:07 -0600268#endif /* CONFIG_DM_SPI_FLASH */