1 // SPDX-License-Identifier: GPL-2.0+
2 /*
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.
8 */
9
10 #include <common.h>
11 #include <dm.h>
12 #include <errno.h>
13 #include <linux/mtd/spi-nor.h>
14 #include <log.h>
15 #include <malloc.h>
16 #include <spi.h>
17 #include <spi_flash.h>
18 #include <spi-mem.h>
19
20 #include "sf_internal.h"
21
spi_nor_create_read_dirmap(struct spi_nor * nor)22 static 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
spi_nor_create_write_dirmap(struct spi_nor * nor)50 static 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
76 /**
77 * spi_flash_probe_slave() - Probe for a SPI flash device on a bus
78 *
79 * @flashp: Pointer to place to put flash info, which may be NULL if the
80 * space should be allocated
81 */
spi_flash_probe_slave(struct spi_flash * flash)82 static int spi_flash_probe_slave(struct spi_flash *flash)
83 {
84 struct spi_slave *spi = flash->spi;
85 int ret;
86
87 /* Setup spi_slave */
88 if (!spi) {
89 printf("SF: Failed to set up slave\n");
90 return -ENODEV;
91 }
92
93 /* Claim spi bus */
94 ret = spi_claim_bus(spi);
95 if (ret) {
96 debug("SF: Failed to claim SPI bus: %d\n", ret);
97 return ret;
98 }
99
100 ret = spi_nor_scan(flash);
101 if (ret)
102 goto err_read_id;
103
104 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
114 if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
115 ret = spi_flash_mtd_register(flash);
116
117 err_read_id:
118 spi_release_bus(spi);
119 return ret;
120 }
121
122 #if !CONFIG_IS_ENABLED(DM_SPI_FLASH)
spi_flash_probe(unsigned int busnum,unsigned int cs,unsigned int max_hz,unsigned int spi_mode)123 struct spi_flash *spi_flash_probe(unsigned int busnum, unsigned int cs,
124 unsigned int max_hz, unsigned int spi_mode)
125 {
126 struct spi_slave *bus;
127 struct spi_flash *flash;
128
129 bus = spi_setup_slave(busnum, cs, max_hz, spi_mode);
130 if (!bus)
131 return NULL;
132
133 /* 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
140 flash->spi = bus;
141 if (spi_flash_probe_slave(flash)) {
142 spi_free_slave(bus);
143 free(flash);
144 return NULL;
145 }
146
147 return flash;
148 }
149
spi_flash_free(struct spi_flash * flash)150 void spi_flash_free(struct spi_flash *flash)
151 {
152 if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
153 spi_mem_dirmap_destroy(flash->dirmap.wdesc);
154 spi_mem_dirmap_destroy(flash->dirmap.rdesc);
155 }
156
157 if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
158 spi_flash_mtd_unregister(flash);
159
160 spi_free_slave(flash->spi);
161 free(flash);
162 }
163
164 #else /* defined CONFIG_DM_SPI_FLASH */
165
spi_flash_std_read(struct udevice * dev,u32 offset,size_t len,void * buf)166 static int spi_flash_std_read(struct udevice *dev, u32 offset, size_t len,
167 void *buf)
168 {
169 struct spi_flash *flash = dev_get_uclass_priv(dev);
170 struct mtd_info *mtd = &flash->mtd;
171 size_t retlen;
172
173 return log_ret(mtd->_read(mtd, offset, len, &retlen, buf));
174 }
175
spi_flash_std_write(struct udevice * dev,u32 offset,size_t len,const void * buf)176 static int spi_flash_std_write(struct udevice *dev, u32 offset, size_t len,
177 const void *buf)
178 {
179 struct spi_flash *flash = dev_get_uclass_priv(dev);
180 struct mtd_info *mtd = &flash->mtd;
181 size_t retlen;
182
183 return mtd->_write(mtd, offset, len, &retlen, buf);
184 }
185
spi_flash_std_erase(struct udevice * dev,u32 offset,size_t len)186 static int spi_flash_std_erase(struct udevice *dev, u32 offset, size_t len)
187 {
188 struct spi_flash *flash = dev_get_uclass_priv(dev);
189 struct mtd_info *mtd = &flash->mtd;
190 struct erase_info instr;
191
192 if (!mtd->erasesize ||
193 (offset % mtd->erasesize || len % mtd->erasesize)) {
194 debug("SF: Erase offset/length not multiple of erase size\n");
195 return -EINVAL;
196 }
197
198 memset(&instr, 0, sizeof(instr));
199 instr.addr = offset;
200 instr.len = len;
201
202 return mtd->_erase(mtd, &instr);
203 }
204
spi_flash_std_get_sw_write_prot(struct udevice * dev)205 static 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
spi_flash_std_probe(struct udevice * dev)212 int spi_flash_std_probe(struct udevice *dev)
213 {
214 struct spi_slave *slave = dev_get_parent_priv(dev);
215 struct spi_flash *flash;
216
217 flash = dev_get_uclass_priv(dev);
218 flash->dev = dev;
219 flash->spi = slave;
220 return spi_flash_probe_slave(flash);
221 }
222
spi_flash_std_remove(struct udevice * dev)223 static int spi_flash_std_remove(struct udevice *dev)
224 {
225 struct spi_flash *flash = dev_get_uclass_priv(dev);
226 int ret;
227
228 if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
229 spi_mem_dirmap_destroy(flash->dirmap.wdesc);
230 spi_mem_dirmap_destroy(flash->dirmap.rdesc);
231 }
232
233 ret = spi_nor_remove(flash);
234 if (ret)
235 return ret;
236
237 if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
238 spi_flash_mtd_unregister(flash);
239
240 return 0;
241 }
242
243 static 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,
247 .get_sw_write_prot = spi_flash_std_get_sw_write_prot,
248 };
249
250 static const struct udevice_id spi_flash_std_ids[] = {
251 { .compatible = "jedec,spi-nor" },
252 { }
253 };
254
255 U_BOOT_DRIVER(jedec_spi_nor) = {
256 .name = "jedec_spi_nor",
257 .id = UCLASS_SPI_FLASH,
258 .of_match = spi_flash_std_ids,
259 .probe = spi_flash_std_probe,
260 .remove = spi_flash_std_remove,
261 .priv_auto = sizeof(struct spi_nor),
262 .ops = &spi_flash_std_ops,
263 .flags = DM_FLAG_OS_PREPARE,
264 };
265
266 DM_DRIVER_ALIAS(jedec_spi_nor, spansion_m25p16)
267
268 #endif /* CONFIG_DM_SPI_FLASH */
269