aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot-sam460ex/drivers/mtd/spi
diff options
context:
space:
mode:
Diffstat (limited to 'roms/u-boot-sam460ex/drivers/mtd/spi')
-rw-r--r--roms/u-boot-sam460ex/drivers/mtd/spi/Makefile53
-rw-r--r--roms/u-boot-sam460ex/drivers/mtd/spi/atmel.c551
-rw-r--r--roms/u-boot-sam460ex/drivers/mtd/spi/eeprom_m95xxx.c122
-rw-r--r--roms/u-boot-sam460ex/drivers/mtd/spi/macronix.c337
-rw-r--r--roms/u-boot-sam460ex/drivers/mtd/spi/spansion.c350
-rw-r--r--roms/u-boot-sam460ex/drivers/mtd/spi/spi_flash.c183
-rw-r--r--roms/u-boot-sam460ex/drivers/mtd/spi/spi_flash_internal.h52
-rw-r--r--roms/u-boot-sam460ex/drivers/mtd/spi/sst.c374
-rw-r--r--roms/u-boot-sam460ex/drivers/mtd/spi/stmicro.c351
-rw-r--r--roms/u-boot-sam460ex/drivers/mtd/spi/winbond.c332
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 = &macronix_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;
+}