1*49dec7f7SSheetal Tigadoli /*
2*49dec7f7SSheetal Tigadoli * Copyright (c) 2019-2020, Broadcom
3*49dec7f7SSheetal Tigadoli *
4*49dec7f7SSheetal Tigadoli * SPDX-License-Identifier: BSD-3-Clause
5*49dec7f7SSheetal Tigadoli */
6*49dec7f7SSheetal Tigadoli
7*49dec7f7SSheetal Tigadoli #include <stdbool.h>
8*49dec7f7SSheetal Tigadoli #include <stddef.h>
9*49dec7f7SSheetal Tigadoli #include <stdint.h>
10*49dec7f7SSheetal Tigadoli
11*49dec7f7SSheetal Tigadoli #include <common/debug.h>
12*49dec7f7SSheetal Tigadoli #include <drivers/delay_timer.h>
13*49dec7f7SSheetal Tigadoli #include <errno.h>
14*49dec7f7SSheetal Tigadoli
15*49dec7f7SSheetal Tigadoli #include <sf.h>
16*49dec7f7SSheetal Tigadoli #include <spi.h>
17*49dec7f7SSheetal Tigadoli
18*49dec7f7SSheetal Tigadoli #define SPI_FLASH_CMD_LEN 4
19*49dec7f7SSheetal Tigadoli #define QSPI_WAIT_TIMEOUT_US 200000U /* usec */
20*49dec7f7SSheetal Tigadoli
21*49dec7f7SSheetal Tigadoli #define FINFO(jedec_id, ext_id, _sector_size, _n_sectors, _page_size, _flags) \
22*49dec7f7SSheetal Tigadoli .id = { \
23*49dec7f7SSheetal Tigadoli ((jedec_id) >> 16) & 0xff, \
24*49dec7f7SSheetal Tigadoli ((jedec_id) >> 8) & 0xff, \
25*49dec7f7SSheetal Tigadoli (jedec_id) & 0xff, \
26*49dec7f7SSheetal Tigadoli ((ext_id) >> 8) & 0xff, \
27*49dec7f7SSheetal Tigadoli (ext_id) & 0xff, \
28*49dec7f7SSheetal Tigadoli }, \
29*49dec7f7SSheetal Tigadoli .id_len = (!(jedec_id) ? 0 : (3 + ((ext_id) ? 2 : 0))), \
30*49dec7f7SSheetal Tigadoli .sector_size = (_sector_size), \
31*49dec7f7SSheetal Tigadoli .n_sectors = (_n_sectors), \
32*49dec7f7SSheetal Tigadoli .page_size = _page_size, \
33*49dec7f7SSheetal Tigadoli .flags = (_flags),
34*49dec7f7SSheetal Tigadoli
35*49dec7f7SSheetal Tigadoli /* SPI/QSPI flash device params structure */
36*49dec7f7SSheetal Tigadoli const struct spi_flash_info spi_flash_ids[] = {
37*49dec7f7SSheetal Tigadoli {"W25Q64CV", FINFO(0xef4017, 0x0, 64 * 1024, 128, 256, WR_QPP | SECT_4K)},
38*49dec7f7SSheetal Tigadoli {"W25Q64DW", FINFO(0xef6017, 0x0, 64 * 1024, 128, 256, WR_QPP | SECT_4K)},
39*49dec7f7SSheetal Tigadoli {"W25Q32", FINFO(0xef4016, 0x0, 64 * 1024, 64, 256, SECT_4K)},
40*49dec7f7SSheetal Tigadoli {"MX25l3205D", FINFO(0xc22016, 0x0, 64 * 1024, 64, 256, SECT_4K)},
41*49dec7f7SSheetal Tigadoli };
42*49dec7f7SSheetal Tigadoli
spi_flash_addr(uint32_t addr,uint8_t * cmd)43*49dec7f7SSheetal Tigadoli static void spi_flash_addr(uint32_t addr, uint8_t *cmd)
44*49dec7f7SSheetal Tigadoli {
45*49dec7f7SSheetal Tigadoli /*
46*49dec7f7SSheetal Tigadoli * cmd[0] holds a SPI Flash command, stored earlier
47*49dec7f7SSheetal Tigadoli * cmd[1/2/3] holds 24bit flash address
48*49dec7f7SSheetal Tigadoli */
49*49dec7f7SSheetal Tigadoli cmd[1] = addr >> 16;
50*49dec7f7SSheetal Tigadoli cmd[2] = addr >> 8;
51*49dec7f7SSheetal Tigadoli cmd[3] = addr >> 0;
52*49dec7f7SSheetal Tigadoli }
53*49dec7f7SSheetal Tigadoli
spi_flash_read_id(void)54*49dec7f7SSheetal Tigadoli static const struct spi_flash_info *spi_flash_read_id(void)
55*49dec7f7SSheetal Tigadoli {
56*49dec7f7SSheetal Tigadoli const struct spi_flash_info *info;
57*49dec7f7SSheetal Tigadoli uint8_t id[SPI_FLASH_MAX_ID_LEN];
58*49dec7f7SSheetal Tigadoli int ret;
59*49dec7f7SSheetal Tigadoli
60*49dec7f7SSheetal Tigadoli ret = spi_flash_cmd(CMD_READ_ID, id, SPI_FLASH_MAX_ID_LEN);
61*49dec7f7SSheetal Tigadoli if (ret < 0) {
62*49dec7f7SSheetal Tigadoli ERROR("SF: Error %d reading JEDEC ID\n", ret);
63*49dec7f7SSheetal Tigadoli return NULL;
64*49dec7f7SSheetal Tigadoli }
65*49dec7f7SSheetal Tigadoli
66*49dec7f7SSheetal Tigadoli for (info = spi_flash_ids; info->name != NULL; info++) {
67*49dec7f7SSheetal Tigadoli if (info->id_len) {
68*49dec7f7SSheetal Tigadoli if (!memcmp(info->id, id, info->id_len))
69*49dec7f7SSheetal Tigadoli return info;
70*49dec7f7SSheetal Tigadoli }
71*49dec7f7SSheetal Tigadoli }
72*49dec7f7SSheetal Tigadoli
73*49dec7f7SSheetal Tigadoli printf("SF: unrecognized JEDEC id bytes: %02x, %02x, %02x\n",
74*49dec7f7SSheetal Tigadoli id[0], id[1], id[2]);
75*49dec7f7SSheetal Tigadoli return NULL;
76*49dec7f7SSheetal Tigadoli }
77*49dec7f7SSheetal Tigadoli
78*49dec7f7SSheetal Tigadoli /* Enable writing on the SPI flash */
spi_flash_cmd_write_enable(struct spi_flash * flash)79*49dec7f7SSheetal Tigadoli static inline int spi_flash_cmd_write_enable(struct spi_flash *flash)
80*49dec7f7SSheetal Tigadoli {
81*49dec7f7SSheetal Tigadoli return spi_flash_cmd(CMD_WRITE_ENABLE, NULL, 0);
82*49dec7f7SSheetal Tigadoli }
83*49dec7f7SSheetal Tigadoli
spi_flash_cmd_wait(struct spi_flash * flash)84*49dec7f7SSheetal Tigadoli static int spi_flash_cmd_wait(struct spi_flash *flash)
85*49dec7f7SSheetal Tigadoli {
86*49dec7f7SSheetal Tigadoli uint8_t cmd;
87*49dec7f7SSheetal Tigadoli uint32_t i;
88*49dec7f7SSheetal Tigadoli uint8_t status;
89*49dec7f7SSheetal Tigadoli int ret;
90*49dec7f7SSheetal Tigadoli
91*49dec7f7SSheetal Tigadoli i = 0;
92*49dec7f7SSheetal Tigadoli while (1) {
93*49dec7f7SSheetal Tigadoli cmd = CMD_RDSR;
94*49dec7f7SSheetal Tigadoli ret = spi_flash_cmd_read(&cmd, 1, &status, 1);
95*49dec7f7SSheetal Tigadoli if (ret < 0) {
96*49dec7f7SSheetal Tigadoli ERROR("SF: cmd wait failed\n");
97*49dec7f7SSheetal Tigadoli break;
98*49dec7f7SSheetal Tigadoli }
99*49dec7f7SSheetal Tigadoli if (!(status & STATUS_WIP))
100*49dec7f7SSheetal Tigadoli break;
101*49dec7f7SSheetal Tigadoli
102*49dec7f7SSheetal Tigadoli i++;
103*49dec7f7SSheetal Tigadoli if (i >= QSPI_WAIT_TIMEOUT_US) {
104*49dec7f7SSheetal Tigadoli ERROR("SF: cmd wait timeout\n");
105*49dec7f7SSheetal Tigadoli ret = -1;
106*49dec7f7SSheetal Tigadoli break;
107*49dec7f7SSheetal Tigadoli }
108*49dec7f7SSheetal Tigadoli udelay(1);
109*49dec7f7SSheetal Tigadoli }
110*49dec7f7SSheetal Tigadoli
111*49dec7f7SSheetal Tigadoli return ret;
112*49dec7f7SSheetal Tigadoli }
113*49dec7f7SSheetal Tigadoli
spi_flash_write_common(struct spi_flash * flash,const uint8_t * cmd,size_t cmd_len,const void * buf,size_t buf_len)114*49dec7f7SSheetal Tigadoli static int spi_flash_write_common(struct spi_flash *flash, const uint8_t *cmd,
115*49dec7f7SSheetal Tigadoli size_t cmd_len, const void *buf,
116*49dec7f7SSheetal Tigadoli size_t buf_len)
117*49dec7f7SSheetal Tigadoli {
118*49dec7f7SSheetal Tigadoli int ret;
119*49dec7f7SSheetal Tigadoli
120*49dec7f7SSheetal Tigadoli ret = spi_flash_cmd_write_enable(flash);
121*49dec7f7SSheetal Tigadoli if (ret < 0) {
122*49dec7f7SSheetal Tigadoli ERROR("SF: enabling write failed\n");
123*49dec7f7SSheetal Tigadoli return ret;
124*49dec7f7SSheetal Tigadoli }
125*49dec7f7SSheetal Tigadoli
126*49dec7f7SSheetal Tigadoli ret = spi_flash_cmd_write(cmd, cmd_len, buf, buf_len);
127*49dec7f7SSheetal Tigadoli if (ret < 0) {
128*49dec7f7SSheetal Tigadoli ERROR("SF: write cmd failed\n");
129*49dec7f7SSheetal Tigadoli return ret;
130*49dec7f7SSheetal Tigadoli }
131*49dec7f7SSheetal Tigadoli
132*49dec7f7SSheetal Tigadoli ret = spi_flash_cmd_wait(flash);
133*49dec7f7SSheetal Tigadoli if (ret < 0) {
134*49dec7f7SSheetal Tigadoli ERROR("SF: write timed out\n");
135*49dec7f7SSheetal Tigadoli return ret;
136*49dec7f7SSheetal Tigadoli }
137*49dec7f7SSheetal Tigadoli
138*49dec7f7SSheetal Tigadoli return ret;
139*49dec7f7SSheetal Tigadoli }
140*49dec7f7SSheetal Tigadoli
spi_flash_read_common(const uint8_t * cmd,size_t cmd_len,void * data,size_t data_len)141*49dec7f7SSheetal Tigadoli static int spi_flash_read_common(const uint8_t *cmd, size_t cmd_len,
142*49dec7f7SSheetal Tigadoli void *data, size_t data_len)
143*49dec7f7SSheetal Tigadoli {
144*49dec7f7SSheetal Tigadoli int ret;
145*49dec7f7SSheetal Tigadoli
146*49dec7f7SSheetal Tigadoli ret = spi_flash_cmd_read(cmd, cmd_len, data, data_len);
147*49dec7f7SSheetal Tigadoli if (ret < 0) {
148*49dec7f7SSheetal Tigadoli ERROR("SF: read cmd failed\n");
149*49dec7f7SSheetal Tigadoli return ret;
150*49dec7f7SSheetal Tigadoli }
151*49dec7f7SSheetal Tigadoli
152*49dec7f7SSheetal Tigadoli return ret;
153*49dec7f7SSheetal Tigadoli }
154*49dec7f7SSheetal Tigadoli
spi_flash_read(struct spi_flash * flash,uint32_t offset,uint32_t len,void * data)155*49dec7f7SSheetal Tigadoli int spi_flash_read(struct spi_flash *flash, uint32_t offset,
156*49dec7f7SSheetal Tigadoli uint32_t len, void *data)
157*49dec7f7SSheetal Tigadoli {
158*49dec7f7SSheetal Tigadoli uint32_t read_len = 0, read_addr;
159*49dec7f7SSheetal Tigadoli uint8_t cmd[SPI_FLASH_CMD_LEN];
160*49dec7f7SSheetal Tigadoli int ret;
161*49dec7f7SSheetal Tigadoli
162*49dec7f7SSheetal Tigadoli ret = spi_claim_bus();
163*49dec7f7SSheetal Tigadoli if (ret) {
164*49dec7f7SSheetal Tigadoli ERROR("SF: unable to claim SPI bus\n");
165*49dec7f7SSheetal Tigadoli return ret;
166*49dec7f7SSheetal Tigadoli }
167*49dec7f7SSheetal Tigadoli
168*49dec7f7SSheetal Tigadoli cmd[0] = CMD_READ_NORMAL;
169*49dec7f7SSheetal Tigadoli while (len) {
170*49dec7f7SSheetal Tigadoli read_addr = offset;
171*49dec7f7SSheetal Tigadoli read_len = MIN(flash->page_size, (len - read_len));
172*49dec7f7SSheetal Tigadoli spi_flash_addr(read_addr, cmd);
173*49dec7f7SSheetal Tigadoli
174*49dec7f7SSheetal Tigadoli ret = spi_flash_read_common(cmd, sizeof(cmd), data, read_len);
175*49dec7f7SSheetal Tigadoli if (ret < 0) {
176*49dec7f7SSheetal Tigadoli ERROR("SF: read failed\n");
177*49dec7f7SSheetal Tigadoli break;
178*49dec7f7SSheetal Tigadoli }
179*49dec7f7SSheetal Tigadoli
180*49dec7f7SSheetal Tigadoli offset += read_len;
181*49dec7f7SSheetal Tigadoli len -= read_len;
182*49dec7f7SSheetal Tigadoli data += read_len;
183*49dec7f7SSheetal Tigadoli }
184*49dec7f7SSheetal Tigadoli SPI_DEBUG("SF read done\n");
185*49dec7f7SSheetal Tigadoli
186*49dec7f7SSheetal Tigadoli spi_release_bus();
187*49dec7f7SSheetal Tigadoli return ret;
188*49dec7f7SSheetal Tigadoli }
189*49dec7f7SSheetal Tigadoli
spi_flash_write(struct spi_flash * flash,uint32_t offset,uint32_t len,void * buf)190*49dec7f7SSheetal Tigadoli int spi_flash_write(struct spi_flash *flash, uint32_t offset,
191*49dec7f7SSheetal Tigadoli uint32_t len, void *buf)
192*49dec7f7SSheetal Tigadoli {
193*49dec7f7SSheetal Tigadoli unsigned long byte_addr, page_size;
194*49dec7f7SSheetal Tigadoli uint8_t cmd[SPI_FLASH_CMD_LEN];
195*49dec7f7SSheetal Tigadoli uint32_t chunk_len, actual;
196*49dec7f7SSheetal Tigadoli uint32_t write_addr;
197*49dec7f7SSheetal Tigadoli int ret;
198*49dec7f7SSheetal Tigadoli
199*49dec7f7SSheetal Tigadoli ret = spi_claim_bus();
200*49dec7f7SSheetal Tigadoli if (ret) {
201*49dec7f7SSheetal Tigadoli ERROR("SF: unable to claim SPI bus\n");
202*49dec7f7SSheetal Tigadoli return ret;
203*49dec7f7SSheetal Tigadoli }
204*49dec7f7SSheetal Tigadoli
205*49dec7f7SSheetal Tigadoli page_size = flash->page_size;
206*49dec7f7SSheetal Tigadoli
207*49dec7f7SSheetal Tigadoli cmd[0] = flash->write_cmd;
208*49dec7f7SSheetal Tigadoli for (actual = 0; actual < len; actual += chunk_len) {
209*49dec7f7SSheetal Tigadoli write_addr = offset;
210*49dec7f7SSheetal Tigadoli byte_addr = offset % page_size;
211*49dec7f7SSheetal Tigadoli chunk_len = MIN(len - actual,
212*49dec7f7SSheetal Tigadoli (uint32_t)(page_size - byte_addr));
213*49dec7f7SSheetal Tigadoli spi_flash_addr(write_addr, cmd);
214*49dec7f7SSheetal Tigadoli
215*49dec7f7SSheetal Tigadoli SPI_DEBUG("SF:0x%p=>cmd:{0x%02x 0x%02x%02x%02x} chunk_len:%d\n",
216*49dec7f7SSheetal Tigadoli buf + actual, cmd[0], cmd[1],
217*49dec7f7SSheetal Tigadoli cmd[2], cmd[3], chunk_len);
218*49dec7f7SSheetal Tigadoli
219*49dec7f7SSheetal Tigadoli ret = spi_flash_write_common(flash, cmd, sizeof(cmd),
220*49dec7f7SSheetal Tigadoli buf + actual, chunk_len);
221*49dec7f7SSheetal Tigadoli if (ret < 0) {
222*49dec7f7SSheetal Tigadoli ERROR("SF: write cmd failed\n");
223*49dec7f7SSheetal Tigadoli break;
224*49dec7f7SSheetal Tigadoli }
225*49dec7f7SSheetal Tigadoli
226*49dec7f7SSheetal Tigadoli offset += chunk_len;
227*49dec7f7SSheetal Tigadoli }
228*49dec7f7SSheetal Tigadoli SPI_DEBUG("SF write done\n");
229*49dec7f7SSheetal Tigadoli
230*49dec7f7SSheetal Tigadoli spi_release_bus();
231*49dec7f7SSheetal Tigadoli return ret;
232*49dec7f7SSheetal Tigadoli }
233*49dec7f7SSheetal Tigadoli
spi_flash_erase(struct spi_flash * flash,uint32_t offset,uint32_t len)234*49dec7f7SSheetal Tigadoli int spi_flash_erase(struct spi_flash *flash, uint32_t offset, uint32_t len)
235*49dec7f7SSheetal Tigadoli {
236*49dec7f7SSheetal Tigadoli uint8_t cmd[SPI_FLASH_CMD_LEN];
237*49dec7f7SSheetal Tigadoli uint32_t erase_size, erase_addr;
238*49dec7f7SSheetal Tigadoli int ret;
239*49dec7f7SSheetal Tigadoli
240*49dec7f7SSheetal Tigadoli erase_size = flash->erase_size;
241*49dec7f7SSheetal Tigadoli
242*49dec7f7SSheetal Tigadoli if (offset % erase_size || len % erase_size) {
243*49dec7f7SSheetal Tigadoli ERROR("SF: Erase offset/length not multiple of erase size\n");
244*49dec7f7SSheetal Tigadoli return -1;
245*49dec7f7SSheetal Tigadoli }
246*49dec7f7SSheetal Tigadoli
247*49dec7f7SSheetal Tigadoli ret = spi_claim_bus();
248*49dec7f7SSheetal Tigadoli if (ret) {
249*49dec7f7SSheetal Tigadoli ERROR("SF: unable to claim SPI bus\n");
250*49dec7f7SSheetal Tigadoli return ret;
251*49dec7f7SSheetal Tigadoli }
252*49dec7f7SSheetal Tigadoli
253*49dec7f7SSheetal Tigadoli cmd[0] = flash->erase_cmd;
254*49dec7f7SSheetal Tigadoli while (len) {
255*49dec7f7SSheetal Tigadoli erase_addr = offset;
256*49dec7f7SSheetal Tigadoli spi_flash_addr(erase_addr, cmd);
257*49dec7f7SSheetal Tigadoli
258*49dec7f7SSheetal Tigadoli SPI_DEBUG("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1],
259*49dec7f7SSheetal Tigadoli cmd[2], cmd[3], erase_addr);
260*49dec7f7SSheetal Tigadoli
261*49dec7f7SSheetal Tigadoli ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0);
262*49dec7f7SSheetal Tigadoli if (ret < 0) {
263*49dec7f7SSheetal Tigadoli ERROR("SF: erase failed\n");
264*49dec7f7SSheetal Tigadoli break;
265*49dec7f7SSheetal Tigadoli }
266*49dec7f7SSheetal Tigadoli
267*49dec7f7SSheetal Tigadoli offset += erase_size;
268*49dec7f7SSheetal Tigadoli len -= erase_size;
269*49dec7f7SSheetal Tigadoli }
270*49dec7f7SSheetal Tigadoli SPI_DEBUG("sf erase done\n");
271*49dec7f7SSheetal Tigadoli
272*49dec7f7SSheetal Tigadoli spi_release_bus();
273*49dec7f7SSheetal Tigadoli return ret;
274*49dec7f7SSheetal Tigadoli }
275*49dec7f7SSheetal Tigadoli
spi_flash_probe(struct spi_flash * flash)276*49dec7f7SSheetal Tigadoli int spi_flash_probe(struct spi_flash *flash)
277*49dec7f7SSheetal Tigadoli {
278*49dec7f7SSheetal Tigadoli const struct spi_flash_info *info = NULL;
279*49dec7f7SSheetal Tigadoli int ret;
280*49dec7f7SSheetal Tigadoli
281*49dec7f7SSheetal Tigadoli ret = spi_claim_bus();
282*49dec7f7SSheetal Tigadoli if (ret) {
283*49dec7f7SSheetal Tigadoli ERROR("SF: Unable to claim SPI bus\n");
284*49dec7f7SSheetal Tigadoli ERROR("SF: probe failed\n");
285*49dec7f7SSheetal Tigadoli return ret;
286*49dec7f7SSheetal Tigadoli }
287*49dec7f7SSheetal Tigadoli
288*49dec7f7SSheetal Tigadoli info = spi_flash_read_id();
289*49dec7f7SSheetal Tigadoli if (!info)
290*49dec7f7SSheetal Tigadoli goto probe_fail;
291*49dec7f7SSheetal Tigadoli
292*49dec7f7SSheetal Tigadoli INFO("Flash Name: %s sectors %x, sec size %x\n",
293*49dec7f7SSheetal Tigadoli info->name, info->n_sectors,
294*49dec7f7SSheetal Tigadoli info->sector_size);
295*49dec7f7SSheetal Tigadoli flash->size = info->n_sectors * info->sector_size;
296*49dec7f7SSheetal Tigadoli flash->sector_size = info->sector_size;
297*49dec7f7SSheetal Tigadoli flash->page_size = info->page_size;
298*49dec7f7SSheetal Tigadoli flash->flags = info->flags;
299*49dec7f7SSheetal Tigadoli
300*49dec7f7SSheetal Tigadoli flash->read_cmd = CMD_READ_NORMAL;
301*49dec7f7SSheetal Tigadoli flash->write_cmd = CMD_PAGE_PROGRAM;
302*49dec7f7SSheetal Tigadoli flash->erase_cmd = CMD_ERASE_64K;
303*49dec7f7SSheetal Tigadoli flash->erase_size = ERASE_SIZE_64K;
304*49dec7f7SSheetal Tigadoli
305*49dec7f7SSheetal Tigadoli probe_fail:
306*49dec7f7SSheetal Tigadoli spi_release_bus();
307*49dec7f7SSheetal Tigadoli return ret;
308*49dec7f7SSheetal Tigadoli }
309