blob: 7100b64bf22aae56f74fb2588af8fa0440ab0d9e [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
Simon Glass6f7d2592014-10-13 23:42:07 -060010#include <dm.h>
Simon Glassc10c3d42014-10-13 23:42:05 -060011#include <errno.h>
Chin-Ting Kuo77636df2022-08-19 17:01:09 +080012#include <linux/mtd/spi-nor.h>
Simon Glass0f2af882020-05-10 11:40:05 -060013#include <log.h>
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053014#include <malloc.h>
15#include <spi.h>
16#include <spi_flash.h>
Chin-Ting Kuo77636df2022-08-19 17:01:09 +080017#include <spi-mem.h>
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053018
Jagannadha Sutradharudu Tekiac8242d2013-09-26 16:00:15 +053019#include "sf_internal.h"
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053020
Chin-Ting Kuo77636df2022-08-19 17:01:09 +080021static int spi_nor_create_read_dirmap(struct spi_nor *nor)
22{
23 struct spi_mem_dirmap_info info = {
24 .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
25 SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),
26 SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
27 SPI_MEM_OP_DATA_IN(0, NULL, 0)),
28 .offset = 0,
29 .length = nor->mtd.size,
30 };
31 struct spi_mem_op *op = &info.op_tmpl;
32
33 /* get transfer protocols. */
34 spi_nor_setup_op(nor, op, nor->read_proto);
35 op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
36
37 /* convert the dummy cycles to the number of bytes */
38 op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8;
39 if (spi_nor_protocol_is_dtr(nor->read_proto))
40 op->dummy.nbytes *= 2;
41
42 nor->dirmap.rdesc = spi_mem_dirmap_create(nor->spi, &info);
43 if (IS_ERR(nor->dirmap.rdesc))
44 return PTR_ERR(nor->dirmap.rdesc);
45
46 return 0;
47}
48
49static int spi_nor_create_write_dirmap(struct spi_nor *nor)
50{
51 struct spi_mem_dirmap_info info = {
52 .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0),
53 SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),
54 SPI_MEM_OP_NO_DUMMY,
55 SPI_MEM_OP_DATA_OUT(0, NULL, 0)),
56 .offset = 0,
57 .length = nor->mtd.size,
58 };
59 struct spi_mem_op *op = &info.op_tmpl;
60
61 /* get transfer protocols. */
62 spi_nor_setup_op(nor, op, nor->write_proto);
63 op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
64
65 if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
66 op->addr.nbytes = 0;
67
68 nor->dirmap.wdesc = spi_mem_dirmap_create(nor->spi, &info);
69 if (IS_ERR(nor->dirmap.wdesc))
70 return PTR_ERR(nor->dirmap.wdesc);
71
72 return 0;
73}
74
Simon Glassc10c3d42014-10-13 23:42:05 -060075/**
76 * spi_flash_probe_slave() - Probe for a SPI flash device on a bus
77 *
Simon Glassc10c3d42014-10-13 23:42:05 -060078 * @flashp: Pointer to place to put flash info, which may be NULL if the
79 * space should be allocated
80 */
Jagan Tekic772eaa2015-12-06 21:44:12 +053081static int spi_flash_probe_slave(struct spi_flash *flash)
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053082{
Jagan Teki4abfb982015-12-06 21:33:32 +053083 struct spi_slave *spi = flash->spi;
Jagannadha Sutradharudu Tekibb302202013-09-24 16:01:23 +053084 int ret;
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053085
Jagannadha Sutradharudu Tekibb302202013-09-24 16:01:23 +053086 /* Setup spi_slave */
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053087 if (!spi) {
88 printf("SF: Failed to set up slave\n");
Simon Glassc10c3d42014-10-13 23:42:05 -060089 return -ENODEV;
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053090 }
91
Jagannadha Sutradharudu Tekibb302202013-09-24 16:01:23 +053092 /* Claim spi bus */
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053093 ret = spi_claim_bus(spi);
94 if (ret) {
95 debug("SF: Failed to claim SPI bus: %d\n", ret);
Simon Glassc10c3d42014-10-13 23:42:05 -060096 return ret;
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +053097 }
98
Vignesh R14510412019-02-05 11:29:23 +053099 ret = spi_nor_scan(flash);
Simon Glass07f66712016-02-24 09:14:56 -0700100 if (ret)
Jagannadha Sutradharudu Tekibb302202013-09-24 16:01:23 +0530101 goto err_read_id;
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +0530102
Chin-Ting Kuo77636df2022-08-19 17:01:09 +0800103 if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
104 ret = spi_nor_create_read_dirmap(flash);
105 if (ret)
106 return ret;
107
108 ret = spi_nor_create_write_dirmap(flash);
109 if (ret)
110 return ret;
111 }
112
Jagan Teki380cdbe2020-05-13 00:11:27 +0530113 if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
114 ret = spi_flash_mtd_register(flash);
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +0530115
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +0530116err_read_id:
117 spi_release_bus(spi);
Simon Glassc10c3d42014-10-13 23:42:05 -0600118 return ret;
119}
120
Lukasz Majewski76f442982020-06-04 23:11:53 +0800121#if !CONFIG_IS_ENABLED(DM_SPI_FLASH)
Mario Six5f4c1342018-01-15 11:08:42 +0100122struct spi_flash *spi_flash_probe(unsigned int busnum, unsigned int cs,
123 unsigned int max_hz, unsigned int spi_mode)
Simon Glassc10c3d42014-10-13 23:42:05 -0600124{
Mario Six5f4c1342018-01-15 11:08:42 +0100125 struct spi_slave *bus;
Simon Glassc10c3d42014-10-13 23:42:05 -0600126 struct spi_flash *flash;
127
Mario Six5f4c1342018-01-15 11:08:42 +0100128 bus = spi_setup_slave(busnum, cs, max_hz, spi_mode);
129 if (!bus)
130 return NULL;
131
Simon Glassc10c3d42014-10-13 23:42:05 -0600132 /* Allocate space if needed (not used by sf-uclass */
133 flash = calloc(1, sizeof(*flash));
134 if (!flash) {
135 debug("SF: Failed to allocate spi_flash\n");
136 return NULL;
137 }
138
Jagan Teki4abfb982015-12-06 21:33:32 +0530139 flash->spi = bus;
140 if (spi_flash_probe_slave(flash)) {
Simon Glassc10c3d42014-10-13 23:42:05 -0600141 spi_free_slave(bus);
142 free(flash);
143 return NULL;
144 }
145
146 return flash;
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +0530147}
148
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +0530149void spi_flash_free(struct spi_flash *flash)
150{
Chin-Ting Kuo77636df2022-08-19 17:01:09 +0800151 if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
152 spi_mem_dirmap_destroy(flash->dirmap.wdesc);
153 spi_mem_dirmap_destroy(flash->dirmap.rdesc);
154 }
155
Jagan Teki380cdbe2020-05-13 00:11:27 +0530156 if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
Marek Behúnc1fcc142021-05-26 14:08:20 +0200157 spi_flash_mtd_unregister(flash);
Jagan Teki380cdbe2020-05-13 00:11:27 +0530158
Jagannadha Sutradharudu Tekie9a8b822013-08-29 19:01:56 +0530159 spi_free_slave(flash->spi);
160 free(flash);
161}
Simon Glass6f7d2592014-10-13 23:42:07 -0600162
163#else /* defined CONFIG_DM_SPI_FLASH */
164
165static int spi_flash_std_read(struct udevice *dev, u32 offset, size_t len,
166 void *buf)
167{
Simon Glassde0977b2015-03-05 12:25:20 -0700168 struct spi_flash *flash = dev_get_uclass_priv(dev);
Vignesh R14510412019-02-05 11:29:23 +0530169 struct mtd_info *mtd = &flash->mtd;
170 size_t retlen;
Simon Glass6f7d2592014-10-13 23:42:07 -0600171
Vignesh R14510412019-02-05 11:29:23 +0530172 return log_ret(mtd->_read(mtd, offset, len, &retlen, buf));
Simon Glass6f7d2592014-10-13 23:42:07 -0600173}
174
Jagan Tekic772eaa2015-12-06 21:44:12 +0530175static int spi_flash_std_write(struct udevice *dev, u32 offset, size_t len,
Mario Sixc9214d82018-01-15 11:08:40 +0100176 const void *buf)
Simon Glass6f7d2592014-10-13 23:42:07 -0600177{
Simon Glassde0977b2015-03-05 12:25:20 -0700178 struct spi_flash *flash = dev_get_uclass_priv(dev);
Vignesh R14510412019-02-05 11:29:23 +0530179 struct mtd_info *mtd = &flash->mtd;
180 size_t retlen;
Simon Glass6f7d2592014-10-13 23:42:07 -0600181
Vignesh R14510412019-02-05 11:29:23 +0530182 return mtd->_write(mtd, offset, len, &retlen, buf);
Simon Glass6f7d2592014-10-13 23:42:07 -0600183}
184
Jagan Tekic772eaa2015-12-06 21:44:12 +0530185static int spi_flash_std_erase(struct udevice *dev, u32 offset, size_t len)
Simon Glass6f7d2592014-10-13 23:42:07 -0600186{
Simon Glassde0977b2015-03-05 12:25:20 -0700187 struct spi_flash *flash = dev_get_uclass_priv(dev);
Vignesh R14510412019-02-05 11:29:23 +0530188 struct mtd_info *mtd = &flash->mtd;
189 struct erase_info instr;
190
Simon Glass3190f1e2023-05-04 16:50:47 -0600191 if (!mtd->erasesize ||
192 (offset % mtd->erasesize || len % mtd->erasesize)) {
Simon Glass03f31012020-07-19 10:15:32 -0600193 debug("SF: Erase offset/length not multiple of erase size\n");
Vignesh R14510412019-02-05 11:29:23 +0530194 return -EINVAL;
195 }
196
197 memset(&instr, 0, sizeof(instr));
198 instr.addr = offset;
199 instr.len = len;
Simon Glass6f7d2592014-10-13 23:42:07 -0600200
Vignesh R14510412019-02-05 11:29:23 +0530201 return mtd->_erase(mtd, &instr);
Simon Glass6f7d2592014-10-13 23:42:07 -0600202}
203
Simon Glass61d04642021-03-15 18:11:17 +1300204static int spi_flash_std_get_sw_write_prot(struct udevice *dev)
205{
206 struct spi_flash *flash = dev_get_uclass_priv(dev);
207
208 return spi_flash_cmd_get_sw_write_prot(flash);
209}
210
Simon Glassa104b642019-12-06 21:42:50 -0700211int spi_flash_std_probe(struct udevice *dev)
Simon Glass6f7d2592014-10-13 23:42:07 -0600212{
Simon Glassde44acf2015-09-28 23:32:01 -0600213 struct spi_slave *slave = dev_get_parent_priv(dev);
Simon Glass6f7d2592014-10-13 23:42:07 -0600214 struct spi_flash *flash;
215
Simon Glassde0977b2015-03-05 12:25:20 -0700216 flash = dev_get_uclass_priv(dev);
Simon Glass6f7d2592014-10-13 23:42:07 -0600217 flash->dev = dev;
Jagan Teki4abfb982015-12-06 21:33:32 +0530218 flash->spi = slave;
Jagan Teki4abfb982015-12-06 21:33:32 +0530219 return spi_flash_probe_slave(flash);
Simon Glass6f7d2592014-10-13 23:42:07 -0600220}
221
Boris Brezillon4be0c812018-12-02 10:54:31 +0100222static int spi_flash_std_remove(struct udevice *dev)
223{
Marek Behúnc1fcc142021-05-26 14:08:20 +0200224 struct spi_flash *flash = dev_get_uclass_priv(dev);
Pratyush Yadav46103502021-06-26 00:47:24 +0530225 int ret;
226
Chin-Ting Kuo77636df2022-08-19 17:01:09 +0800227 if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
228 spi_mem_dirmap_destroy(flash->dirmap.wdesc);
229 spi_mem_dirmap_destroy(flash->dirmap.rdesc);
230 }
231
Pratyush Yadav46103502021-06-26 00:47:24 +0530232 ret = spi_nor_remove(flash);
233 if (ret)
234 return ret;
Marek Behúnc1fcc142021-05-26 14:08:20 +0200235
Jagan Teki380cdbe2020-05-13 00:11:27 +0530236 if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
Marek Behúnc1fcc142021-05-26 14:08:20 +0200237 spi_flash_mtd_unregister(flash);
Jagan Teki380cdbe2020-05-13 00:11:27 +0530238
Boris Brezillon4be0c812018-12-02 10:54:31 +0100239 return 0;
240}
241
Simon Glass6f7d2592014-10-13 23:42:07 -0600242static const struct dm_spi_flash_ops spi_flash_std_ops = {
243 .read = spi_flash_std_read,
244 .write = spi_flash_std_write,
245 .erase = spi_flash_std_erase,
Simon Glass61d04642021-03-15 18:11:17 +1300246 .get_sw_write_prot = spi_flash_std_get_sw_write_prot,
Simon Glass6f7d2592014-10-13 23:42:07 -0600247};
248
249static const struct udevice_id spi_flash_std_ids[] = {
Vignesh R54c8a672019-02-05 11:29:22 +0530250 { .compatible = "jedec,spi-nor" },
Simon Glass6f7d2592014-10-13 23:42:07 -0600251 { }
252};
253
Walter Lozano2901ac62020-06-25 01:10:04 -0300254U_BOOT_DRIVER(jedec_spi_nor) = {
255 .name = "jedec_spi_nor",
Simon Glass6f7d2592014-10-13 23:42:07 -0600256 .id = UCLASS_SPI_FLASH,
257 .of_match = spi_flash_std_ids,
258 .probe = spi_flash_std_probe,
Boris Brezillon4be0c812018-12-02 10:54:31 +0100259 .remove = spi_flash_std_remove,
Simon Glassb33cd252020-12-19 10:40:01 -0700260 .priv_auto = sizeof(struct spi_nor),
Simon Glass6f7d2592014-10-13 23:42:07 -0600261 .ops = &spi_flash_std_ops,
Pratyush Yadav46103502021-06-26 00:47:24 +0530262 .flags = DM_FLAG_OS_PREPARE,
Simon Glass6f7d2592014-10-13 23:42:07 -0600263};
264
Simon Glassdf65db82020-12-28 20:34:57 -0700265DM_DRIVER_ALIAS(jedec_spi_nor, spansion_m25p16)
Walter Lozano48e5b042020-06-25 01:10:06 -0300266
Simon Glass6f7d2592014-10-13 23:42:07 -0600267#endif /* CONFIG_DM_SPI_FLASH */