[U-Boot-Users] [rfc] new spiflash subsystem

Ulf Samuelsson ulf at atmel.com
Tue Jan 29 00:01:15 CET 2008


Unfortunately, this code seems useless, at least for the combination AT91 + SPI flash.
Some issues:

I believe that the AT45 is not using the same command set as other
SPI flash memories. I think the commands need to be separated.
AT45 is much more advanced than other SPI flash.
Did you really test your code on the AT45 series?

You assume, incorrectly, that all sector sizes are the same size.

How do you do "byte writes" which is an important feature of the  AT45?

Your code does not support DMA transfers, while the current dataflash code runs DMA up to 50 Mbps.

Erasing the entire SPI flash is generally stupid, since you store the environment there.
You typically also store the initial bootloader and U-Boot.
Very rarely you want to erase the complete flash ,and a protection mechanism is
needed to avoid accidental overwrites.
The current solution allows dataflash pages to be protected.

Typically you want to store data with a checksum,since relying
on the boot of the linux kernel to produce the error, will in my experience
make people confused and they will spend a lot of time barking up the wrong tree.

There is a general problem with U-Boot which seems to assume
that there is more RAM than flash in the system.
How do you easily copy 256 MB from an SD-Card to an onboard 256 MB NAND flash
when the SDRAM is 64 MB?

Today, you have to use 10 lines (U-Boot occupies 1 MB) and that is really bad.

The vanilla way of supporting storage devices is really wasteful in resources, 
and you cannot compare two memory areas if the memory area is larger than
half the SDRAM size.

Best Regards
Ulf Samuelsson


> /*
> * SPI flash driver user interface
> *
> * Copyright (c) 2005-2008 Analog Devices Inc.
> *
> * Licensed under the GPL-2 or later.
> */
> 
> #include <common.h>
> #include <config.h>
> #include <command.h>
> 
> #if 1 //(CONFIG_COMMANDS & CFG_CMD_SPIFLASH)
> 
> int do_spiflash(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
> {
> {
> int i = 0;
> while (i < argc)
> printf("[%s] ", argv[i++]);
> printf("\n");
> }
> 
> if (argc == 1) {
> usage:
> printf("Usage:\n%s\n", cmdtp->usage);
> return 1;
> }
> 
> ulong cs = CFG_SPIFLASH_CS_DEFAULT;
> #ifdef CFG_SPIFLASH_MULTI
> cs = simple_strtoul(argv[1], NULL, 10);
> --argc;
> ++argv;
> #endif
> 
> if (!strcmp(argv[1], "info")) {
> if (argc != 2)
> goto usage;
> return spiflash_info(cs);
> }
> 
> if (!strcmp(argv[1], "erase")) {
> size_t off, cnt;
> 
> if (argc == 4) {
> off = simple_strtoul(argv[2], NULL, 16);
> cnt = simple_strtoul(argv[3], NULL, 16);
> printf("Erasing SPI flash @CS%lu:  off 0x%lx  count 0x%lx ... ",
> cs, off, cnt);
> } else if (argc == 2) {
> off = cnt = -1;
> printf("Erasing entire SPI flash @CS%lu ... ", cs);
> } else
> goto usage;
> 
> int ret = spiflash_erase(cs, off, cnt);
> puts(" done\n");
> return ret;
> }
> 
> if (argc != 5)
> goto usage;
> 
> size_t (*spiflash_func)(int cs, size_t off, size_t cnt, uchar *buffer);
> if (!strcmp(argv[1], "read"))
> spiflash_func = spiflash_read;
> else if (!strcmp(argv[1], "write"))
> spiflash_func = spiflash_write;
> else
> goto usage;
> 
> uchar *addr = (uchar *)simple_strtoul(argv[2], NULL, 16);
> size_t off = simple_strtoul(argv[3], NULL, 16);
> size_t cnt = simple_strtoul(argv[4], NULL, 16);
> 
> printf("SPI flash @CS%lu %s: addr 0x%p  off 0x%lx  count 0x%lx ... ",
> cs, argv[1], addr, off, cnt);
> size_t ret = spiflash_func(cs, off, cnt, addr);
> puts(" done\n");
> 
> return (ret != 0 ? 0 : 1);
> }
> 
> #ifdef CFG_SPIFLASH_MULTI
> U_BOOT_CMD(spiflash, 6, 1, do_spiflash,
> "spiflash - SPI flash sub-system\n",
> "read  cs addr off cnt\n"
> "spiflash write cs addr off cnt\n"
> "spiflash erase [off cnt]\n"
> "spiflash info cs\n"
> " - read/write 'cnt' bytes from SPI flash with chip-select 'cs' at offset 'off' into/from 'addr'\n"
> );
> #else
> U_BOOT_CMD(spiflash, 5, 1, do_spiflash,
> "spiflash - SPI flash sub-system\n",
> "read  addr off cnt\n"
> "spiflash write addr off cnt\n"
> "spiflash erase [off cnt]\n"
> "spiflash info\n"
> " - read/write 'cnt' bytes from SPI flash at offset 'off' into/from 'addr'\n"
> );
> #endif
> 
> #endif
>


--------------------------------------------------------------------------------


> /*
> * SPI flash driver for JEDEC compatible parts
> *
> * Copyright (c) 2005-2008 Analog Devices Inc.
> *
> * Licensed under the GPL-2 or later.
> */
> 
> #include <common.h>
> #include <config.h>
> 
> #if defined(CFG_SPIFLASH_JEDEC_DRIVER)
> 
> struct flash_info {
> char     *name;
> uint16_t id;
> unsigned sector_size;
> unsigned num_sectors;
> };
> 
> /* SPI Speeds: 50 MHz / 33 MHz */
> static struct flash_info flash_spansion_serial_flash[] = {
> { "S25FL016", 0x0215, 64 * 1024, 32 },
> { "S25FL032", 0x0216, 64 * 1024, 64 },
> { "S25FL064", 0x0217, 64 * 1024, 128 },
> { "S25FL0128", 0x0218, 256 * 1024, 64 },
> { NULL, 0, 0, 0 }
> };
> 
> /* SPI Speeds: 50 MHz / 20 MHz */
> static struct flash_info flash_st_serial_flash[] = {
> { "m25p05", 0x2010, 32 * 1024, 2 },
> { "m25p10", 0x2011, 32 * 1024, 4 },
> { "m25p20", 0x2012, 64 * 1024, 4 },
> { "m25p40", 0x2013, 64 * 1024, 8 },
> { "m25p16", 0x2015, 64 * 1024, 32 },
> { "m25p32", 0x2016, 64 * 1024, 64 },
> { "m25p64", 0x2017, 64 * 1024, 128 },
> { "m25p128", 0x2018, 256 * 1024, 64 },
> { NULL, 0, 0, 0 }
> };
> 
> /* SPI Speeds: 66 MHz / 33 MHz */
> static struct flash_info flash_atmel_dataflash[] = {
> { "AT45DB011x", 0x0c, 264, 512 },
> { "AT45DB021x", 0x14, 264, 1025 },
> { "AT45DB041x", 0x1c, 264, 2048 },
> { "AT45DB081x", 0x24, 264, 4096 },
> { "AT45DB161x", 0x2c, 528, 4096 },
> { "AT45DB321x", 0x34, 528, 8192 },
> { "AT45DB642x", 0x3c, 1056, 8192 },
> { NULL, 0, 0, 0 }
> };
> 
> /* SPI Speed: 50 MHz / 25 MHz or 40 MHz / 20 MHz */
> static struct flash_info flash_winbond_serial_flash[] = {
> { "W25X10", 0x3011, 16 * 256, 32 },
> { "W25X20", 0x3012, 16 * 256, 64 },
> { "W25X40", 0x3013, 16 * 256, 128 },
> { "W25X80", 0x3014, 16 * 256, 256 },
> { "W25P80", 0x2014, 256 * 256, 16 },
> { "W25P16", 0x2015, 256 * 256, 32 },
> { NULL, 0, 0, 0 }
> };
> 
> struct flash_ops {
> uint8_t read, write, erase, status;
> };
> 
> #define OP_READ_SLOW 0x03
> #define OP_READ_FAST 0x0B
> #ifdef CFG_SPIFLASH_SLOW_READ
> # define OP_READ OP_READ_SLOW
> #else
> # define OP_READ OP_READ_FAST
> #endif
> static struct flash_ops flash_st_ops = {
> .read = OP_READ,
> .write = 0x02,
> .erase = 0xD8,
> .status = 0x05,
> };
> 
> static struct flash_ops flash_atmel_ops = {
> .read = OP_READ,
> .write = 0x82,
> .erase = 0x81,
> .status = 0xD7,
> };
> 
> static struct flash_ops flash_winbond_ops = {
> .read = OP_READ,
> .write = 0x02,
> .erase = 0x20,
> .status = 0x05,
> };
> 
> struct manufacturer_info {
> const char *name;
> uint8_t id;
> struct flash_info *flashes;
> struct flash_ops *ops;
> };
> 
> static struct {
> struct manufacturer_info *manufacturer;
> struct flash_info *flash;
> struct flash_ops *ops;
> uint8_t manufacturer_id, device_id1, device_id2;
> unsigned int write_length;
> unsigned long sector_size, num_sectors;
> } flash;
> 
> enum {
> JED_MANU_SPANSION = 0x01,
> JED_MANU_ST       = 0x20,
> JED_MANU_ATMEL    = 0x1F,
> JED_MANU_WINBOND  = 0xEF,
> };
> 
> static struct manufacturer_info flash_manufacturers[] = {
> {
> .name = "Spansion",
> .id = JED_MANU_SPANSION,
> .flashes = flash_spansion_serial_flash,
> .ops = &flash_st_ops,
> },
> {
> .name = "ST",
> .id = JED_MANU_ST,
> .flashes = flash_st_serial_flash,
> .ops = &flash_st_ops,
> },
> {
> .name = "Atmel",
> .id = JED_MANU_ATMEL,
> .flashes = flash_atmel_dataflash,
> .ops = &flash_atmel_ops,
> },
> {
> .name = "Winbond",
> .id = JED_MANU_WINBOND,
> .flashes = flash_winbond_serial_flash,
> .ops = &flash_winbond_ops,
> },
> };
> 
> #define TIMEOUT 5000 /* timeout of 5 seconds */
> 
> static uint8_t read_status_register(int cs)
> {
> uint8_t status_register;
> 
> /* send instruction to read status register */
> spiflash_cs_set(cs, 1);
> spiflash_exchange_byte(flash.ops->status);
> /* send dummy to receive the status register */
> status_register = spiflash_exchange_byte(0);
> spiflash_cs_set(cs, 0);
> 
> return status_register;
> }
> 
> /* Request and read the manufacturer and device id of parts which
> * are compatible with the JEDEC standard (JEP106) and use that to
> * setup other operating conditions.
> */
> static int spiflash_detect_part(int cs)
> {
> uint16_t dev_id;
> size_t i;
> 
> #ifndef CFG_SPIFLASH_MULTI
> static char called_init;
> if (called_init)
> return 0;
> #endif
> 
> spiflash_cs_set(cs, 1);
> 
> /* Send the request for the part identification */
> spiflash_exchange_byte(0x9F);
> 
> /* Now read in the manufacturer id bytes */
> do {
> flash.manufacturer_id = spiflash_exchange_byte(0);
> if (flash.manufacturer_id == 0x7F)
> puts("Warning: unhandled manufacturer continuation byte!\n");
> } while (flash.manufacturer_id == 0x7F);
> 
> /* Now read in the first device id byte */
> flash.device_id1 = spiflash_exchange_byte(0);
> 
> /* Now read in the second device id byte */
> flash.device_id2 = spiflash_exchange_byte(0);
> 
> spiflash_cs_set(cs, 0);
> 
> dev_id = (flash.device_id1 << 8) | flash.device_id2;
> 
> for (i = 0; i < ARRAY_SIZE(flash_manufacturers); ++i) {
> if (flash.manufacturer_id == flash_manufacturers[i].id)
> break;
> }
> if (i == ARRAY_SIZE(flash_manufacturers))
> goto unknown;
> 
> flash.manufacturer = &flash_manufacturers[i];
> flash.ops = flash_manufacturers[i].ops;
> 
> switch (flash.manufacturer_id) {
> case JED_MANU_SPANSION:
> case JED_MANU_ST:
> case JED_MANU_WINBOND:
> for (i = 0; flash.manufacturer->flashes[i].name; ++i) {
> if (dev_id == flash.manufacturer->flashes[i].id)
> break;
> }
> if (!flash.manufacturer->flashes[i].name)
> goto unknown;
> 
> flash.flash = &flash.manufacturer->flashes[i];
> flash.sector_size = flash.flash->sector_size;
> flash.num_sectors = flash.flash->num_sectors;
> flash.write_length = 256;
> break;
> 
> case JED_MANU_ATMEL: {
> uint8_t status = read_status_register(cs);
> 
> for (i = 0; flash.manufacturer->flashes[i].name; ++i) {
> if ((status & 0x3c) == flash.manufacturer->flashes[i].id)
> break;
> }
> if (!flash.manufacturer->flashes[i].name)
> goto unknown;
> 
> flash.flash = &flash.manufacturer->flashes[i];
> flash.sector_size = flash.flash->sector_size;
> flash.num_sectors = flash.flash->num_sectors;
> 
> /* see if flash is in "power of 2" mode */
> if (status & 0x1)
> flash.sector_size &= ~(1 << (ffs(flash.sector_size) - 1));
> 
> flash.write_length = flash.sector_size;
> break;
> }
> }
> 
> #ifndef CFG_SPIFLASH_MULTI
> called_init = 1;
> #endif
> return 0;
> 
> unknown:
> printf("Unknown SPI device: 0x%02X 0x%02X 0x%02X\n",
> flash.manufacturer_id, flash.device_id1, flash.device_id2);
> return 1;
> }
> 
> static int wait_for_ready_status(int cs)
> {
> ulong start = get_timer(0);
> 
> while (get_timer(0) - start < TIMEOUT) {
> switch (flash.manufacturer_id) {
> case JED_MANU_SPANSION:
> case JED_MANU_ST:
> case JED_MANU_WINBOND:
> if (!(read_status_register(cs) & 0x01))
> return 0;
> break;
> 
> case JED_MANU_ATMEL:
> if (read_status_register(cs) & 0x80)
> return 0;
> break;
> }
> 
> if (ctrlc()) {
> puts("\nAbort\n");
> return -1;
> }
> }
> 
> puts("Timeout\n");
> return -1;
> }
> 
> static void transmit_address(size_t addr)
> {
> /* Send the highest byte of the 24 bit address at first */
> spiflash_exchange_byte(addr >> 16);
> /* Send the middle byte of the 24 bit address  at second */
> spiflash_exchange_byte(addr >> 8);
> /* Send the lowest byte of the 24 bit address finally */
> spiflash_exchange_byte(addr);
> }
> 
> static int enable_writing(int cs)
> {
> ulong start;
> 
> if (flash.manufacturer_id == JED_MANU_ATMEL)
> return 0;
> 
> /* A write enable instruction must previously have been executed */
> spiflash_cs_set(cs, 1);
> spiflash_exchange_byte(0x06);
> spiflash_cs_set(cs, 0);
> 
> /* The status register will be polled to check the write enable latch "WREN" */
> start = get_timer(0);
> while (get_timer(0) - start < TIMEOUT) {
> if (read_status_register(cs) & 0x02)
> return 0;
> 
> if (ctrlc()) {
> puts("\nAbort\n");
> return -1;
> }
> }
> 
> puts("Timeout\n");
> return -1;
> }
> 
> size_t spiflash_read(int cs, size_t off, size_t cnt, uchar *buffer)
> {
> size_t ret = cnt;
> 
> spiflash_on(cs);
> 
> if (spiflash_detect_part(cs))
> ret = 0;
> else {
> spiflash_cs_set(cs, 1);
> 
> /* Tell the flash we want to read */
> spiflash_exchange_byte(flash.ops->read);
> 
> /* Tell the flash where to read */
> transmit_address(off);
> 
> /* Send dummy byte when doing SPI fast reads */
> if (flash.ops->read == OP_READ_FAST)
> spiflash_exchange_byte(0);
> 
> /* Now grab the stream of bytes coming back */
> size_t i;
> for (i = 1; i <= cnt; ++i) {
> *buffer++ = spiflash_exchange_byte(0);
> if (i % flash.sector_size == 0)
> puts(".");
> }
> 
> spiflash_cs_set(cs, 0);
> }
> 
> spiflash_off(cs);
> 
> return ret;
> }
> 
> static size_t write_flash(int cs, size_t address, size_t cnt, uchar *buffer)
> {
> size_t i, write_buffer_size;
> 
> if (enable_writing(cs))
> return -1;
> 
> /* Send write command followed by the 24 bit address */
> spiflash_cs_set(cs, 1);
> spiflash_exchange_byte(flash.ops->write);
> transmit_address(address);
> 
> /* Shoot out a single write buffer */
> write_buffer_size = min(cnt, flash.write_length);
> for (i = 0; i < write_buffer_size; ++i)
> spiflash_exchange_byte(buffer[i]);
> 
> spiflash_cs_set(cs, 0);
> 
> /* Wait for the flash to do its thing */
> if (wait_for_ready_status(cs))
> return -1;
> else
> return i;
> }
> 
> static int write_sector(int cs, size_t address, size_t cnt, uchar *buffer)
> {
> size_t write_cnt;
> 
> while (cnt != 0) {
> write_cnt = write_flash(cs, address, cnt, buffer);
> if (write_cnt == -1)
> return -1;
> 
> /* Now that we've sent some bytes out to the flash, update
> * our counters a bit
> */
> cnt -= write_cnt;
> address += write_cnt;
> buffer += write_cnt;
> }
> 
> return 0;
> }
> 
> static int erase_sector(int cs, size_t address)
> {
> /* sector gets checked in higher function, so assume it's valid
> * here and figure out the offset of the sector in flash
> */
> if (enable_writing(cs))
> return -1;
> 
> /*
> * Send the erase block command to the flash followed by the 24 address
> * to point to the start of a sector
> */
> spiflash_cs_set(cs, 1);
> spiflash_exchange_byte(flash.ops->erase);
> transmit_address(address);
> spiflash_cs_set(cs, 0);
> 
> return wait_for_ready_status(cs);
> }
> 
> static size_t write_or_erase(int cs, size_t off, size_t cnt, uchar *buffer, int w_o_e)
> {
> int ret = cnt;
> 
> spiflash_on(cs);
> 
> if (spiflash_detect_part(cs))
> ret = 0;
> else if (off % flash.sector_size || cnt % flash.sector_size) {
> ret = 0;
> printf("\n%s: off/cnt not aligned to sector size 0x%x\n",
> __func__, flash.sector_size);
> } else {
> while (off < cnt) {
> if ((w_o_e == 'w' && write_sector(cs, off, cnt, buffer)) ||
>     (w_o_e == 'e' && erase_sector(cs, off)))
> {
> ret = 0;
> break;
> }
> off += flash.sector_size;
> puts(".");
> }
> }
> 
> spiflash_off(cs);
> 
> return ret;
> }
> 
> size_t spiflash_write(int cs, size_t off, size_t cnt, uchar *buffer)
> {
> return write_or_erase(cs, off, cnt, buffer, 'w');
> }
> 
> size_t spiflash_erase(int cs, size_t off, size_t cnt)
> {
> return write_or_erase(cs, off, cnt, NULL, 'e');
> }
> 
> int spiflash_info(int cs)
> {
> int ret = 0;
> 
> spiflash_on(cs);
> 
> if (spiflash_detect_part(cs))
> ret = 1;
> else {
> printf("SPI Device: %s 0x%02X (%s) 0x%02X 0x%02X\n"
> "Parameters: num sectors = %i, sector size = %i, write size = %i\n"
> "Flash Size: %i mbit (%i mbyte)\n"
> "Status: 0x%02X\n",
> flash.flash->name, flash.manufacturer_id, flash.manufacturer->name,
> flash.device_id1, flash.device_id2, flash.num_sectors,
> flash.sector_size, flash.write_length,
> (flash.num_sectors * flash.sector_size) >> 17,
> (flash.num_sectors * flash.sector_size) >> 20,
> read_status_register(cs));
> 
> printf(" Sector Start Addresses:");
> unsigned i, off = 0;
> for (i = 0; i < flash.num_sectors; ++i) {
> if (i % 5 == 0)
> puts("\n    ");
> printf("%08x      ", off);
> off += flash.sector_size;
> }
> if (i % 5)
> puts("\n");
> }
> 
> spiflash_off(cs);
> 
> return ret;
> }
> 
> #endif
>


--------------------------------------------------------------------------------


> /*
> * SPI flash driver
> *
> * Enter bugs at http://blackfin.uclinux.org/
> *
> * Copyright (c) 2005-2007 Analog Devices Inc.
> *
> * Licensed under the GPL-2 or later.
> */
> 
> /* Configuration options:
> * CONFIG_SPI_BAUD - value to load into SPI_BAUD (divisor of SCLK to get SPI CLK)
> * CONFIG_SPI_FLASH_SLOW_READ - force usage of the slower read
> * WARNING: make sure your SCLK + SPI_BAUD is slow enough
> */
> 
> #include <common.h>
> #include <malloc.h>
> #include <asm/io.h>
> #include <asm/mach-common/bits/spi.h>
> 
> #if defined(CFG_SPIFLASH)
> 
> void spiflash_on(int cs)
> {
> /* [#3541] This delay appears to be necessary, but not sure
> * exactly why as the history behind it is non-existant.
> */
> udelay(CONFIG_CCLK_HZ / 25000000);
> 
> /* enable SPI pins: SSEL, MOSI, MISO, SCK */
> #ifdef __ADSPBF54x__
> *pPORTE_FER |= (SPI0_SCK | SPI0_MOSI | SPI0_MISO | SPI0_SEL1);
> #elif defined(__ADSPBF534__) || defined(__ADSPBF536__) || defined(__ADSPBF537__)
> *pPORTF_FER |= (PF10 | PF11 | PF12 | PF13);
> #elif defined(__ADSPBF52x__)
> bfin_write_PORTG_MUX((bfin_read_PORTG_MUX() & ~PORT_x_MUX_0_MASK) | PORT_x_MUX_0_FUNC_3);
> bfin_write_PORTG_FER(bfin_read_PORTG_FER() | PG1 | PG2 | PG3 | PG4);
> #endif
> 
> /* initate communication upon write of TDBR */
> *pSPI_CTL = (SPE|MSTR|CPHA|CPOL|0x01);
> *pSPI_BAUD = CONFIG_SPI_BAUD;
> }
> 
> void spiflash_off(int cs)
> {
> /* put SPI settings back to reset state */
> *pSPI_CTL = 0x0400;
> *pSPI_BAUD = 0;
> SSYNC();
> }
> 
> void spiflash_cs_set(int cs, uint on)
> {
> if (on) {
> cs = 1 << cs;
> 
> /* toggle SSEL to reset the device so it'll take a new command */
> *pSPI_FLG = 0xFF00 | cs;
> SSYNC();
> 
> *pSPI_FLG = ((0xFF & ~cs) << 8) | cs;
> } else {
> /* put SPI settings back to reset state */
> *pSPI_FLG = 0xFF00;
> }
> SSYNC();
> }
> 
> uint8_t spiflash_exchange_byte(uint8_t transmit)
> {
> *pSPI_TDBR = transmit;
> SSYNC();
> 
> while ((*pSPI_STAT & TXS))
> if (ctrlc())
> break;
> while (!(*pSPI_STAT & SPIF))
> if (ctrlc())
> break;
> while (!(*pSPI_STAT & RXS))
> if (ctrlc())
> break;
> 
> /* Read dummy to empty the receive register */
> return *pSPI_RDBR;
> }
> 
> #endif
>


--------------------------------------------------------------------------------


> -------------------------------------------------------------------------
> This SF.net email is sponsored by: Microsoft
> Defy all challenges. Microsoft(R) Visual Studio 2008.
> http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/


--------------------------------------------------------------------------------


> _______________________________________________
> U-Boot-Users mailing list
> U-Boot-Users at lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/u-boot-users
>




More information about the U-Boot mailing list