diff options
Diffstat (limited to 'platform/zynq/spiflash.c')
-rw-r--r-- | platform/zynq/spiflash.c | 580 |
1 files changed, 0 insertions, 580 deletions
diff --git a/platform/zynq/spiflash.c b/platform/zynq/spiflash.c deleted file mode 100644 index ffcec663..00000000 --- a/platform/zynq/spiflash.c +++ /dev/null @@ -1,580 +0,0 @@ -/* - * Copyright (c) 2014 Brian Swetland - * Copyright (c) 2014 Travis Geiselbrecht - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include <debug.h> -#include <assert.h> -#include <trace.h> -#include <compiler.h> -#include <stdio.h> -#include <stdlib.h> -#include <err.h> -#include <string.h> -#include <rand.h> -#include <reg.h> -#include <pow2.h> - -#include <lib/bio.h> -#include <lib/console.h> -#include <dev/qspi.h> -#include <kernel/thread.h> - -#include <platform/zynq.h> - -#define LOCAL_TRACE 0 - -// parameters specifically for the 16MB spansion S25FL128S flash -#define PARAMETER_AREA_SIZE (128*1024) -#define PAGE_PROGRAM_SIZE (256) // can be something else based on the part -#define PAGE_ERASE_SLEEP_TIME (150) // amount of time before waiting to check if erase completed -#define SECTOR_ERASE_SIZE (4096) -#define LARGE_SECTOR_ERASE_SIZE (64*1024) - -#define STS_PROGRAM_ERR (1<<6) -#define STS_ERASE_ERR (1<<5) -#define STS_BUSY (1<<0) - -#define MAX_GEOMETRY_COUNT (2) - -struct spi_flash { - bool detected; - - struct qspi_ctxt qspi; - bdev_t bdev; - bio_erase_geometry_info_t geometry[MAX_GEOMETRY_COUNT]; - - off_t size; -}; - -static struct spi_flash flash; - -static ssize_t spiflash_bdev_read(struct bdev *, void *buf, off_t offset, size_t len); -static ssize_t spiflash_bdev_read_block(struct bdev *, void *buf, bnum_t block, uint count); -static ssize_t spiflash_bdev_write_block(struct bdev *, const void *buf, bnum_t block, uint count); -static ssize_t spiflash_bdev_erase(struct bdev *, off_t offset, size_t len); -static int spiflash_ioctl(struct bdev *, int request, void *argp); - -// adjust 24 bit address to be correct-byte-order for 32bit qspi commands -static uint32_t qspi_fix_addr(uint32_t addr) -{ - DEBUG_ASSERT((addr & ~(0x00ffffff)) == 0); // only dealing with 24bit addresses - - return ((addr & 0xff) << 24) | ((addr&0xff00) << 8) | ((addr>>8) & 0xff00); -} - -static void qspi_rd32(struct qspi_ctxt *qspi, uint32_t addr, uint32_t *data, uint32_t count) -{ - qspi_rd(qspi, qspi_fix_addr(addr) | 0x6B, 4, data, count); -} - -static inline void qspi_wren(struct qspi_ctxt *qspi) -{ - qspi_wr1(qspi, 0x06); -} - -static inline void qspi_clsr(struct qspi_ctxt *qspi) -{ - qspi_wr1(qspi, 0x30); -} - -static inline uint32_t qspi_rd_cr1(struct qspi_ctxt *qspi) -{ - return qspi_rd1(qspi, 0x35) >> 24; -} - -static inline uint32_t qspi_rd_status(struct qspi_ctxt *qspi) -{ - return qspi_rd1(qspi, 0x05) >> 24; -} - -static inline void qspi_wr_status_cr1(struct qspi_ctxt *qspi, uint8_t status, uint8_t cr1) -{ - uint32_t cmd = (cr1 << 16) | (status << 8) | 0x01; - - qspi_wren(qspi); - qspi_wr3(qspi, cmd); -} - -static ssize_t qspi_erase_sector(struct qspi_ctxt *qspi, uint32_t addr) -{ - uint32_t cmd; - uint32_t status; - ssize_t toerase; - - LTRACEF("addr 0x%x\n", addr); - - DEBUG_ASSERT(qspi); - - if (addr < PARAMETER_AREA_SIZE) { - // erase a small parameter sector (4K) - DEBUG_ASSERT(IS_ALIGNED(addr, SECTOR_ERASE_SIZE)); - if (!IS_ALIGNED(addr, SECTOR_ERASE_SIZE)) - return ERR_INVALID_ARGS; - - cmd = 0x20; - toerase = SECTOR_ERASE_SIZE; - } else { - // erase a large sector (64k or 256k) - DEBUG_ASSERT(IS_ALIGNED(addr, LARGE_SECTOR_ERASE_SIZE)); - if (!IS_ALIGNED(addr, LARGE_SECTOR_ERASE_SIZE)) - return ERR_INVALID_ARGS; - - cmd = 0xd8; - toerase = LARGE_SECTOR_ERASE_SIZE; - } - - qspi_wren(qspi); - qspi_wr(qspi, qspi_fix_addr(addr) | cmd, 3, 0, 0); - - thread_sleep(PAGE_ERASE_SLEEP_TIME); - while ((status = qspi_rd_status(qspi)) & STS_BUSY) - ; - - LTRACEF("status 0x%x\n", status); - if (status & (STS_PROGRAM_ERR | STS_ERASE_ERR)) { - TRACEF("failed @ 0x%x\n", addr); - qspi_clsr(qspi); - return ERR_IO; - } - - return toerase; -} - -static ssize_t qspi_write_page(struct qspi_ctxt *qspi, uint32_t addr, const uint8_t *data) -{ - uint32_t oldkhz, status; - - LTRACEF("addr 0x%x, data %p\n", addr, data); - - DEBUG_ASSERT(qspi); - DEBUG_ASSERT(data); - DEBUG_ASSERT(IS_ALIGNED(addr, PAGE_PROGRAM_SIZE)); - - if (!IS_ALIGNED(addr, PAGE_PROGRAM_SIZE)) - return ERR_INVALID_ARGS; - - oldkhz = qspi->khz; - if (qspi_set_speed(qspi, 80000)) - return ERR_IO; - - qspi_wren(qspi); - qspi_wr(qspi, qspi_fix_addr(addr) | 0x32, 3, (uint32_t *)data, PAGE_PROGRAM_SIZE / 4); - qspi_set_speed(qspi, oldkhz); - - while ((status = qspi_rd_status(qspi)) & STS_BUSY) ; - - if (status & (STS_PROGRAM_ERR | STS_ERASE_ERR)) { - printf("qspi_write_page failed @ %x\n", addr); - qspi_clsr(qspi); - return ERR_IO; - } - return PAGE_PROGRAM_SIZE; -} - -static ssize_t spiflash_read_cfi(void *buf, size_t len) -{ - DEBUG_ASSERT(len > 0 && (len % 4) == 0); - - qspi_rd(&flash.qspi, 0x9f, 0, buf, len / 4); - - if (len < 4) - return len; - - /* look at byte 3 of the cfi, which says the total length of the cfi structure */ - size_t cfi_len = ((uint8_t *)buf)[3]; - if (cfi_len == 0) - cfi_len = 512; - else - cfi_len += 3; - - return MIN(len, cfi_len); -} - -static ssize_t spiflash_read_otp(void *buf, uint32_t addr, size_t len) -{ - DEBUG_ASSERT(len > 0 && (len % 4) == 0); - - if (len > 1024) - len = 1024; - - qspi_rd(&flash.qspi, 0x4b, 4, buf, len / 4); - - if (len < 4) - return len; - - return len; -} - -status_t spiflash_detect(void) -{ - if (flash.detected) - return NO_ERROR; - - qspi_init(&flash.qspi, 100000); - - /* read and parse the cfi */ - uint8_t *buf = calloc(1, 512); - ssize_t len = spiflash_read_cfi(buf, 512); - if (len < 4) - goto nodetect; - - LTRACEF("looking at vendor/device id combination: %02x:%02x:%02x\n", buf[0], buf[1], buf[2]); - - /* at the moment, we only support particular spansion flashes */ - if (buf[0] != 0x01) goto nodetect; - - if (buf[1] == 0x20 && buf[2] == 0x18) { - /* 128Mb version */ - flash.size = 16*1024*1024; - } else if (buf[1] == 0x02 && buf[2] == 0x19) { - /* 256Mb version */ - flash.size = 32*1024*1024; - } else { - TRACEF("unknown vendor/device id combination: %02x:%02x:%02x\n", - buf[0], buf[1], buf[2]); - goto nodetect; - } - - /* Fill out our geometry info based on the CFI */ - size_t region_count = buf[0x2C]; - if (region_count > countof(flash.geometry)) { - TRACEF("erase region count (%zu) exceeds max allowed (%zu)\n", - region_count, countof(flash.geometry)); - goto nodetect; - } - - size_t offset = 0; - for (size_t i = 0; i < region_count; i++) { - const uint8_t *info = buf + 0x2D + (i << 2); - size_t pages = ((((size_t)info[1]) << 8) | info[0]) + 1; - size_t erase_size = ((((size_t)info[3]) << 8) | info[2]) << 8; - - if (!ispow2(erase_size)) { - TRACEF("Region %zu page size (%zu) is not a power of 2\n", - i, erase_size); - goto nodetect; - } - - flash.geometry[i].erase_size = erase_size; - flash.geometry[i].erase_shift = log2_uint(erase_size); - flash.geometry[i].start = offset; - flash.geometry[i].size = pages << flash.geometry[i].erase_shift; - - size_t erase_mask = ((size_t)0x1 << flash.geometry[i].erase_shift) - 1; - if (offset & erase_mask) { - TRACEF("Region %zu not aligned to erase boundary (start %zu, erase size %zu)\n", - i, offset, erase_size); - goto nodetect; - } - - offset += flash.geometry[i].size; - } - - free(buf); - - /* read the 16 byte random number out of the OTP area and add to the rand entropy pool */ - uint32_t r[4]; - memset(r, 0, sizeof(r)); - spiflash_read_otp(r, 0, 16); - - LTRACEF("OTP random %08x%08x%08x%08x\n", r[0], r[1], r[2], r[3]); - rand_add_entropy(r, sizeof(r)); - - flash.detected = true; - - /* see if we're in serial mode */ - uint32_t cr1 = qspi_rd_cr1(&flash.qspi); - if ((cr1 & (1<<1)) == 0) { - printf("spiflash: device not in quad mode, cannot use for read/write\n"); - goto nouse; - } - - /* construct the block device */ - bio_initialize_bdev(&flash.bdev, "spi0", - PAGE_PROGRAM_SIZE, flash.size / PAGE_PROGRAM_SIZE, - region_count, flash.geometry, BIO_FLAGS_NONE); - - /* override our block device hooks */ - flash.bdev.read = &spiflash_bdev_read; - flash.bdev.read_block = &spiflash_bdev_read_block; - // flash.bdev.write has a default hook that will be okay - flash.bdev.write_block = &spiflash_bdev_write_block; - flash.bdev.erase = &spiflash_bdev_erase; - flash.bdev.ioctl = &spiflash_ioctl; - - /* we erase to 0xff */ - flash.bdev.erase_byte = 0xff; - - bio_register_device(&flash.bdev); - - LTRACEF("found flash of size 0x%llx\n", flash.size); - -nouse: - return NO_ERROR; - -nodetect: - LTRACEF("flash not found\n"); - - free(buf); - flash.detected = false; - return ERR_NOT_FOUND; -} - -// bio layer hooks -static ssize_t spiflash_bdev_read(struct bdev *bdev, void *buf, off_t offset, size_t len) -{ - LTRACEF("dev %p, buf %p, offset 0x%llx, len 0x%zx\n", bdev, buf, offset, len); - - DEBUG_ASSERT(flash.detected); - - len = bio_trim_range(bdev, offset, len); - if (len == 0) - return 0; - - // XXX handle not multiple of 4 - qspi_rd32(&flash.qspi, offset, buf, len / 4); - - return len; -} - -static ssize_t spiflash_bdev_read_block(struct bdev *bdev, void *buf, bnum_t block, uint count) -{ - LTRACEF("dev %p, buf %p, block 0x%x, count %u\n", bdev, buf, block, count); - - count = bio_trim_block_range(bdev, block, count); - if (count == 0) - return 0; - - return spiflash_bdev_read(bdev, buf, block << bdev->block_shift, count << bdev->block_shift); -} - -static ssize_t spiflash_bdev_write_block(struct bdev *bdev, const void *_buf, bnum_t block, uint count) -{ - LTRACEF("dev %p, buf %p, block 0x%x, count %u\n", bdev, _buf, block, count); - - DEBUG_ASSERT(bdev->block_size == PAGE_PROGRAM_SIZE); - - count = bio_trim_block_range(bdev, block, count); - if (count == 0) - return 0; - - const uint8_t *buf = _buf; - - ssize_t written = 0; - while (count > 0) { - ssize_t err = qspi_write_page(&flash.qspi, block * PAGE_PROGRAM_SIZE, buf); - if (err < 0) - return err; - - buf += PAGE_PROGRAM_SIZE; - written += err; - block++; - count--; - } - - return written; -} - -static ssize_t spiflash_bdev_erase(struct bdev *bdev, off_t offset, size_t len) -{ - LTRACEF("dev %p, offset 0x%llx, len 0x%zx\n", bdev, offset, len); - - len = bio_trim_range(bdev, offset, len); - if (len == 0) - return 0; - - ssize_t erased = 0; - while (erased < (ssize_t)len) { - ssize_t err = qspi_erase_sector(&flash.qspi, offset); - if (err < 0) - return err; - - erased += err; - offset += err; - } - - return erased; -} - -static int spiflash_ioctl(struct bdev *bdev, int request, void *argp) -{ - LTRACEF("dev %p, request %d, argp %p\n", bdev, request, argp); - - int ret = NO_ERROR; - switch (request) { - case BIO_IOCTL_GET_MEM_MAP: - /* put the device into linear mode */ - ret = qspi_enable_linear(&flash.qspi); - // Fallthrough. - case BIO_IOCTL_GET_MAP_ADDR: - if (argp) - *(void **)argp = (void *)QSPI_LINEAR_BASE; - break; - case BIO_IOCTL_PUT_MEM_MAP: - /* put the device back into regular mode */ - ret = qspi_disable_linear(&flash.qspi); - break; - default: - ret = ERR_NOT_SUPPORTED; - } - - return ret; -} - -// debug tests -int cmd_spiflash(int argc, const cmd_args *argv) -{ - if (argc < 2) { -notenoughargs: - printf("not enough arguments\n"); -usage: - printf("usage:\n"); -#if LK_DEBUGLEVEL > 1 - printf("\t%s detect\n", argv[0].str); - printf("\t%s cfi\n", argv[0].str); - printf("\t%s cr1\n", argv[0].str); - printf("\t%s otp\n", argv[0].str); - printf("\t%s linear [true/false]\n", argv[0].str); - printf("\t%s read <offset> <length>\n", argv[0].str); - printf("\t%s write <offset> <length> <address>\n", argv[0].str); - printf("\t%s erase <offset>\n", argv[0].str); -#endif - printf("\t%s setquad (dangerous)\n", argv[0].str); - return ERR_INVALID_ARGS; - } - -#if LK_DEBUGLEVEL > 1 - if (!strcmp(argv[1].str, "detect")) { - spiflash_detect(); - } else if (!strcmp(argv[1].str, "cr1")) { - if (!flash.detected) { - printf("flash not detected\n"); - return -1; - } - - uint32_t cr1 = qspi_rd_cr1(&flash.qspi); - printf("cr1 0x%x\n", cr1); - } else if (!strcmp(argv[1].str, "cfi")) { - if (!flash.detected) { - printf("flash not detected\n"); - return -1; - } - - uint8_t *buf = calloc(1, 512); - ssize_t len = spiflash_read_cfi(buf, 512); - printf("returned cfi len %ld\n", len); - - hexdump8(buf, len); - - free(buf); - } else if (!strcmp(argv[1].str, "otp")) { - if (!flash.detected) { - printf("flash not detected\n"); - return -1; - } - - uint8_t *buf = calloc(1, 1024); - ssize_t len = spiflash_read_otp(buf, 0, 1024); - printf("spiflash_read_otp returns %ld\n", len); - - hexdump8(buf, len); - - free(buf); - } else if (!strcmp(argv[1].str, "linear")) { - if (argc < 3) goto notenoughargs; - if (!flash.detected) { - printf("flash not detected\n"); - return -1; - } - - if (argv[2].b) - qspi_enable_linear(&flash.qspi); - else - qspi_disable_linear(&flash.qspi); - } else if (!strcmp(argv[1].str, "read")) { - if (argc < 4) goto notenoughargs; - if (!flash.detected) { - printf("flash not detected\n"); - return -1; - } - - uint8_t *buf = calloc(1, argv[3].u); - - qspi_rd32(&flash.qspi, argv[2].u, (uint32_t *)buf, argv[3].u / 4); - - hexdump8(buf, argv[3].u); - free(buf); - } else if (!strcmp(argv[1].str, "write")) { - if (argc < 5) goto notenoughargs; - if (!flash.detected) { - printf("flash not detected\n"); - return -1; - } - - status_t err = qspi_write_page(&flash.qspi, argv[2].u, argv[4].p); - printf("write_page returns %d\n", err); - } else if (!strcmp(argv[1].str, "erase")) { - if (argc < 3) goto notenoughargs; - if (!flash.detected) { - printf("flash not detected\n"); - return -1; - } - - status_t err = qspi_erase_sector(&flash.qspi, argv[2].u); - printf("erase returns %d\n", err); - } else -#endif - if (!strcmp(argv[1].str, "setquad")) { - if (!flash.detected) { - printf("flash not detected\n"); - return -1; - } - - uint32_t cr1 = qspi_rd_cr1(&flash.qspi); - printf("cr1 before 0x%x\n", cr1); - - if (cr1 & (1<<1)) { - printf("flash already in quad mode\n"); - return 0; - } - - qspi_wr_status_cr1(&flash.qspi, 0, cr1 | (1<<1)); - - thread_sleep(500); - cr1 = qspi_rd_cr1(&flash.qspi); - printf("cr1 after 0x%x\n", cr1); - } else { - printf("unknown command\n"); - goto usage; - } - - return 0; -} - -#if defined(WITH_LIB_CONSOLE) -#include <lib/console.h> - -STATIC_COMMAND_START -STATIC_COMMAND("spiflash", "spi flash manipulation utilities", cmd_spiflash) -STATIC_COMMAND_END(qspi); - -#endif |