blob: b807c1d4e07d0695cb3cbbc1a5003f9033e3bedb [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2014 Brian Swetland
3 * Copyright (c) 2014 Travis Geiselbrecht
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files
7 * (the "Software"), to deal in the Software without restriction,
8 * including without limitation the rights to use, copy, modify, merge,
9 * publish, distribute, sublicense, and/or sell copies of the Software,
10 * and to permit persons to whom the Software is furnished to do so,
11 * subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24#include <debug.h>
25#include <assert.h>
26#include <trace.h>
27#include <compiler.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <err.h>
31#include <string.h>
32#include <rand.h>
33#include <reg.h>
34#include <pow2.h>
35
36#include <lib/bio.h>
37#include <lib/console.h>
38#include <dev/qspi.h>
39#include <kernel/thread.h>
40
41#include <platform/zynq.h>
42
43#define LOCAL_TRACE 0
44
45// parameters specifically for the 16MB spansion S25FL128S flash
46#define PARAMETER_AREA_SIZE (128*1024)
47#define PAGE_PROGRAM_SIZE (256) // can be something else based on the part
48#define PAGE_ERASE_SLEEP_TIME (150) // amount of time before waiting to check if erase completed
49#define SECTOR_ERASE_SIZE (4096)
50#define LARGE_SECTOR_ERASE_SIZE (64*1024)
51
52#define STS_PROGRAM_ERR (1<<6)
53#define STS_ERASE_ERR (1<<5)
54#define STS_BUSY (1<<0)
55
56#define MAX_GEOMETRY_COUNT (2)
57
58struct spi_flash {
59 bool detected;
60
61 struct qspi_ctxt qspi;
62 bdev_t bdev;
63 bio_erase_geometry_info_t geometry[MAX_GEOMETRY_COUNT];
64
65 off_t size;
66};
67
68static struct spi_flash flash;
69
70static ssize_t spiflash_bdev_read(struct bdev *, void *buf, off_t offset, size_t len);
71static ssize_t spiflash_bdev_read_block(struct bdev *, void *buf, bnum_t block, uint count);
72static ssize_t spiflash_bdev_write_block(struct bdev *, const void *buf, bnum_t block, uint count);
73static ssize_t spiflash_bdev_erase(struct bdev *, off_t offset, size_t len);
74static int spiflash_ioctl(struct bdev *, int request, void *argp);
75
76// adjust 24 bit address to be correct-byte-order for 32bit qspi commands
77static uint32_t qspi_fix_addr(uint32_t addr)
78{
79 DEBUG_ASSERT((addr & ~(0x00ffffff)) == 0); // only dealing with 24bit addresses
80
81 return ((addr & 0xff) << 24) | ((addr&0xff00) << 8) | ((addr>>8) & 0xff00);
82}
83
84static void qspi_rd32(struct qspi_ctxt *qspi, uint32_t addr, uint32_t *data, uint32_t count)
85{
86 qspi_rd(qspi, qspi_fix_addr(addr) | 0x6B, 4, data, count);
87}
88
89static inline void qspi_wren(struct qspi_ctxt *qspi)
90{
91 qspi_wr1(qspi, 0x06);
92}
93
94static inline void qspi_clsr(struct qspi_ctxt *qspi)
95{
96 qspi_wr1(qspi, 0x30);
97}
98
99static inline uint32_t qspi_rd_cr1(struct qspi_ctxt *qspi)
100{
101 return qspi_rd1(qspi, 0x35) >> 24;
102}
103
104static inline uint32_t qspi_rd_status(struct qspi_ctxt *qspi)
105{
106 return qspi_rd1(qspi, 0x05) >> 24;
107}
108
109static inline void qspi_wr_status_cr1(struct qspi_ctxt *qspi, uint8_t status, uint8_t cr1)
110{
111 uint32_t cmd = (cr1 << 16) | (status << 8) | 0x01;
112
113 qspi_wren(qspi);
114 qspi_wr3(qspi, cmd);
115}
116
117static ssize_t qspi_erase_sector(struct qspi_ctxt *qspi, uint32_t addr)
118{
119 uint32_t cmd;
120 uint32_t status;
121 ssize_t toerase;
122
123 LTRACEF("addr 0x%x\n", addr);
124
125 DEBUG_ASSERT(qspi);
126
127 if (addr < PARAMETER_AREA_SIZE) {
128 // erase a small parameter sector (4K)
129 DEBUG_ASSERT(IS_ALIGNED(addr, SECTOR_ERASE_SIZE));
130 if (!IS_ALIGNED(addr, SECTOR_ERASE_SIZE))
131 return ERR_INVALID_ARGS;
132
133 cmd = 0x20;
134 toerase = SECTOR_ERASE_SIZE;
135 } else {
136 // erase a large sector (64k or 256k)
137 DEBUG_ASSERT(IS_ALIGNED(addr, LARGE_SECTOR_ERASE_SIZE));
138 if (!IS_ALIGNED(addr, LARGE_SECTOR_ERASE_SIZE))
139 return ERR_INVALID_ARGS;
140
141 cmd = 0xd8;
142 toerase = LARGE_SECTOR_ERASE_SIZE;
143 }
144
145 qspi_wren(qspi);
146 qspi_wr(qspi, qspi_fix_addr(addr) | cmd, 3, 0, 0);
147
148 thread_sleep(PAGE_ERASE_SLEEP_TIME);
149 while ((status = qspi_rd_status(qspi)) & STS_BUSY)
150 ;
151
152 LTRACEF("status 0x%x\n", status);
153 if (status & (STS_PROGRAM_ERR | STS_ERASE_ERR)) {
154 TRACEF("failed @ 0x%x\n", addr);
155 qspi_clsr(qspi);
156 return ERR_IO;
157 }
158
159 return toerase;
160}
161
162static ssize_t qspi_write_page(struct qspi_ctxt *qspi, uint32_t addr, const uint8_t *data)
163{
164 uint32_t oldkhz, status;
165
166 LTRACEF("addr 0x%x, data %p\n", addr, data);
167
168 DEBUG_ASSERT(qspi);
169 DEBUG_ASSERT(data);
170 DEBUG_ASSERT(IS_ALIGNED(addr, PAGE_PROGRAM_SIZE));
171
172 if (!IS_ALIGNED(addr, PAGE_PROGRAM_SIZE))
173 return ERR_INVALID_ARGS;
174
175 oldkhz = qspi->khz;
176 if (qspi_set_speed(qspi, 80000))
177 return ERR_IO;
178
179 qspi_wren(qspi);
180 qspi_wr(qspi, qspi_fix_addr(addr) | 0x32, 3, (uint32_t *)data, PAGE_PROGRAM_SIZE / 4);
181 qspi_set_speed(qspi, oldkhz);
182
183 while ((status = qspi_rd_status(qspi)) & STS_BUSY) ;
184
185 if (status & (STS_PROGRAM_ERR | STS_ERASE_ERR)) {
186 printf("qspi_write_page failed @ %x\n", addr);
187 qspi_clsr(qspi);
188 return ERR_IO;
189 }
190 return PAGE_PROGRAM_SIZE;
191}
192
193static ssize_t spiflash_read_cfi(void *buf, size_t len)
194{
195 DEBUG_ASSERT(len > 0 && (len % 4) == 0);
196
197 qspi_rd(&flash.qspi, 0x9f, 0, buf, len / 4);
198
199 if (len < 4)
200 return len;
201
202 /* look at byte 3 of the cfi, which says the total length of the cfi structure */
203 size_t cfi_len = ((uint8_t *)buf)[3];
204 if (cfi_len == 0)
205 cfi_len = 512;
206 else
207 cfi_len += 3;
208
209 return MIN(len, cfi_len);
210}
211
212static ssize_t spiflash_read_otp(void *buf, uint32_t addr, size_t len)
213{
214 DEBUG_ASSERT(len > 0 && (len % 4) == 0);
215
216 if (len > 1024)
217 len = 1024;
218
219 qspi_rd(&flash.qspi, 0x4b, 4, buf, len / 4);
220
221 if (len < 4)
222 return len;
223
224 return len;
225}
226
227status_t spiflash_detect(void)
228{
229 if (flash.detected)
230 return NO_ERROR;
231
232 qspi_init(&flash.qspi, 100000);
233
234 /* read and parse the cfi */
235 uint8_t *buf = calloc(1, 512);
236 ssize_t len = spiflash_read_cfi(buf, 512);
237 if (len < 4)
238 goto nodetect;
239
240 LTRACEF("looking at vendor/device id combination: %02x:%02x:%02x\n", buf[0], buf[1], buf[2]);
241
242 /* at the moment, we only support particular spansion flashes */
243 if (buf[0] != 0x01) goto nodetect;
244
245 if (buf[1] == 0x20 && buf[2] == 0x18) {
246 /* 128Mb version */
247 flash.size = 16*1024*1024;
248 } else if (buf[1] == 0x02 && buf[2] == 0x19) {
249 /* 256Mb version */
250 flash.size = 32*1024*1024;
251 } else {
252 TRACEF("unknown vendor/device id combination: %02x:%02x:%02x\n",
253 buf[0], buf[1], buf[2]);
254 goto nodetect;
255 }
256
257 /* Fill out our geometry info based on the CFI */
258 size_t region_count = buf[0x2C];
259 if (region_count > countof(flash.geometry)) {
260 TRACEF("erase region count (%zu) exceeds max allowed (%zu)\n",
261 region_count, countof(flash.geometry));
262 goto nodetect;
263 }
264
265 size_t offset = 0;
266 for (size_t i = 0; i < region_count; i++) {
267 const uint8_t* info = buf + 0x2D + (i << 2);
268 size_t pages = ((((size_t)info[1]) << 8) | info[0]) + 1;
269 size_t erase_size = ((((size_t)info[3]) << 8) | info[2]) << 8;
270
271 if (!ispow2(erase_size)) {
272 TRACEF("Region %zu page size (%zu) is not a power of 2\n",
273 i, erase_size);
274 goto nodetect;
275 }
276
277 flash.geometry[i].erase_size = erase_size;
278 flash.geometry[i].erase_shift = log2_uint(erase_size);
279 flash.geometry[i].start = offset;
280 flash.geometry[i].size = pages << flash.geometry[i].erase_shift;
281
282 size_t erase_mask = ((size_t)0x1 << flash.geometry[i].erase_shift) - 1;
283 if (offset & erase_mask) {
284 TRACEF("Region %zu not aligned to erase boundary (start %zu, erase size %zu)\n",
285 i, offset, erase_size);
286 goto nodetect;
287 }
288
289 offset += flash.geometry[i].size;
290 }
291
292 free(buf);
293
294 /* read the 16 byte random number out of the OTP area and add to the rand entropy pool */
295 uint32_t r[4];
296 memset(r, 0, sizeof(r));
297 spiflash_read_otp(r, 0, 16);
298
299 LTRACEF("OTP random %08x%08x%08x%08x\n", r[0], r[1], r[2], r[3]);
300 rand_add_entropy(r, sizeof(r));
301
302 flash.detected = true;
303
304 /* see if we're in serial mode */
305 uint32_t cr1 = qspi_rd_cr1(&flash.qspi);
306 if ((cr1 & (1<<1)) == 0) {
307 printf("spiflash: device not in quad mode, cannot use for read/write\n");
308 goto nouse;
309 }
310
311 /* construct the block device */
312 bio_initialize_bdev(&flash.bdev, "spi0",
313 PAGE_PROGRAM_SIZE, flash.size / PAGE_PROGRAM_SIZE,
314 region_count, flash.geometry, BIO_FLAGS_NONE);
315
316 /* override our block device hooks */
317 flash.bdev.read = &spiflash_bdev_read;
318 flash.bdev.read_block = &spiflash_bdev_read_block;
319 // flash.bdev.write has a default hook that will be okay
320 flash.bdev.write_block = &spiflash_bdev_write_block;
321 flash.bdev.erase = &spiflash_bdev_erase;
322 flash.bdev.ioctl = &spiflash_ioctl;
323
324 /* we erase to 0xff */
325 flash.bdev.erase_byte = 0xff;
326
327 bio_register_device(&flash.bdev);
328
329 LTRACEF("found flash of size 0x%llx\n", flash.size);
330
331nouse:
332 return NO_ERROR;
333
334nodetect:
335 LTRACEF("flash not found\n");
336
337 free(buf);
338 flash.detected = false;
339 return ERR_NOT_FOUND;
340}
341
342// bio layer hooks
343static ssize_t spiflash_bdev_read(struct bdev *bdev, void *buf, off_t offset, size_t len)
344{
345 LTRACEF("dev %p, buf %p, offset 0x%llx, len 0x%zx\n", bdev, buf, offset, len);
346
347 DEBUG_ASSERT(flash.detected);
348
349 len = bio_trim_range(bdev, offset, len);
350 if (len == 0)
351 return 0;
352
353 // XXX handle not mulitple of 4
354 qspi_rd32(&flash.qspi, offset, buf, len / 4);
355
356 return len;
357}
358
359static ssize_t spiflash_bdev_read_block(struct bdev *bdev, void *buf, bnum_t block, uint count)
360{
361 LTRACEF("dev %p, buf %p, block 0x%x, count %u\n", bdev, buf, block, count);
362
363 count = bio_trim_block_range(bdev, block, count);
364 if (count == 0)
365 return 0;
366
367 return spiflash_bdev_read(bdev, buf, block << bdev->block_shift, count << bdev->block_shift);
368}
369
370static ssize_t spiflash_bdev_write_block(struct bdev *bdev, const void *_buf, bnum_t block, uint count)
371{
372 LTRACEF("dev %p, buf %p, block 0x%x, count %u\n", bdev, _buf, block, count);
373
374 DEBUG_ASSERT(bdev->block_size == PAGE_PROGRAM_SIZE);
375
376 count = bio_trim_block_range(bdev, block, count);
377 if (count == 0)
378 return 0;
379
380 const uint8_t *buf = _buf;
381
382 ssize_t written = 0;
383 while (count > 0) {
384 ssize_t err = qspi_write_page(&flash.qspi, block * PAGE_PROGRAM_SIZE, buf);
385 if (err < 0)
386 return err;
387
388 buf += PAGE_PROGRAM_SIZE;
389 written += err;
390 block++;
391 count--;
392 }
393
394 return written;
395}
396
397static ssize_t spiflash_bdev_erase(struct bdev *bdev, off_t offset, size_t len)
398{
399 LTRACEF("dev %p, offset 0x%llx, len 0x%zx\n", bdev, offset, len);
400
401 len = bio_trim_range(bdev, offset, len);
402 if (len == 0)
403 return 0;
404
405 ssize_t erased = 0;
406 while (erased < (ssize_t)len) {
407 ssize_t err = qspi_erase_sector(&flash.qspi, offset);
408 if (err < 0)
409 return err;
410
411 erased += err;
412 offset += err;
413 }
414
415 return erased;
416}
417
418static int spiflash_ioctl(struct bdev *bdev, int request, void *argp)
419{
420 LTRACEF("dev %p, request %d, argp %p\n", bdev, request, argp);
421
422 int ret = NO_ERROR;
423 switch (request) {
424 case BIO_IOCTL_GET_MEM_MAP:
425 /* put the device into linear mode */
426 ret = qspi_enable_linear(&flash.qspi);
427 // Fallthrough.
428 case BIO_IOCTL_GET_MAP_ADDR:
429 if (argp)
430 *(void **)argp = (void *)QSPI_LINEAR_BASE;
431 break;
432 case BIO_IOCTL_PUT_MEM_MAP:
433 /* put the device back into regular mode */
434 ret = qspi_disable_linear(&flash.qspi);
435 break;
436 default:
437 ret = ERR_NOT_SUPPORTED;
438 }
439
440 return ret;
441}
442
443// debug tests
444int cmd_spiflash(int argc, const cmd_args *argv)
445{
446 if (argc < 2) {
447notenoughargs:
448 printf("not enough arguments\n");
449usage:
450 printf("usage:\n");
451#if LK_DEBUGLEVEL > 1
452 printf("\t%s detect\n", argv[0].str);
453 printf("\t%s cfi\n", argv[0].str);
454 printf("\t%s cr1\n", argv[0].str);
455 printf("\t%s otp\n", argv[0].str);
456 printf("\t%s linear [true/false]\n", argv[0].str);
457 printf("\t%s read <offset> <length>\n", argv[0].str);
458 printf("\t%s write <offset> <length> <address>\n", argv[0].str);
459 printf("\t%s erase <offset>\n", argv[0].str);
460#endif
461 printf("\t%s setquad (dangerous)\n", argv[0].str);
462 return ERR_INVALID_ARGS;
463 }
464
465#if LK_DEBUGLEVEL > 1
466 if (!strcmp(argv[1].str, "detect")) {
467 spiflash_detect();
468 } else if (!strcmp(argv[1].str, "cr1")) {
469 if (!flash.detected) {
470 printf("flash not detected\n");
471 return -1;
472 }
473
474 uint32_t cr1 = qspi_rd_cr1(&flash.qspi);
475 printf("cr1 0x%x\n", cr1);
476 } else if (!strcmp(argv[1].str, "cfi")) {
477 if (!flash.detected) {
478 printf("flash not detected\n");
479 return -1;
480 }
481
482 uint8_t *buf = calloc(1, 512);
483 ssize_t len = spiflash_read_cfi(buf, 512);
484 printf("returned cfi len %ld\n", len);
485
486 hexdump8(buf, len);
487
488 free(buf);
489 } else if (!strcmp(argv[1].str, "otp")) {
490 if (!flash.detected) {
491 printf("flash not detected\n");
492 return -1;
493 }
494
495 uint8_t *buf = calloc(1, 1024);
496 ssize_t len = spiflash_read_otp(buf, 0, 1024);
497 printf("spiflash_read_otp returns %ld\n", len);
498
499 hexdump8(buf, len);
500
501 free(buf);
502 } else if (!strcmp(argv[1].str, "linear")) {
503 if (argc < 3) goto notenoughargs;
504 if (!flash.detected) {
505 printf("flash not detected\n");
506 return -1;
507 }
508
509 if (argv[2].b)
510 qspi_enable_linear(&flash.qspi);
511 else
512 qspi_disable_linear(&flash.qspi);
513 } else if (!strcmp(argv[1].str, "read")) {
514 if (argc < 4) goto notenoughargs;
515 if (!flash.detected) {
516 printf("flash not detected\n");
517 return -1;
518 }
519
520 uint8_t *buf = calloc(1, argv[3].u);
521
522 qspi_rd32(&flash.qspi, argv[2].u, (uint32_t *)buf, argv[3].u / 4);
523
524 hexdump8(buf, argv[3].u);
525 free(buf);
526 } else if (!strcmp(argv[1].str, "write")) {
527 if (argc < 5) goto notenoughargs;
528 if (!flash.detected) {
529 printf("flash not detected\n");
530 return -1;
531 }
532
533 status_t err = qspi_write_page(&flash.qspi, argv[2].u, argv[4].p);
534 printf("write_page returns %d\n", err);
535 } else if (!strcmp(argv[1].str, "erase")) {
536 if (argc < 3) goto notenoughargs;
537 if (!flash.detected) {
538 printf("flash not detected\n");
539 return -1;
540 }
541
542 status_t err = qspi_erase_sector(&flash.qspi, argv[2].u);
543 printf("erase returns %d\n", err);
544 } else
545#endif
546 if (!strcmp(argv[1].str, "setquad")) {
547 if (!flash.detected) {
548 printf("flash not detected\n");
549 return -1;
550 }
551
552 uint32_t cr1 = qspi_rd_cr1(&flash.qspi);
553 printf("cr1 before 0x%x\n", cr1);
554
555 if (cr1 & (1<<1)) {
556 printf("flash already in quad mode\n");
557 return 0;
558 }
559
560 qspi_wr_status_cr1(&flash.qspi, 0, cr1 | (1<<1));
561
562 thread_sleep(500);
563 cr1 = qspi_rd_cr1(&flash.qspi);
564 printf("cr1 after 0x%x\n", cr1);
565 } else {
566 printf("unknown command\n");
567 goto usage;
568 }
569
570 return 0;
571}
572
573#if defined(WITH_LIB_CONSOLE)
574#include <lib/console.h>
575
576STATIC_COMMAND_START
577STATIC_COMMAND("spiflash", "spi flash manipulation utilities", cmd_spiflash)
578STATIC_COMMAND_END(qspi);
579
580#endif
581
582// vim: set ts=4 sw=4 noexpandtab: