#include "spi_flash_internal.h"
-static void spi_flash_addr(u32 addr, u8 *cmd)
+ DECLARE_GLOBAL_DATA_PTR;
+
+static void spi_flash_addr(struct spi_flash *flash,
+ unsigned long page_addr, unsigned long byte_addr, u8 *cmd)
{
/* cmd[0] is actual command */
- cmd[1] = addr >> 16;
- cmd[2] = addr >> 8;
- cmd[3] = addr >> 0;
+ if (flash->addr_width == 4) {
+ cmd[1] = page_addr >> 16;
+ cmd[2] = page_addr >> 8;
+ cmd[3] = page_addr;
+ cmd[4] = byte_addr;
+ } else {
+ cmd[1] = page_addr >> 8;
+ cmd[2] = page_addr;
+ cmd[3] = byte_addr;
+ }
+}
+
+static int spi_flash_check_bankaddr_access(struct spi_flash *flash, u32 *offset)
+{
+ int ret;
+
+ if (*offset >= 0x1000000) {
+ ret = spi_flash_bankaddr_access(flash, STATUS_BANKADDR_ENABLE);
+ if (ret) {
+ debug("SF: fail to %s bank addr bit\n",
+ STATUS_BANKADDR_ENABLE ? "set" : "reset");
+ return ret;
+ }
+ *offset -= 0x1000000;
+ } else {
+ ret = spi_flash_bankaddr_access(flash, STATUS_BANKADDR_DISABLE);
+ if (ret) {
+ debug("SF: fail to %s bank addr bit\n",
+ STATUS_BANKADDR_DISABLE ? "set" : "reset");
+ return ret;
+ }
+ }
+
+ return ret;
}
static int spi_flash_read_write(struct spi_slave *spi,
for (actual = 0; actual < len; actual += chunk_len) {
chunk_len = min(len - actual, page_size - byte_addr);
- cmd[1] = page_addr >> 8;
- cmd[2] = page_addr;
- cmd[3] = byte_addr;
+ if (flash->spi->max_write_size)
+ chunk_len = min(chunk_len, flash->spi->max_write_size);
+
+ spi_flash_addr(flash, page_addr, byte_addr, cmd);
- debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n",
- buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
+ debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x0x%02x } \
+ chunk_len = %zu\n", buf + actual, cmd[0], cmd[1],
+ cmd[2], cmd[3], cmd[4], chunk_len);
ret = spi_flash_cmd_write_enable(flash);
if (ret < 0) {
if (ret)
break;
- page_addr++;
- byte_addr = 0;
+ byte_addr += chunk_len;
+ if (byte_addr == page_size) {
+ page_addr++;
+ byte_addr = 0;
+ }
}
- debug("SF: program %s %zu bytes @ %#x\n",
+ printf("SF: program %s %zu bytes @ %#x\n",
ret ? "failure" : "success", len, offset);
spi_release_bus(flash->spi);
int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
size_t len, void *data)
{
- u8 cmd[5];
+ unsigned long page_addr;
+ unsigned long page_size;
+ unsigned long byte_addr;
+ u8 cmd[flash->addr_width+2];
+ int ret;
+
+ if ((flash->size > 0x1000000) && (flash->addr_width == 3)) {
+ ret = spi_flash_check_bankaddr_access(flash, &offset);
+ if (ret) {
+ debug("SF: fail to acess bank_addr\n");
+ return ret;
+ }
+ }
+
+ page_size = flash->page_size;
+ page_addr = offset / page_size;
+ byte_addr = offset % page_size;
+ /* Handle memory-mapped SPI */
+ if (flash->memory_map)
+ memcpy(data, flash->memory_map + offset, len);
+
cmd[0] = CMD_READ_ARRAY_FAST;
- spi_flash_addr(offset, cmd);
- cmd[4] = 0x00;
+ spi_flash_addr(flash, page_addr, byte_addr, cmd);
+ cmd[sizeof(cmd)-1] = 0x00;
return spi_flash_read_common(flash, cmd, sizeof(cmd), data, len);
}
return 0;
}
+int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 ear)
+{
+ u8 cmd;
+ u8 idcode0;
+ int ret;
+
+ ret = spi_flash_cmd(flash->spi, CMD_READ_ID, &idcode0, 1);
+ if (ret) {
+ debug("SF: fail to read read id\n");
+ return ret;
+ }
+
+ if (idcode0 == 0x01) {
+ cmd = CMD_BANKADDR_BRWR;
+ } else if ((idcode0 == 0xef) || (idcode0 == 0x20)) {
+ cmd = CMD_EXT_WREAR;
+ } else {
+ printf("SF: unable to support extended addr reg write"
+ " for %s flash\n", flash->name);
+ return -1;
+ }
+
+ ret = spi_flash_cmd_write_enable(flash);
+ if (ret < 0) {
+ debug("SF: enabling write failed\n");
+ return ret;
+ }
+
+ ret = spi_flash_cmd_write(flash->spi, &cmd, 1, &ear, 1);
+ if (ret) {
+ debug("SF: fail to write bank addr register\n");
+ return ret;
+ }
+
+ ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
+ if (ret < 0) {
+ debug("SF: write config register timed out\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int spi_flash_cmd_bankaddr_read(struct spi_flash *flash, void *data)
+{
+ u8 cmd;
+ u8 idcode0;
+ int ret;
+
+ ret = spi_flash_cmd(flash->spi, CMD_READ_ID, &idcode0, 1);
+ if (ret) {
+ debug("SF: fail to read read id\n");
+ return ret;
+ }
+
+ if (idcode0 == 0x01) {
+ cmd = CMD_BANKADDR_BRRD;
+ } else if ((idcode0 == 0xef) || (idcode0 == 0x20)) {
+ cmd = CMD_EXT_RDEAR;
+ } else {
+ printf("SF: unable to support extended addr reg read"
+ " for %s flash\n", flash->name);
+ return -1;
+ }
+
+ return spi_flash_read_common(flash, &cmd, 1, data, 1);
+}
+
+int spi_flash_bankaddr_access(struct spi_flash *flash, u8 status)
+{
+ int ret, pass;
+ u8 data = 0, write_done = 0;
+
+ for (pass = 0; pass < 2; pass++) {
+ ret = spi_flash_cmd_bankaddr_read(flash, (void *)&data);
+ if (ret < 0) {
+ debug("SF: fail to read bank addr register\n");
+ return ret;
+ }
+
+ if ((data != status) & !write_done) {
+ debug("SF: need to %s bank addr bit\n",
+ status ? "set" : "reset");
+
+ write_done = 1;
+ ret = spi_flash_cmd_bankaddr_write(flash, status);
+ if (ret < 0) {
+ debug("SF: fail to write bank addr bit\n");
+ return ret;
+ }
+ } else {
+ debug("SF: bank addr bit is %s.\n",
+ status ? "set" : "reset");
+ return ret;
+ }
+ }
+
+ return -1;
+}
+
+ #ifdef CONFIG_OF_CONTROL
+ int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash)
+ {
+ fdt_addr_t addr;
+ fdt_size_t size;
+ int node;
+
+ /* If there is no node, do nothing */
+ node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH);
+ if (node < 0)
+ return 0;
+
+ addr = fdtdec_get_addr_size(blob, node, "memory-map", &size);
+ if (addr == FDT_ADDR_T_NONE) {
+ debug("%s: Cannot decode address\n", __func__);
+ return 0;
+ }
+
+ if (flash->size != size) {
+ debug("%s: Memory map must cover entire device\n", __func__);
+ return -1;
+ }
+ flash->memory_map = (void *)addr;
+
+ return 0;
+ }
+ #endif /* CONFIG_OF_CONTROL */
+
/*
* The following table holds all device probe functions
*