diff options
Diffstat (limited to 'roms/u-boot-sam460ex/drivers/mtd/spi')
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mtd/spi/Makefile | 53 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mtd/spi/atmel.c | 551 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mtd/spi/eeprom_m95xxx.c | 122 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mtd/spi/macronix.c | 337 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mtd/spi/spansion.c | 350 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mtd/spi/spi_flash.c | 183 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mtd/spi/spi_flash_internal.h | 52 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mtd/spi/sst.c | 374 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mtd/spi/stmicro.c | 351 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mtd/spi/winbond.c | 332 |
10 files changed, 2705 insertions, 0 deletions
diff --git a/roms/u-boot-sam460ex/drivers/mtd/spi/Makefile b/roms/u-boot-sam460ex/drivers/mtd/spi/Makefile new file mode 100644 index 000000000..4f11b3631 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/spi/Makefile @@ -0,0 +1,53 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libspi_flash.a + +COBJS-$(CONFIG_SPI_FLASH) += spi_flash.o +COBJS-$(CONFIG_SPI_FLASH_ATMEL) += atmel.o +COBJS-$(CONFIG_SPI_FLASH_MACRONIX) += macronix.o +COBJS-$(CONFIG_SPI_FLASH_SPANSION) += spansion.o +COBJS-$(CONFIG_SPI_FLASH_SST) += sst.o +COBJS-$(CONFIG_SPI_FLASH_STMICRO) += stmicro.o +COBJS-$(CONFIG_SPI_FLASH_WINBOND) += winbond.o +COBJS-$(CONFIG_SPI_M95XXX) += eeprom_m95xxx.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/roms/u-boot-sam460ex/drivers/mtd/spi/atmel.c b/roms/u-boot-sam460ex/drivers/mtd/spi/atmel.c new file mode 100644 index 000000000..8306c000d --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/spi/atmel.c @@ -0,0 +1,551 @@ +/* + * Atmel SPI DataFlash support + * + * Copyright (C) 2008 Atmel Corporation + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +/* AT45-specific commands */ +#define CMD_AT45_READ_STATUS 0xd7 +#define CMD_AT45_ERASE_PAGE 0x81 +#define CMD_AT45_LOAD_PROG_BUF1 0x82 +#define CMD_AT45_LOAD_BUF1 0x84 +#define CMD_AT45_LOAD_PROG_BUF2 0x85 +#define CMD_AT45_LOAD_BUF2 0x87 +#define CMD_AT45_PROG_BUF1 0x88 +#define CMD_AT45_PROG_BUF2 0x89 + +/* AT45 status register bits */ +#define AT45_STATUS_P2_PAGE_SIZE (1 << 0) +#define AT45_STATUS_READY (1 << 7) + +/* DataFlash family IDs, as obtained from the second idcode byte */ +#define DF_FAMILY_AT26F 0 +#define DF_FAMILY_AT45 1 +#define DF_FAMILY_AT26DF 2 /* AT25DF and AT26DF */ + +struct atmel_spi_flash_params { + u8 idcode1; + /* Log2 of page size in power-of-two mode */ + u8 l2_page_size; + u8 pages_per_block; + u8 blocks_per_sector; + u8 nr_sectors; + const char *name; +}; + +/* spi_flash needs to be first so upper layers can free() it */ +struct atmel_spi_flash { + struct spi_flash flash; + const struct atmel_spi_flash_params *params; +}; + +static inline struct atmel_spi_flash * +to_atmel_spi_flash(struct spi_flash *flash) +{ + return container_of(flash, struct atmel_spi_flash, flash); +} + +static const struct atmel_spi_flash_params atmel_spi_flash_table[] = { + { + .idcode1 = 0x22, + .l2_page_size = 8, + .pages_per_block = 8, + .blocks_per_sector = 16, + .nr_sectors = 4, + .name = "AT45DB011D", + }, + { + .idcode1 = 0x23, + .l2_page_size = 8, + .pages_per_block = 8, + .blocks_per_sector = 16, + .nr_sectors = 8, + .name = "AT45DB021D", + }, + { + .idcode1 = 0x24, + .l2_page_size = 8, + .pages_per_block = 8, + .blocks_per_sector = 32, + .nr_sectors = 8, + .name = "AT45DB041D", + }, + { + .idcode1 = 0x25, + .l2_page_size = 8, + .pages_per_block = 8, + .blocks_per_sector = 32, + .nr_sectors = 16, + .name = "AT45DB081D", + }, + { + .idcode1 = 0x26, + .l2_page_size = 9, + .pages_per_block = 8, + .blocks_per_sector = 32, + .nr_sectors = 16, + .name = "AT45DB161D", + }, + { + .idcode1 = 0x27, + .l2_page_size = 9, + .pages_per_block = 8, + .blocks_per_sector = 64, + .nr_sectors = 64, + .name = "AT45DB321D", + }, + { + .idcode1 = 0x28, + .l2_page_size = 10, + .pages_per_block = 8, + .blocks_per_sector = 32, + .nr_sectors = 32, + .name = "AT45DB642D", + }, +}; + +static int at45_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + int ret; + u8 cmd = CMD_AT45_READ_STATUS; + u8 status; + + timebase = get_timer(0); + + ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); + if (ret) + return -1; + + do { + ret = spi_xfer(spi, 8, NULL, &status, 0); + if (ret) + return -1; + + if (status & AT45_STATUS_READY) + break; + } while (get_timer(timebase) < timeout); + + /* Deactivate CS */ + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if (status & AT45_STATUS_READY) + return 0; + + /* Timed out */ + return -1; +} + +/* + * Assemble the address part of a command for AT45 devices in + * non-power-of-two page size mode. + */ +static void at45_build_address(struct atmel_spi_flash *asf, u8 *cmd, u32 offset) +{ + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + unsigned int page_shift; + + /* + * The "extra" space per page is the power-of-two page size + * divided by 32. + */ + page_shift = asf->params->l2_page_size; + page_size = (1 << page_shift) + (1 << (page_shift - 5)); + page_shift++; + page_addr = offset / page_size; + byte_addr = offset % page_size; + + cmd[0] = page_addr >> (16 - page_shift); + cmd[1] = page_addr << (page_shift - 8) | (byte_addr >> 8); + cmd[2] = byte_addr; +} + +static int dataflash_read_fast_p2(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + u8 cmd[5]; + + cmd[0] = CMD_READ_ARRAY_FAST; + cmd[1] = offset >> 16; + cmd[2] = offset >> 8; + cmd[3] = offset; + cmd[4] = 0x00; + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int dataflash_read_fast_at45(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); + u8 cmd[5]; + + cmd[0] = CMD_READ_ARRAY_FAST; + at45_build_address(asf, cmd + 1, offset); + cmd[4] = 0x00; + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +/* + * TODO: the two write funcs (_p2/_at45) should get unified ... + */ +static int dataflash_write_p2(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); + unsigned long page_size; + u32 addr = offset; + size_t chunk_len; + size_t actual; + int ret; + u8 cmd[4]; + + /* + * TODO: This function currently uses only page buffer #1. We can + * speed this up by using both buffers and loading one buffer while + * the other is being programmed into main memory. + */ + + page_size = (1 << asf->params->l2_page_size); + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - (addr % page_size)); + + /* Use the same address bits for both commands */ + cmd[0] = CMD_AT45_LOAD_BUF1; + cmd[1] = addr >> 16; + cmd[2] = addr >> 8; + cmd[3] = addr; + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: Loading AT45 buffer failed\n"); + goto out; + } + + cmd[0] = CMD_AT45_PROG_BUF1; + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: AT45 page programming failed\n"); + goto out; + } + + ret = at45_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: AT45 page programming timed out\n"); + goto out; + } + + addr += chunk_len; + } + + debug("SF: AT45: Successfully programmed %zu bytes @ 0x%x\n", + len, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + +static int dataflash_write_at45(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + unsigned int page_shift; + size_t chunk_len; + size_t actual; + int ret; + u8 cmd[4]; + + /* + * TODO: This function currently uses only page buffer #1. We can + * speed this up by using both buffers and loading one buffer while + * the other is being programmed into main memory. + */ + + page_shift = asf->params->l2_page_size; + page_size = (1 << page_shift) + (1 << (page_shift - 5)); + page_shift++; + page_addr = offset / page_size; + byte_addr = offset % page_size; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - byte_addr); + + /* Use the same address bits for both commands */ + cmd[0] = CMD_AT45_LOAD_BUF1; + cmd[1] = page_addr >> (16 - page_shift); + cmd[2] = page_addr << (page_shift - 8) | (byte_addr >> 8); + cmd[3] = byte_addr; + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: Loading AT45 buffer failed\n"); + goto out; + } + + cmd[0] = CMD_AT45_PROG_BUF1; + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: AT45 page programming failed\n"); + goto out; + } + + ret = at45_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: AT45 page programming timed out\n"); + goto out; + } + + page_addr++; + byte_addr = 0; + } + + debug("SF: AT45: Successfully programmed %zu bytes @ 0x%x\n", + len, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + +/* + * TODO: the two erase funcs (_p2/_at45) should get unified ... + */ +int dataflash_erase_p2(struct spi_flash *flash, u32 offset, size_t len) +{ + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); + unsigned long page_size; + + size_t actual; + int ret; + u8 cmd[4]; + + /* + * TODO: This function currently uses page erase only. We can + * probably speed things up by using block and/or sector erase + * when possible. + */ + + page_size = (1 << asf->params->l2_page_size); + + if (offset % page_size || len % page_size) { + debug("SF: Erase offset/length not multiple of page size\n"); + return -1; + } + + cmd[0] = CMD_AT45_ERASE_PAGE; + cmd[3] = 0x00; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual += page_size) { + cmd[1] = offset >> 16; + cmd[2] = offset >> 8; + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: AT45 page erase failed\n"); + goto out; + } + + ret = at45_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret < 0) { + debug("SF: AT45 page erase timed out\n"); + goto out; + } + + offset += page_size; + } + + debug("SF: AT45: Successfully erased %zu bytes @ 0x%x\n", + len, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + +int dataflash_erase_at45(struct spi_flash *flash, u32 offset, size_t len) +{ + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); + unsigned long page_addr; + unsigned long page_size; + unsigned int page_shift; + size_t actual; + int ret; + u8 cmd[4]; + + /* + * TODO: This function currently uses page erase only. We can + * probably speed things up by using block and/or sector erase + * when possible. + */ + + page_shift = asf->params->l2_page_size; + page_size = (1 << page_shift) + (1 << (page_shift - 5)); + page_shift++; + page_addr = offset / page_size; + + if (offset % page_size || len % page_size) { + debug("SF: Erase offset/length not multiple of page size\n"); + return -1; + } + + cmd[0] = CMD_AT45_ERASE_PAGE; + cmd[3] = 0x00; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual += page_size) { + cmd[1] = page_addr >> (16 - page_shift); + cmd[2] = page_addr << (page_shift - 8); + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: AT45 page erase failed\n"); + goto out; + } + + ret = at45_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret < 0) { + debug("SF: AT45 page erase timed out\n"); + goto out; + } + + page_addr++; + } + + debug("SF: AT45: Successfully erased %zu bytes @ 0x%x\n", + len, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + +struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode) +{ + const struct atmel_spi_flash_params *params; + unsigned long page_size; + unsigned int family; + struct atmel_spi_flash *asf; + unsigned int i; + int ret; + u8 status; + + for (i = 0; i < ARRAY_SIZE(atmel_spi_flash_table); i++) { + params = &atmel_spi_flash_table[i]; + if (params->idcode1 == idcode[1]) + break; + } + + if (i == ARRAY_SIZE(atmel_spi_flash_table)) { + debug("SF: Unsupported DataFlash ID %02x\n", + idcode[1]); + return NULL; + } + + asf = malloc(sizeof(struct atmel_spi_flash)); + if (!asf) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + asf->params = params; + asf->flash.spi = spi; + asf->flash.name = params->name; + + /* Assuming power-of-two page size initially. */ + page_size = 1 << params->l2_page_size; + + family = idcode[1] >> 5; + + switch (family) { + case DF_FAMILY_AT45: + /* + * AT45 chips have configurable page size. The status + * register indicates which configuration is active. + */ + ret = spi_flash_cmd(spi, CMD_AT45_READ_STATUS, &status, 1); + if (ret) + goto err; + + debug("SF: AT45 status register: %02x\n", status); + + if (!(status & AT45_STATUS_P2_PAGE_SIZE)) { + asf->flash.read = dataflash_read_fast_at45; + asf->flash.write = dataflash_write_at45; + asf->flash.erase = dataflash_erase_at45; + page_size += 1 << (params->l2_page_size - 5); + } else { + asf->flash.read = dataflash_read_fast_p2; + asf->flash.write = dataflash_write_p2; + asf->flash.erase = dataflash_erase_p2; + } + + break; + + case DF_FAMILY_AT26F: + case DF_FAMILY_AT26DF: + asf->flash.read = dataflash_read_fast_p2; + break; + + default: + debug("SF: Unsupported DataFlash family %u\n", family); + goto err; + } + + asf->flash.size = page_size * params->pages_per_block + * params->blocks_per_sector + * params->nr_sectors; + + debug("SF: Detected %s with page size %lu, total %u bytes\n", + params->name, page_size, asf->flash.size); + + return &asf->flash; + +err: + free(asf); + return NULL; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/spi/eeprom_m95xxx.c b/roms/u-boot-sam460ex/drivers/mtd/spi/eeprom_m95xxx.c new file mode 100644 index 000000000..632db4e9e --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/spi/eeprom_m95xxx.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2009 + * Albin Tonnerre, Free Electrons <albin.tonnerre@free-electrons.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <spi.h> + +#define SPI_EEPROM_WREN 0x06 +#define SPI_EEPROM_RDSR 0x05 +#define SPI_EEPROM_READ 0x03 +#define SPI_EEPROM_WRITE 0x02 + +#ifndef CONFIG_DEFAULT_SPI_BUS +#define CONFIG_DEFAULT_SPI_BUS 0 +#endif + +#ifndef CONFIG_DEFAULT_SPI_MODE +#define CONFIG_DEFAULT_SPI_MODE SPI_MODE_0 +#endif + +ssize_t spi_read (uchar *addr, int alen, uchar *buffer, int len) +{ + struct spi_slave *slave; + u8 cmd = SPI_EEPROM_READ; + + slave = spi_setup_slave(CONFIG_DEFAULT_SPI_BUS, 1, 1000000, + CONFIG_DEFAULT_SPI_MODE); + if(!slave) + return 0; + + spi_claim_bus(slave); + + /* command */ + if(spi_xfer(slave, 8, &cmd, NULL, SPI_XFER_BEGIN)) + return -1; + + /* + * if alen == 3, addr[0] is the block number, we never use it here. All we + * need are the lower 16 bits + */ + if (alen == 3) + addr++; + + /* address, and data */ + if(spi_xfer(slave, 16, addr, NULL, 0)) + return -1; + if(spi_xfer(slave, 8 * len, NULL, buffer, SPI_XFER_END)) + return -1; + + spi_release_bus(slave); + spi_free_slave(slave); + return len; +} + +ssize_t spi_write (uchar *addr, int alen, uchar *buffer, int len) +{ + struct spi_slave *slave; + char buf[3]; + + slave = spi_setup_slave(CONFIG_DEFAULT_SPI_BUS, 1, 1000000, + CONFIG_DEFAULT_SPI_MODE); + if (!slave) + return 0; + + spi_claim_bus(slave); + + buf[0] = SPI_EEPROM_WREN; + if(spi_xfer(slave, 8, buf, NULL, SPI_XFER_BEGIN | SPI_XFER_END)) + return -1; + + buf[0] = SPI_EEPROM_WRITE; + + /* As for reading, drop addr[0] if alen is 3 */ + if (alen == 3) { + alen--; + addr++; + } + + memcpy(buf + 1, addr, alen); + /* command + addr, then data */ + if(spi_xfer(slave, 24, buf, NULL, SPI_XFER_BEGIN)) + return -1; + if(spi_xfer(slave, len * 8, buffer, NULL, SPI_XFER_END)) + return -1; + + reset_timer_masked(); + do { + buf[0] = SPI_EEPROM_RDSR; + buf[1] = 0; + spi_xfer(slave, 16, buf, buf, SPI_XFER_BEGIN | SPI_XFER_END); + + if (!(buf[1] & 1)) + break; + + } while (get_timer_masked() < CONFIG_SYS_SPI_WRITE_TOUT); + + if (buf[1] & 1) + printf ("*** spi_write: Time out while writing!\n"); + + spi_release_bus(slave); + spi_free_slave(slave); + return len; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/spi/macronix.c b/roms/u-boot-sam460ex/drivers/mtd/spi/macronix.c new file mode 100644 index 000000000..fe1310bf9 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/spi/macronix.c @@ -0,0 +1,337 @@ +/* + * Copyright 2009(C) Marvell International Ltd. and its affiliates + * Prafulla Wadaskar <prafulla@marvell.com> + * + * Based on drivers/mtd/spi/stmicro.c + * + * Copyright 2008, Network Appliance Inc. + * Jason McMullan <mcmullan@netapp.com> + * + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +/* MX25xx-specific commands */ +#define CMD_MX25XX_WREN 0x06 /* Write Enable */ +#define CMD_MX25XX_WRDI 0x04 /* Write Disable */ +#define CMD_MX25XX_RDSR 0x05 /* Read Status Register */ +#define CMD_MX25XX_WRSR 0x01 /* Write Status Register */ +#define CMD_MX25XX_READ 0x03 /* Read Data Bytes */ +#define CMD_MX25XX_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ +#define CMD_MX25XX_PP 0x02 /* Page Program */ +#define CMD_MX25XX_SE 0x20 /* Sector Erase */ +#define CMD_MX25XX_BE 0xD8 /* Block Erase */ +#define CMD_MX25XX_CE 0xc7 /* Chip Erase */ +#define CMD_MX25XX_DP 0xb9 /* Deep Power-down */ +#define CMD_MX25XX_RES 0xab /* Release from DP, and Read Signature */ + +#define MACRONIX_SR_WIP (1 << 0) /* Write-in-Progress */ + +struct macronix_spi_flash_params { + u16 idcode; + u16 page_size; + u16 pages_per_sector; + u16 sectors_per_block; + u16 nr_blocks; + const char *name; +}; + +struct macronix_spi_flash { + struct spi_flash flash; + const struct macronix_spi_flash_params *params; +}; + +static inline struct macronix_spi_flash *to_macronix_spi_flash(struct spi_flash + *flash) +{ + return container_of(flash, struct macronix_spi_flash, flash); +} + +static const struct macronix_spi_flash_params macronix_spi_flash_table[] = { + { + .idcode = 0x2015, + .page_size = 256, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 32, + .name = "MX25L1605D", + }, + { + .idcode = 0x2016, + .page_size = 256, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 64, + .name = "MX25L3205D", + }, + { + .idcode = 0x2017, + .page_size = 256, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 128, + .name = "MX25L6405D", + }, + { + .idcode = 0x2018, + .page_size = 256, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 256, + .name = "MX25L12805D", + }, + { + .idcode = 0x2618, + .page_size = 256, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 256, + .name = "MX25L12855E", + }, +}; + +static int macronix_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + int ret; + u8 status; + u8 cmd = CMD_MX25XX_RDSR; + + ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); + if (ret) { + debug("SF: Failed to send command %02x: %d\n", cmd, ret); + return ret; + } + + timebase = get_timer(0); + do { + ret = spi_xfer(spi, 8, NULL, &status, 0); + if (ret) + return -1; + + if ((status & MACRONIX_SR_WIP) == 0) + break; + + } while (get_timer(timebase) < timeout); + + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if ((status & MACRONIX_SR_WIP) == 0) + return 0; + + /* Timed out */ + return -1; +} + +static int macronix_read_fast(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + struct macronix_spi_flash *mcx = to_macronix_spi_flash(flash); + unsigned long page_addr; + unsigned long page_size; + u8 cmd[5]; + + page_size = mcx->params->page_size; + page_addr = offset / page_size; + + cmd[0] = CMD_READ_ARRAY_FAST; + cmd[1] = page_addr >> 8; + cmd[2] = page_addr; + cmd[3] = offset % page_size; + cmd[4] = 0x00; + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int macronix_write(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + struct macronix_spi_flash *mcx = to_macronix_spi_flash(flash); + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + size_t chunk_len; + size_t actual; + int ret; + u8 cmd[4]; + + page_size = mcx->params->page_size; + page_addr = offset / page_size; + byte_addr = offset % page_size; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + ret = 0; + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - byte_addr); + + cmd[0] = CMD_MX25XX_PP; + cmd[1] = page_addr >> 8; + cmd[2] = page_addr; + cmd[3] = byte_addr; + + debug + ("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n", + buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); + + ret = spi_flash_cmd(flash->spi, CMD_MX25XX_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + break; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: Macronix Page Program failed\n"); + break; + } + + ret = macronix_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: Macronix page programming timed out\n"); + break; + } + + page_addr++; + byte_addr = 0; + } + + debug("SF: Macronix: Successfully programmed %u bytes @ 0x%x\n", + len, offset); + + spi_release_bus(flash->spi); + return ret; +} + +int macronix_erase(struct spi_flash *flash, u32 offset, size_t len) +{ + struct macronix_spi_flash *mcx = to_macronix_spi_flash(flash); + unsigned long sector_size; + size_t actual; + int ret; + u8 cmd[4]; + + /* + * This function currently uses sector erase only. + * probably speed things up by using bulk erase + * when possible. + */ + + sector_size = mcx->params->page_size * mcx->params->pages_per_sector + * mcx->params->sectors_per_block; + + if (offset % sector_size || len % sector_size) { + debug("SF: Erase offset/length not multiple of sector size\n"); + return -1; + } + + len /= sector_size; + cmd[0] = CMD_MX25XX_BE; + cmd[2] = 0x00; + cmd[3] = 0x00; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + ret = 0; + for (actual = 0; actual < len; actual++) { + cmd[1] = (offset / sector_size) + actual; + + ret = spi_flash_cmd(flash->spi, CMD_MX25XX_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + break; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: Macronix page erase failed\n"); + break; + } + + ret = macronix_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret < 0) { + debug("SF: Macronix page erase timed out\n"); + break; + } + } + + debug("SF: Macronix: Successfully erased %u bytes @ 0x%x\n", + len * sector_size, offset); + + spi_release_bus(flash->spi); + return ret; +} + +struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode) +{ + const struct macronix_spi_flash_params *params; + struct macronix_spi_flash *mcx; + unsigned int i; + u16 id = idcode[2] | idcode[1] << 8; + + for (i = 0; i < ARRAY_SIZE(macronix_spi_flash_table); i++) { + params = ¯onix_spi_flash_table[i]; + if (params->idcode == id) + break; + } + + if (i == ARRAY_SIZE(macronix_spi_flash_table)) { + debug("SF: Unsupported Macronix ID %04x\n", id); + return NULL; + } + + mcx = malloc(sizeof(*mcx)); + if (!mcx) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + mcx->params = params; + mcx->flash.spi = spi; + mcx->flash.name = params->name; + + mcx->flash.write = macronix_write; + mcx->flash.erase = macronix_erase; + mcx->flash.read = macronix_read_fast; + mcx->flash.size = params->page_size * params->pages_per_sector + * params->sectors_per_block * params->nr_blocks; + + printf("SF: Detected %s with page size %u, total %u bytes\n", + params->name, params->page_size, mcx->flash.size); + + return &mcx->flash; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/spi/spansion.c b/roms/u-boot-sam460ex/drivers/mtd/spi/spansion.c new file mode 100644 index 000000000..fdb791798 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/spi/spansion.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2009 Freescale Semiconductor, Inc. + * + * Author: Mingkai Hu (Mingkai.hu@freescale.com) + * Based on stmicro.c by Wolfgang Denk (wd@denx.de), + * TsiChung Liew (Tsi-Chung.Liew@freescale.com), + * and Jason McMullan (mcmullan@netapp.com) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +/* S25FLxx-specific commands */ +#define CMD_S25FLXX_READ 0x03 /* Read Data Bytes */ +#define CMD_S25FLXX_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ +#define CMD_S25FLXX_READID 0x90 /* Read Manufacture ID and Device ID */ +#define CMD_S25FLXX_WREN 0x06 /* Write Enable */ +#define CMD_S25FLXX_WRDI 0x04 /* Write Disable */ +#define CMD_S25FLXX_RDSR 0x05 /* Read Status Register */ +#define CMD_S25FLXX_WRSR 0x01 /* Write Status Register */ +#define CMD_S25FLXX_PP 0x02 /* Page Program */ +#define CMD_S25FLXX_SE 0xd8 /* Sector Erase */ +#define CMD_S25FLXX_BE 0xc7 /* Bulk Erase */ +#define CMD_S25FLXX_DP 0xb9 /* Deep Power-down */ +#define CMD_S25FLXX_RES 0xab /* Release from DP, and Read Signature */ + +#define SPSN_ID_S25FL008A 0x0213 +#define SPSN_ID_S25FL016A 0x0214 +#define SPSN_ID_S25FL032A 0x0215 +#define SPSN_ID_S25FL064A 0x0216 +#define SPSN_ID_S25FL128P 0x2018 +#define SPSN_EXT_ID_S25FL128P_256KB 0x0300 +#define SPSN_EXT_ID_S25FL128P_64KB 0x0301 + +#define SPANSION_SR_WIP (1 << 0) /* Write-in-Progress */ + +struct spansion_spi_flash_params { + u16 idcode1; + u16 idcode2; + u16 page_size; + u16 pages_per_sector; + u16 nr_sectors; + const char *name; +}; + +struct spansion_spi_flash { + struct spi_flash flash; + const struct spansion_spi_flash_params *params; +}; + +static inline struct spansion_spi_flash *to_spansion_spi_flash(struct spi_flash + *flash) +{ + return container_of(flash, struct spansion_spi_flash, flash); +} + +static const struct spansion_spi_flash_params spansion_spi_flash_table[] = { + { + .idcode1 = SPSN_ID_S25FL008A, + .idcode2 = 0, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 16, + .name = "S25FL008A", + }, + { + .idcode1 = SPSN_ID_S25FL016A, + .idcode2 = 0, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 32, + .name = "S25FL016A", + }, + { + .idcode1 = SPSN_ID_S25FL032A, + .idcode2 = 0, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 64, + .name = "S25FL032A", + }, + { + .idcode1 = SPSN_ID_S25FL064A, + .idcode2 = 0, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 128, + .name = "S25FL064A", + }, + { + .idcode1 = SPSN_ID_S25FL128P, + .idcode2 = SPSN_EXT_ID_S25FL128P_64KB, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 256, + .name = "S25FL128P_64K", + }, + { + .idcode1 = SPSN_ID_S25FL128P, + .idcode2 = SPSN_EXT_ID_S25FL128P_256KB, + .page_size = 256, + .pages_per_sector = 1024, + .nr_sectors = 64, + .name = "S25FL128P_256K", + }, +}; + +static int spansion_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + int ret; + u8 status; + + timebase = get_timer(0); + do { + ret = spi_flash_cmd(spi, CMD_S25FLXX_RDSR, &status, sizeof(status)); + if (ret) + return -1; + + if ((status & SPANSION_SR_WIP) == 0) + break; + + } while (get_timer(timebase) < timeout); + + + if ((status & SPANSION_SR_WIP) == 0) + return 0; + + /* Timed out */ + return -1; +} + +static int spansion_read_fast(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash); + unsigned long page_addr; + unsigned long page_size; + u8 cmd[5]; + + page_size = spsn->params->page_size; + page_addr = offset / page_size; + + cmd[0] = CMD_READ_ARRAY_FAST; + cmd[1] = page_addr >> 8; + cmd[2] = page_addr; + cmd[3] = offset % page_size; + cmd[4] = 0x00; + + debug + ("READ: 0x%x => cmd = { 0x%02x 0x%02x%02x%02x%02x } len = 0x%x\n", + offset, cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], len); + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int spansion_write(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash); + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + size_t chunk_len; + size_t actual; + int ret; + u8 cmd[4]; + + page_size = spsn->params->page_size; + page_addr = offset / page_size; + byte_addr = offset % page_size; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + ret = 0; + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - byte_addr); + + cmd[0] = CMD_S25FLXX_PP; + cmd[1] = page_addr >> 8; + cmd[2] = page_addr; + cmd[3] = byte_addr; + + debug + ("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n", + buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); + + ret = spi_flash_cmd(flash->spi, CMD_S25FLXX_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + break; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: SPANSION Page Program failed\n"); + break; + } + + ret = spansion_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: SPANSION page programming timed out\n"); + break; + } + + page_addr++; + byte_addr = 0; + } + + debug("SF: SPANSION: Successfully programmed %u bytes @ 0x%x\n", + len, offset); + + spi_release_bus(flash->spi); + return ret; +} + +int spansion_erase(struct spi_flash *flash, u32 offset, size_t len) +{ + struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash); + unsigned long sector_size; + size_t actual; + int ret; + u8 cmd[4]; + + /* + * This function currently uses sector erase only. + * probably speed things up by using bulk erase + * when possible. + */ + + sector_size = spsn->params->page_size * spsn->params->pages_per_sector; + + if (offset % sector_size || len % sector_size) { + debug("SF: Erase offset/length not multiple of sector size\n"); + return -1; + } + + len /= sector_size; + cmd[0] = CMD_S25FLXX_SE; + cmd[2] = 0x00; + cmd[3] = 0x00; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + ret = 0; + for (actual = 0; actual < len; actual++) { + cmd[1] = (offset / sector_size) + actual; + + ret = spi_flash_cmd(flash->spi, CMD_S25FLXX_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + break; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: SPANSION page erase failed\n"); + break; + } + + /* Up to 2 seconds */ + ret = spansion_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret < 0) { + debug("SF: SPANSION page erase timed out\n"); + break; + } + } + + debug("SF: SPANSION: Successfully erased %u bytes @ 0x%x\n", + len * sector_size, offset); + + spi_release_bus(flash->spi); + return ret; +} + +struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode) +{ + const struct spansion_spi_flash_params *params; + struct spansion_spi_flash *spsn; + unsigned int i; + unsigned short jedec, ext_jedec; + + jedec = idcode[1] << 8 | idcode[2]; + ext_jedec = idcode[3] << 8 | idcode[4]; + + for (i = 0; i < ARRAY_SIZE(spansion_spi_flash_table); i++) { + params = &spansion_spi_flash_table[i]; + if (params->idcode1 == jedec) { + if (params->idcode2 == ext_jedec) + break; + } + } + + if (i == ARRAY_SIZE(spansion_spi_flash_table)) { + debug("SF: Unsupported SPANSION ID %04x %04x\n", jedec, ext_jedec); + return NULL; + } + + spsn = malloc(sizeof(struct spansion_spi_flash)); + if (!spsn) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + spsn->params = params; + spsn->flash.spi = spi; + spsn->flash.name = params->name; + + spsn->flash.write = spansion_write; + spsn->flash.erase = spansion_erase; + spsn->flash.read = spansion_read_fast; + spsn->flash.size = params->page_size * params->pages_per_sector + * params->nr_sectors; + + debug("SF: Detected %s with page size %u, total %u bytes\n", + params->name, params->page_size, spsn->flash.size); + + return &spsn->flash; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/spi/spi_flash.c b/roms/u-boot-sam460ex/drivers/mtd/spi/spi_flash.c new file mode 100644 index 000000000..612f819dc --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/spi/spi_flash.c @@ -0,0 +1,183 @@ +/* + * SPI flash interface + * + * Copyright (C) 2008 Atmel Corporation + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <malloc.h> +#include <spi.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (len == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(spi, 8, &cmd, NULL, flags); + if (ret) { + debug("SF: Failed to send command %02x: %d\n", cmd, ret); + return ret; + } + + if (len) { + ret = spi_xfer(spi, len * 8, NULL, response, SPI_XFER_END); + if (ret) + debug("SF: Failed to read response (%zu bytes): %d\n", + len, ret); + } + + return ret; +} + +int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (data_len == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); + if (ret) { + debug("SF: Failed to send read command (%zu bytes): %d\n", + cmd_len, ret); + } else if (data_len != 0) { + ret = spi_xfer(spi, data_len * 8, NULL, data, SPI_XFER_END); + if (ret) + debug("SF: Failed to read %zu bytes of data: %d\n", + data_len, ret); + } + + return ret; +} + +int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, + const void *data, size_t data_len) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (data_len == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); + if (ret) { + debug("SF: Failed to send read command (%zu bytes): %d\n", + cmd_len, ret); + } else if (data_len != 0) { + ret = spi_xfer(spi, data_len * 8, data, NULL, SPI_XFER_END); + if (ret) + debug("SF: Failed to read %zu bytes of data: %d\n", + data_len, ret); + } + + return ret; +} + + +int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len) +{ + struct spi_slave *spi = flash->spi; + int ret; + + spi_claim_bus(spi); + ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); + spi_release_bus(spi); + + return ret; +} + +struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode) +{ + struct spi_slave *spi; + struct spi_flash *flash; + int ret; + u8 idcode[5]; + + spi = spi_setup_slave(bus, cs, max_hz, spi_mode); + if (!spi) { + debug("SF: Failed to set up slave\n"); + return NULL; + } + + ret = spi_claim_bus(spi); + if (ret) { + debug("SF: Failed to claim SPI bus: %d\n", ret); + goto err_claim_bus; + } + + /* Read the ID codes */ + ret = spi_flash_cmd(spi, CMD_READ_ID, &idcode, sizeof(idcode)); + if (ret) + goto err_read_id; + + debug("SF: Got idcode %02x %02x %02x %02x %02x\n", idcode[0], + idcode[1], idcode[2], idcode[3], idcode[4]); + + switch (idcode[0]) { +#ifdef CONFIG_SPI_FLASH_SPANSION + case 0x01: + flash = spi_flash_probe_spansion(spi, idcode); + break; +#endif +#ifdef CONFIG_SPI_FLASH_ATMEL + case 0x1F: + flash = spi_flash_probe_atmel(spi, idcode); + break; +#endif +#ifdef CONFIG_SPI_FLASH_MACRONIX + case 0xc2: + flash = spi_flash_probe_macronix(spi, idcode); + break; +#endif +#ifdef CONFIG_SPI_FLASH_WINBOND + case 0xef: + flash = spi_flash_probe_winbond(spi, idcode); + break; +#endif +#ifdef CONFIG_SPI_FLASH_STMICRO + case 0x20: + flash = spi_flash_probe_stmicro(spi, idcode); + break; +#endif +#ifdef CONFIG_SPI_FLASH_SST + case 0xBF: + flash = spi_flash_probe_sst(spi, idcode); + break; +#endif + default: + debug("SF: Unsupported manufacturer %02X\n", idcode[0]); + flash = NULL; + break; + } + + if (!flash) + goto err_manufacturer_probe; + + spi_release_bus(spi); + + return flash; + +err_manufacturer_probe: +err_read_id: + spi_release_bus(spi); +err_claim_bus: + spi_free_slave(spi); + return NULL; +} + +void spi_flash_free(struct spi_flash *flash) +{ + spi_free_slave(flash->spi); + free(flash); +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/spi/spi_flash_internal.h b/roms/u-boot-sam460ex/drivers/mtd/spi/spi_flash_internal.h new file mode 100644 index 000000000..08546fbb0 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/spi/spi_flash_internal.h @@ -0,0 +1,52 @@ +/* + * SPI flash internal definitions + * + * Copyright (C) 2008 Atmel Corporation + */ + +/* Common parameters -- kind of high, but they should only occur when there + * is a problem (and well your system already is broken), so err on the side + * of caution in case we're dealing with slower SPI buses and/or processors. + */ +#define SPI_FLASH_PROG_TIMEOUT (2 * CONFIG_SYS_HZ) +#define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ) +#define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CONFIG_SYS_HZ) + +/* Common commands */ +#define CMD_READ_ID 0x9f + +#define CMD_READ_ARRAY_SLOW 0x03 +#define CMD_READ_ARRAY_FAST 0x0b +#define CMD_READ_ARRAY_LEGACY 0xe8 + +/* Send a single-byte command to the device and read the response */ +int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len); + +/* + * Send a multi-byte command to the device and read the response. Used + * for flash array reads, etc. + */ +int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len); + +/* + * Send a multi-byte command to the device followed by (optional) + * data. Used for programming the flash array, etc. + */ +int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, + const void *data, size_t data_len); + +/* + * Same as spi_flash_cmd_read() except it also claims/releases the SPI + * bus. Used as common part of the ->read() operation. + */ +int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len); + +/* Manufacturer-specific probe functions */ +struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode); diff --git a/roms/u-boot-sam460ex/drivers/mtd/spi/sst.c b/roms/u-boot-sam460ex/drivers/mtd/spi/sst.c new file mode 100644 index 000000000..50e929918 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/spi/sst.c @@ -0,0 +1,374 @@ +/* + * Driver for SST serial flashes + * + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Copyright 2008, Network Appliance Inc. + * Jason McMullan <mcmullan@netapp.com> + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + * Copyright (c) 2008-2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +#define CMD_SST_WREN 0x06 /* Write Enable */ +#define CMD_SST_WRDI 0x04 /* Write Disable */ +#define CMD_SST_RDSR 0x05 /* Read Status Register */ +#define CMD_SST_WRSR 0x01 /* Write Status Register */ +#define CMD_SST_READ 0x03 /* Read Data Bytes */ +#define CMD_SST_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ +#define CMD_SST_BP 0x02 /* Byte Program */ +#define CMD_SST_AAI_WP 0xAD /* Auto Address Increment Word Program */ +#define CMD_SST_SE 0x20 /* Sector Erase */ + +#define SST_SR_WIP (1 << 0) /* Write-in-Progress */ +#define SST_SR_WEL (1 << 1) /* Write enable */ +#define SST_SR_BP0 (1 << 2) /* Block Protection 0 */ +#define SST_SR_BP1 (1 << 3) /* Block Protection 1 */ +#define SST_SR_BP2 (1 << 4) /* Block Protection 2 */ +#define SST_SR_AAI (1 << 6) /* Addressing mode */ +#define SST_SR_BPL (1 << 7) /* BP bits lock */ + +struct sst_spi_flash_params { + u8 idcode1; + u16 nr_sectors; + const char *name; +}; + +struct sst_spi_flash { + struct spi_flash flash; + const struct sst_spi_flash_params *params; +}; + +static inline struct sst_spi_flash *to_sst_spi_flash(struct spi_flash *flash) +{ + return container_of(flash, struct sst_spi_flash, flash); +} + +#define SST_SECTOR_SIZE (4 * 1024) +static const struct sst_spi_flash_params sst_spi_flash_table[] = { + { + .idcode1 = 0x8d, + .nr_sectors = 128, + .name = "SST25VF040B", + },{ + .idcode1 = 0x8e, + .nr_sectors = 256, + .name = "SST25VF080B", + },{ + .idcode1 = 0x41, + .nr_sectors = 512, + .name = "SST25VF016B", + },{ + .idcode1 = 0x4a, + .nr_sectors = 1024, + .name = "SST25VF032B", + },{ + .idcode1 = 0x01, + .nr_sectors = 16, + .name = "SST25WF512", + },{ + .idcode1 = 0x02, + .nr_sectors = 32, + .name = "SST25WF010", + },{ + .idcode1 = 0x03, + .nr_sectors = 64, + .name = "SST25WF020", + },{ + .idcode1 = 0x04, + .nr_sectors = 128, + .name = "SST25WF040", + }, +}; + +static int +sst_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + int ret; + u8 byte = CMD_SST_RDSR; + + ret = spi_xfer(spi, sizeof(byte) * 8, &byte, NULL, SPI_XFER_BEGIN); + if (ret) { + debug("SF: Failed to send command %02x: %d\n", byte, ret); + return ret; + } + + timebase = get_timer(0); + do { + ret = spi_xfer(spi, sizeof(byte) * 8, NULL, &byte, 0); + if (ret) + break; + + if ((byte & SST_SR_WIP) == 0) + break; + + } while (get_timer(timebase) < timeout); + + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if (!ret && (byte & SST_SR_WIP) != 0) + ret = -1; + + if (ret) + debug("SF: sst wait for ready timed out\n"); + return ret; +} + +static int +sst_enable_writing(struct spi_flash *flash) +{ + int ret = spi_flash_cmd(flash->spi, CMD_SST_WREN, NULL, 0); + if (ret) + debug("SF: Enabling Write failed\n"); + return ret; +} + +static int +sst_disable_writing(struct spi_flash *flash) +{ + int ret = spi_flash_cmd(flash->spi, CMD_SST_WRDI, NULL, 0); + if (ret) + debug("SF: Disabling Write failed\n"); + return ret; +} + +static int +sst_read_fast(struct spi_flash *flash, u32 offset, size_t len, void *buf) +{ + u8 cmd[5] = { + CMD_READ_ARRAY_FAST, + offset >> 16, + offset >> 8, + offset, + 0x00, + }; + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int +sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) +{ + int ret; + u8 cmd[4] = { + CMD_SST_BP, + offset >> 16, + offset >> 8, + offset, + }; + + debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", + spi_w8r8(flash->spi, CMD_SST_RDSR), buf, cmd[0], offset); + + ret = sst_enable_writing(flash); + if (ret) + return ret; + + ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), buf, 1); + if (ret) + return ret; + + return sst_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); +} + +static int +sst_write(struct spi_flash *flash, u32 offset, size_t len, const void *buf) +{ + size_t actual, cmd_len; + int ret; + u8 cmd[4]; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + /* If the data is not word aligned, write out leading single byte */ + actual = offset % 2; + if (actual) { + ret = sst_byte_write(flash, offset, buf); + if (ret) + goto done; + } + offset += actual; + + ret = sst_enable_writing(flash); + if (ret) + goto done; + + cmd_len = 4; + cmd[0] = CMD_SST_AAI_WP; + cmd[1] = offset >> 16; + cmd[2] = offset >> 8; + cmd[3] = offset; + + for (; actual < len - 1; actual += 2) { + debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", + spi_w8r8(flash->spi, CMD_SST_RDSR), buf + actual, cmd[0], + offset); + + ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len, + buf + actual, 2); + if (ret) { + debug("SF: sst word program failed\n"); + break; + } + + ret = sst_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret) + break; + + cmd_len = 1; + offset += 2; + } + + if (!ret) + ret = sst_disable_writing(flash); + + /* If there is a single trailing byte, write it out */ + if (!ret && actual != len) + ret = sst_byte_write(flash, offset, buf + actual); + + done: + debug("SF: sst: program %s %zu bytes @ 0x%zx\n", + ret ? "failure" : "success", len, offset - actual); + + spi_release_bus(flash->spi); + return ret; +} + +int +sst_erase(struct spi_flash *flash, u32 offset, size_t len) +{ + unsigned long sector_size; + u32 start, end; + int ret; + u8 cmd[4]; + + /* + * This function currently uses sector erase only. + * Probably speed things up by using bulk erase + * when possible. + */ + + sector_size = SST_SECTOR_SIZE; + + if (offset % sector_size) { + debug("SF: Erase offset not multiple of sector size\n"); + return -1; + } + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + cmd[0] = CMD_SST_SE; + cmd[3] = 0; + start = offset; + end = start + len; + + ret = 0; + while (offset < end) { + cmd[1] = offset >> 16; + cmd[2] = offset >> 8; + offset += sector_size; + + debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], + cmd[2], cmd[3], offset); + + ret = sst_enable_writing(flash); + if (ret) + break; + + ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), NULL, 0); + if (ret) { + debug("SF: sst page erase failed\n"); + break; + } + + ret = sst_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret) + break; + } + + debug("SF: sst: Successfully erased %lu bytes @ 0x%x\n", + len * sector_size, start); + + spi_release_bus(flash->spi); + return ret; +} + +static int +sst_unlock(struct spi_flash *flash) +{ + int ret; + u8 cmd, status; + + ret = sst_enable_writing(flash); + if (ret) + return ret; + + cmd = CMD_SST_WRSR; + status = 0; + ret = spi_flash_cmd_write(flash->spi, &cmd, 1, &status, 1); + if (ret) + debug("SF: Unable to set status byte\n"); + + debug("SF: sst: status = %x\n", spi_w8r8(flash->spi, CMD_SST_RDSR)); + + return ret; +} + +struct spi_flash * +spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode) +{ + const struct sst_spi_flash_params *params; + struct sst_spi_flash *stm; + size_t i; + + for (i = 0; i < ARRAY_SIZE(sst_spi_flash_table); ++i) { + params = &sst_spi_flash_table[i]; + if (params->idcode1 == idcode[2]) + break; + } + + if (i == ARRAY_SIZE(sst_spi_flash_table)) { + debug("SF: Unsupported SST ID %02x\n", idcode[1]); + return NULL; + } + + stm = malloc(sizeof(*stm)); + if (!stm) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + stm->params = params; + stm->flash.spi = spi; + stm->flash.name = params->name; + + stm->flash.write = sst_write; + stm->flash.erase = sst_erase; + stm->flash.read = sst_read_fast; + stm->flash.size = SST_SECTOR_SIZE * params->nr_sectors; + + debug("SF: Detected %s with page size %u, total %u bytes\n", + params->name, SST_SECTOR_SIZE, stm->flash.size); + + /* Flash powers up read-only, so clear BP# bits */ + sst_unlock(&stm->flash); + + return &stm->flash; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/spi/stmicro.c b/roms/u-boot-sam460ex/drivers/mtd/spi/stmicro.c new file mode 100644 index 000000000..ae0d0471f --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/spi/stmicro.c @@ -0,0 +1,351 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright 2008, Network Appliance Inc. + * Jason McMullan <mcmullan@netapp.com> + * + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +/* M25Pxx-specific commands */ +#define CMD_M25PXX_WREN 0x06 /* Write Enable */ +#define CMD_M25PXX_WRDI 0x04 /* Write Disable */ +#define CMD_M25PXX_RDSR 0x05 /* Read Status Register */ +#define CMD_M25PXX_WRSR 0x01 /* Write Status Register */ +#define CMD_M25PXX_READ 0x03 /* Read Data Bytes */ +#define CMD_M25PXX_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ +#define CMD_M25PXX_PP 0x02 /* Page Program */ +#define CMD_M25PXX_SE 0xd8 /* Sector Erase */ +#define CMD_M25PXX_BE 0xc7 /* Bulk Erase */ +#define CMD_M25PXX_DP 0xb9 /* Deep Power-down */ +#define CMD_M25PXX_RES 0xab /* Release from DP, and Read Signature */ + +#define STM_ID_M25P16 0x15 +#define STM_ID_M25P20 0x12 +#define STM_ID_M25P32 0x16 +#define STM_ID_M25P40 0x13 +#define STM_ID_M25P64 0x17 +#define STM_ID_M25P80 0x14 +#define STM_ID_M25P128 0x18 + +#define STMICRO_SR_WIP (1 << 0) /* Write-in-Progress */ + +struct stmicro_spi_flash_params { + u8 idcode1; + u16 page_size; + u16 pages_per_sector; + u16 nr_sectors; + const char *name; +}; + +/* spi_flash needs to be first so upper layers can free() it */ +struct stmicro_spi_flash { + struct spi_flash flash; + const struct stmicro_spi_flash_params *params; +}; + +static inline struct stmicro_spi_flash *to_stmicro_spi_flash(struct spi_flash + *flash) +{ + return container_of(flash, struct stmicro_spi_flash, flash); +} + +static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = { + { + .idcode1 = STM_ID_M25P16, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 32, + .name = "M25P16", + }, + { + .idcode1 = STM_ID_M25P20, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 4, + .name = "M25P20", + }, + { + .idcode1 = STM_ID_M25P32, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 64, + .name = "M25P32", + }, + { + .idcode1 = STM_ID_M25P40, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 8, + .name = "M25P40", + }, + { + .idcode1 = STM_ID_M25P64, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 128, + .name = "M25P64", + }, + { + .idcode1 = STM_ID_M25P80, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 16, + .name = "M25P80", + }, + { + .idcode1 = STM_ID_M25P128, + .page_size = 256, + .pages_per_sector = 1024, + .nr_sectors = 64, + .name = "M25P128", + }, +}; + +static int stmicro_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + int ret; + u8 cmd = CMD_M25PXX_RDSR; + u8 status; + + ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); + if (ret) { + debug("SF: Failed to send command %02x: %d\n", cmd, ret); + return ret; + } + + timebase = get_timer(0); + do { + ret = spi_xfer(spi, 8, NULL, &status, 0); + if (ret) + return -1; + + if ((status & STMICRO_SR_WIP) == 0) + break; + + } while (get_timer(timebase) < timeout); + + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if ((status & STMICRO_SR_WIP) == 0) + return 0; + + /* Timed out */ + return -1; +} + +static int stmicro_read_fast(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash); + unsigned long page_addr; + unsigned long page_size; + u8 cmd[5]; + + page_size = stm->params->page_size; + page_addr = offset / page_size; + + cmd[0] = CMD_READ_ARRAY_FAST; + cmd[1] = page_addr >> 8; + cmd[2] = page_addr; + cmd[3] = offset % page_size; + cmd[4] = 0x00; + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int stmicro_write(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash); + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + size_t chunk_len; + size_t actual; + int ret; + u8 cmd[4]; + + page_size = stm->params->page_size; + page_addr = offset / page_size; + byte_addr = offset % page_size; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + ret = 0; + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - byte_addr); + + cmd[0] = CMD_M25PXX_PP; + cmd[1] = page_addr >> 8; + cmd[2] = page_addr; + cmd[3] = byte_addr; + + debug + ("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n", + buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); + + ret = spi_flash_cmd(flash->spi, CMD_M25PXX_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + break; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: STMicro Page Program failed\n"); + break; + } + + ret = stmicro_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: STMicro page programming timed out\n"); + break; + } + + page_addr++; + byte_addr = 0; + } + + debug("SF: STMicro: Successfully programmed %u bytes @ 0x%x\n", + len, offset); + + spi_release_bus(flash->spi); + return ret; +} + +int stmicro_erase(struct spi_flash *flash, u32 offset, size_t len) +{ + struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash); + unsigned long sector_size; + size_t actual; + int ret; + u8 cmd[4]; + + /* + * This function currently uses sector erase only. + * probably speed things up by using bulk erase + * when possible. + */ + + sector_size = stm->params->page_size * stm->params->pages_per_sector; + + if (offset % sector_size || len % sector_size) { + debug("SF: Erase offset/length not multiple of sector size\n"); + return -1; + } + + len /= sector_size; + cmd[0] = CMD_M25PXX_SE; + cmd[2] = 0x00; + cmd[3] = 0x00; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + ret = 0; + for (actual = 0; actual < len; actual++) { + cmd[1] = offset >> 16; + offset += sector_size; + + ret = spi_flash_cmd(flash->spi, CMD_M25PXX_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + break; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: STMicro page erase failed\n"); + break; + } + + ret = stmicro_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret < 0) { + debug("SF: STMicro page erase timed out\n"); + break; + } + } + + debug("SF: STMicro: Successfully erased %u bytes @ 0x%x\n", + len * sector_size, offset); + + spi_release_bus(flash->spi); + return ret; +} + +struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode) +{ + const struct stmicro_spi_flash_params *params; + struct stmicro_spi_flash *stm; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) { + params = &stmicro_spi_flash_table[i]; + if (params->idcode1 == idcode[2]) { + break; + } + } + + if (i == ARRAY_SIZE(stmicro_spi_flash_table)) { + debug("SF: Unsupported STMicro ID %02x\n", idcode[1]); + return NULL; + } + + stm = malloc(sizeof(struct stmicro_spi_flash)); + if (!stm) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + stm->params = params; + stm->flash.spi = spi; + stm->flash.name = params->name; + + stm->flash.write = stmicro_write; + stm->flash.erase = stmicro_erase; + stm->flash.read = stmicro_read_fast; + stm->flash.size = params->page_size * params->pages_per_sector + * params->nr_sectors; + + debug("SF: Detected %s with page size %u, total %u bytes\n", + params->name, params->page_size, stm->flash.size); + + return &stm->flash; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/spi/winbond.c b/roms/u-boot-sam460ex/drivers/mtd/spi/winbond.c new file mode 100644 index 000000000..b8da92319 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/spi/winbond.c @@ -0,0 +1,332 @@ +/* + * Copyright 2008, Network Appliance Inc. + * Author: Jason McMullan <mcmullan <at> netapp.com> + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +/* M25Pxx-specific commands */ +#define CMD_W25_WREN 0x06 /* Write Enable */ +#define CMD_W25_WRDI 0x04 /* Write Disable */ +#define CMD_W25_RDSR 0x05 /* Read Status Register */ +#define CMD_W25_WRSR 0x01 /* Write Status Register */ +#define CMD_W25_READ 0x03 /* Read Data Bytes */ +#define CMD_W25_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ +#define CMD_W25_PP 0x02 /* Page Program */ +#define CMD_W25_SE 0x20 /* Sector (4K) Erase */ +#define CMD_W25_BE 0xd8 /* Block (64K) Erase */ +#define CMD_W25_CE 0xc7 /* Chip Erase */ +#define CMD_W25_DP 0xb9 /* Deep Power-down */ +#define CMD_W25_RES 0xab /* Release from DP, and Read Signature */ + +#define WINBOND_ID_W25X16 0x3015 +#define WINBOND_ID_W25X32 0x3016 +#define WINBOND_ID_W25X64 0x3017 + +#define WINBOND_SR_WIP (1 << 0) /* Write-in-Progress */ + +struct winbond_spi_flash_params { + uint16_t id; + /* Log2 of page size in power-of-two mode */ + uint8_t l2_page_size; + uint16_t pages_per_sector; + uint16_t sectors_per_block; + uint8_t nr_blocks; + const char *name; +}; + +/* spi_flash needs to be first so upper layers can free() it */ +struct winbond_spi_flash { + struct spi_flash flash; + const struct winbond_spi_flash_params *params; +}; + +static inline struct winbond_spi_flash * +to_winbond_spi_flash(struct spi_flash *flash) +{ + return container_of(flash, struct winbond_spi_flash, flash); +} + +static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { + { + .id = WINBOND_ID_W25X16, + .l2_page_size = 8, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 32, + .name = "W25X16", + }, + { + .id = WINBOND_ID_W25X32, + .l2_page_size = 8, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 64, + .name = "W25X32", + }, + { + .id = WINBOND_ID_W25X64, + .l2_page_size = 8, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 128, + .name = "W25X64", + }, +}; + +static int winbond_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + int ret; + u8 status; + u8 cmd[4] = { CMD_W25_RDSR, 0xff, 0xff, 0xff }; + + ret = spi_xfer(spi, 32, &cmd[0], NULL, SPI_XFER_BEGIN); + if (ret) { + debug("SF: Failed to send command %02x: %d\n", cmd, ret); + return ret; + } + + timebase = get_timer(0); + do { + ret = spi_xfer(spi, 8, NULL, &status, 0); + if (ret) { + debug("SF: Failed to get status for cmd %02x: %d\n", cmd, ret); + return -1; + } + + if ((status & WINBOND_SR_WIP) == 0) + break; + + } while (get_timer(timebase) < timeout); + + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if ((status & WINBOND_SR_WIP) == 0) + return 0; + + debug("SF: Timed out on command %02x: %d\n", cmd, ret); + /* Timed out */ + return -1; +} + +/* + * Assemble the address part of a command for Winbond devices in + * non-power-of-two page size mode. + */ +static void winbond_build_address(struct winbond_spi_flash *stm, u8 *cmd, u32 offset) +{ + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + unsigned int page_shift; + + /* + * The "extra" space per page is the power-of-two page size + * divided by 32. + */ + page_shift = stm->params->l2_page_size; + page_size = (1 << page_shift); + page_addr = offset / page_size; + byte_addr = offset % page_size; + + cmd[0] = page_addr >> (16 - page_shift); + cmd[1] = page_addr << (page_shift - 8) | (byte_addr >> 8); + cmd[2] = byte_addr; +} + +static int winbond_read_fast(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + struct winbond_spi_flash *stm = to_winbond_spi_flash(flash); + u8 cmd[5]; + + cmd[0] = CMD_READ_ARRAY_FAST; + winbond_build_address(stm, cmd + 1, offset); + cmd[4] = 0x00; + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int winbond_write(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + struct winbond_spi_flash *stm = to_winbond_spi_flash(flash); + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + unsigned int page_shift; + size_t chunk_len; + size_t actual; + int ret; + u8 cmd[4]; + + page_shift = stm->params->l2_page_size; + page_size = (1 << page_shift); + page_addr = offset / page_size; + byte_addr = offset % page_size; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - byte_addr); + + cmd[0] = CMD_W25_PP; + cmd[1] = page_addr >> (16 - page_shift); + cmd[2] = page_addr << (page_shift - 8) | (byte_addr >> 8); + cmd[3] = byte_addr; + debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n", + buf + actual, + cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); + + ret = spi_flash_cmd(flash->spi, CMD_W25_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + goto out; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: Winbond Page Program failed\n"); + goto out; + } + + ret = winbond_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: Winbond page programming timed out\n"); + goto out; + } + + page_addr++; + byte_addr = 0; + } + + debug("SF: Winbond: Successfully programmed %u bytes @ 0x%x\n", + len, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + +int winbond_erase(struct spi_flash *flash, u32 offset, size_t len) +{ + struct winbond_spi_flash *stm = to_winbond_spi_flash(flash); + unsigned long sector_size; + unsigned int page_shift; + size_t actual; + int ret; + u8 cmd[4]; + + /* + * This function currently uses sector erase only. + * probably speed things up by using bulk erase + * when possible. + */ + + page_shift = stm->params->l2_page_size; + sector_size = (1 << page_shift) * stm->params->pages_per_sector; + + if (offset % sector_size || len % sector_size) { + debug("SF: Erase offset/length not multiple of sector size\n"); + return -1; + } + + len /= sector_size; + cmd[0] = CMD_W25_SE; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual++) { + winbond_build_address(stm, &cmd[1], offset + actual * sector_size); + printf("Erase: %02x %02x %02x %02x\n", + cmd[0], cmd[1], cmd[2], cmd[3]); + + ret = spi_flash_cmd(flash->spi, CMD_W25_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + goto out; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: Winbond sector erase failed\n"); + goto out; + } + + ret = winbond_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret < 0) { + debug("SF: Winbond sector erase timed out\n"); + goto out; + } + } + + debug("SF: Winbond: Successfully erased %u bytes @ 0x%x\n", + len * sector_size, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + +struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode) +{ + const struct winbond_spi_flash_params *params; + unsigned long page_size; + struct winbond_spi_flash *stm; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(winbond_spi_flash_table); i++) { + params = &winbond_spi_flash_table[i]; + if (params->id == ((idcode[1] << 8) | idcode[2])) + break; + } + + if (i == ARRAY_SIZE(winbond_spi_flash_table)) { + debug("SF: Unsupported Winbond ID %02x%02x\n", + idcode[1], idcode[2]); + return NULL; + } + + stm = malloc(sizeof(struct winbond_spi_flash)); + if (!stm) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + stm->params = params; + stm->flash.spi = spi; + stm->flash.name = params->name; + + /* Assuming power-of-two page size initially. */ + page_size = 1 << params->l2_page_size; + + stm->flash.write = winbond_write; + stm->flash.erase = winbond_erase; + stm->flash.read = winbond_read_fast; + stm->flash.size = page_size * params->pages_per_sector + * params->sectors_per_block + * params->nr_blocks; + + debug("SF: Detected %s with page size %u, total %u bytes\n", + params->name, page_size, stm->flash.size); + + return &stm->flash; +} |