summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@ti.com>2013-03-20 14:55:10 -0400
committerTom Rini <trini@ti.com>2013-03-20 14:55:10 -0400
commit8b906a9f0b3fd0d4421e08c4fa62f61a01289611 (patch)
tree049acc34fee9692c0c394b8d1697d97257c0a074 /drivers
parent3c47f2f4871c345c20b9d986b11fec550ef6cc9f (diff)
parent1e7133e99e27c4f02998e7eb87fa43424d48152b (diff)
Merge branch 'spi' of git://git.denx.de/u-boot-x86
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/spi/atmel.c8
-rw-r--r--drivers/mtd/spi/eon.c8
-rw-r--r--drivers/mtd/spi/macronix.c8
-rw-r--r--drivers/mtd/spi/ramtron.c4
-rw-r--r--drivers/mtd/spi/spansion.c8
-rw-r--r--drivers/mtd/spi/spi_flash.c81
-rw-r--r--drivers/mtd/spi/sst.c8
-rw-r--r--drivers/mtd/spi/stmicro.c8
-rw-r--r--drivers/mtd/spi/winbond.c8
-rw-r--r--drivers/spi/Makefile4
-rw-r--r--drivers/spi/altera_spi.c4
-rw-r--r--drivers/spi/andes_spi.c4
-rw-r--r--drivers/spi/armada100_spi.c4
-rw-r--r--drivers/spi/atmel_spi.c4
-rw-r--r--drivers/spi/bfin_spi.c4
-rw-r--r--drivers/spi/bfin_spi6xx.c4
-rw-r--r--drivers/spi/cf_qspi.c4
-rw-r--r--drivers/spi/cf_spi.c4
-rw-r--r--drivers/spi/davinci_spi.c4
-rw-r--r--drivers/spi/exynos_spi.c4
-rw-r--r--drivers/spi/fsl_espi.c4
-rw-r--r--drivers/spi/ich.c754
-rw-r--r--drivers/spi/ich.h143
-rw-r--r--drivers/spi/kirkwood_spi.c5
-rw-r--r--drivers/spi/mpc52xx_spi.c5
-rw-r--r--drivers/spi/mpc8xxx_spi.c5
-rw-r--r--drivers/spi/mxc_spi.c4
-rw-r--r--drivers/spi/mxs_spi.c4
-rw-r--r--drivers/spi/oc_tiny_spi.c5
-rw-r--r--drivers/spi/omap3_spi.c27
-rw-r--r--drivers/spi/sh_spi.c4
-rw-r--r--drivers/spi/soft_spi.c4
-rw-r--r--drivers/spi/spi.c39
-rw-r--r--drivers/spi/tegra_slink.c4
-rw-r--r--drivers/spi/tegra_spi.c4
-rw-r--r--drivers/spi/xilinx_spi.c4
36 files changed, 1062 insertions, 138 deletions
diff --git a/drivers/mtd/spi/atmel.c b/drivers/mtd/spi/atmel.c
index 006f6d5d04..6a92c4b774 100644
--- a/drivers/mtd/spi/atmel.c
+++ b/drivers/mtd/spi/atmel.c
@@ -480,15 +480,13 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode)
return NULL;
}
- asf = malloc(sizeof(struct atmel_spi_flash));
+ asf = spi_flash_alloc(struct atmel_spi_flash, spi, params->name);
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;
@@ -513,7 +511,6 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode)
asf->flash.erase = dataflash_erase_at45;
page_size += 1 << (params->l2_page_size - 5);
} else {
- asf->flash.read = spi_flash_cmd_read_fast;
asf->flash.write = dataflash_write_p2;
asf->flash.erase = dataflash_erase_p2;
}
@@ -524,9 +521,6 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode)
case DF_FAMILY_AT26F:
case DF_FAMILY_AT26DF:
- asf->flash.read = spi_flash_cmd_read_fast;
- asf->flash.write = spi_flash_cmd_write_multi;
- asf->flash.erase = spi_flash_cmd_erase;
asf->flash.page_size = page_size;
asf->flash.sector_size = 4096;
/* clear SPRL# bit for locked flash */
diff --git a/drivers/mtd/spi/eon.c b/drivers/mtd/spi/eon.c
index 691ed4efc4..b16e7ab098 100644
--- a/drivers/mtd/spi/eon.c
+++ b/drivers/mtd/spi/eon.c
@@ -46,18 +46,12 @@ struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode)
return NULL;
}
- flash = malloc(sizeof(*flash));
+ flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
- flash->spi = spi;
- flash->name = params->name;
-
- flash->write = spi_flash_cmd_write_multi;
- flash->erase = spi_flash_cmd_erase;
- flash->read = spi_flash_cmd_read_fast;
flash->page_size = 256;
flash->sector_size = 256 * 16 * 16;
flash->size = 256 * 16
diff --git a/drivers/mtd/spi/macronix.c b/drivers/mtd/spi/macronix.c
index c97a39d499..036c30d3be 100644
--- a/drivers/mtd/spi/macronix.c
+++ b/drivers/mtd/spi/macronix.c
@@ -97,18 +97,12 @@ struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode)
return NULL;
}
- flash = malloc(sizeof(*flash));
+ flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
- flash->spi = spi;
- flash->name = params->name;
-
- flash->write = spi_flash_cmd_write_multi;
- flash->erase = spi_flash_cmd_erase;
- flash->read = spi_flash_cmd_read_fast;
flash->page_size = 256;
flash->sector_size = 256 * 16 * 16;
flash->size = flash->sector_size * params->nr_blocks;
diff --git a/drivers/mtd/spi/ramtron.c b/drivers/mtd/spi/ramtron.c
index 0999781496..5299a6dbde 100644
--- a/drivers/mtd/spi/ramtron.c
+++ b/drivers/mtd/spi/ramtron.c
@@ -284,15 +284,13 @@ struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode)
return NULL;
found:
- sn = malloc(sizeof(*sn));
+ sn = spi_flash_alloc(struct ramtron_spi_fram, spi, params->name);
if (!sn) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
sn->params = params;
- sn->flash.spi = spi;
- sn->flash.name = params->name;
sn->flash.write = ramtron_write;
sn->flash.read = ramtron_read;
diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c
index 9288672c84..bc558c4c96 100644
--- a/drivers/mtd/spi/spansion.c
+++ b/drivers/mtd/spi/spansion.c
@@ -128,18 +128,12 @@ struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode)
return NULL;
}
- flash = malloc(sizeof(*flash));
+ flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
- flash->spi = spi;
- flash->name = params->name;
-
- flash->write = spi_flash_cmd_write_multi;
- flash->erase = spi_flash_cmd_erase;
- flash->read = spi_flash_cmd_read_fast;
flash->page_size = 256;
flash->sector_size = 256 * params->pages_per_sector;
flash->size = flash->sector_size * params->nr_sectors;
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index 00aece9291..111185af17 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -8,6 +8,7 @@
*/
#include <common.h>
+#include <fdtdec.h>
#include <malloc.h>
#include <spi.h>
#include <spi_flash.h>
@@ -15,6 +16,8 @@
#include "spi_flash_internal.h"
+DECLARE_GLOBAL_DATA_PTR;
+
static void spi_flash_addr(u32 addr, u8 *cmd)
{
/* cmd[0] is actual command */
@@ -87,6 +90,9 @@ int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,
for (actual = 0; actual < len; actual += chunk_len) {
chunk_len = min(len - actual, page_size - byte_addr);
+ if (flash->spi->max_write_size)
+ chunk_len = min(chunk_len, flash->spi->max_write_size);
+
cmd[1] = page_addr >> 8;
cmd[2] = page_addr;
cmd[3] = byte_addr;
@@ -111,8 +117,11 @@ int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,
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",
@@ -140,6 +149,10 @@ int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
{
u8 cmd[5];
+ /* 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;
@@ -269,6 +282,34 @@ int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr)
return 0;
}
+#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
*
@@ -385,9 +426,18 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
goto err_manufacturer_probe;
}
+#ifdef CONFIG_OF_CONTROL
+ if (spi_flash_decode_fdt(gd->fdt_blob, flash)) {
+ debug("SF: FDT decode error\n");
+ goto err_manufacturer_probe;
+ }
+#endif
printf("SF: Detected %s with page size ", flash->name);
print_size(flash->sector_size, ", total ");
- print_size(flash->size, "\n");
+ print_size(flash->size, "");
+ if (flash->memory_map)
+ printf(", mapped at %p", flash->memory_map);
+ puts("\n");
spi_release_bus(spi);
@@ -401,6 +451,31 @@ err_claim_bus:
return NULL;
}
+void *spi_flash_do_alloc(int offset, int size, struct spi_slave *spi,
+ const char *name)
+{
+ struct spi_flash *flash;
+ void *ptr;
+
+ ptr = malloc(size);
+ if (!ptr) {
+ debug("SF: Failed to allocate memory\n");
+ return NULL;
+ }
+ memset(ptr, '\0', size);
+ flash = (struct spi_flash *)(ptr + offset);
+
+ /* Set up some basic fields - caller will sort out sizes */
+ flash->spi = spi;
+ flash->name = name;
+
+ flash->read = spi_flash_cmd_read_fast;
+ flash->write = spi_flash_cmd_write_multi;
+ flash->erase = spi_flash_cmd_erase;
+
+ return flash;
+}
+
void spi_flash_free(struct spi_flash *flash)
{
spi_free_slave(flash->spi);
diff --git a/drivers/mtd/spi/sst.c b/drivers/mtd/spi/sst.c
index ced4f2473f..95f5490c35 100644
--- a/drivers/mtd/spi/sst.c
+++ b/drivers/mtd/spi/sst.c
@@ -203,22 +203,16 @@ spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode)
return NULL;
}
- stm = malloc(sizeof(*stm));
+ stm = spi_flash_alloc(struct sst_spi_flash, spi, params->name);
if (!stm) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
stm->params = params;
- stm->flash.spi = spi;
- stm->flash.name = params->name;
if (stm->params->flags & SST_FEAT_WP)
stm->flash.write = sst_write_wp;
- else
- stm->flash.write = spi_flash_cmd_write_multi;
- stm->flash.erase = spi_flash_cmd_erase;
- stm->flash.read = spi_flash_cmd_read_fast;
stm->flash.page_size = 256;
stm->flash.sector_size = 4096;
stm->flash.size = stm->flash.sector_size * params->nr_sectors;
diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c
index 8a193449d0..2a9972bd4e 100644
--- a/drivers/mtd/spi/stmicro.c
+++ b/drivers/mtd/spi/stmicro.c
@@ -176,18 +176,12 @@ struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)
return NULL;
}
- flash = malloc(sizeof(*flash));
+ flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
- flash->spi = spi;
- flash->name = params->name;
-
- flash->write = spi_flash_cmd_write_multi;
- flash->erase = spi_flash_cmd_erase;
- flash->read = spi_flash_cmd_read_fast;
flash->page_size = 256;
flash->sector_size = 256 * params->pages_per_sector;
flash->size = flash->sector_size * params->nr_sectors;
diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c
index 4418302169..05dc644926 100644
--- a/drivers/mtd/spi/winbond.c
+++ b/drivers/mtd/spi/winbond.c
@@ -92,18 +92,12 @@ struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode)
return NULL;
}
- flash = malloc(sizeof(*flash));
+ flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
- flash->spi = spi;
- flash->name = params->name;
-
- flash->write = spi_flash_cmd_write_multi;
- flash->erase = spi_flash_cmd_erase;
- flash->read = spi_flash_cmd_read_fast;
flash->page_size = 256;
flash->sector_size = 4096;
flash->size = 4096 * 16 * params->nr_blocks;
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index b8264df3a9..42685955dc 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -25,6 +25,9 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libspi.o
+# There are many options which enable SPI, so make this library available
+COBJS-y += spi.o
+
COBJS-$(CONFIG_ALTERA_SPI) += altera_spi.o
COBJS-$(CONFIG_ANDES_SPI) += andes_spi.o
COBJS-$(CONFIG_ARMADA100_SPI) += armada100_spi.o
@@ -36,6 +39,7 @@ COBJS-$(CONFIG_CF_SPI) += cf_spi.o
COBJS-$(CONFIG_CF_QSPI) += cf_qspi.o
COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
COBJS-$(CONFIG_EXYNOS_SPI) += exynos_spi.o
+COBJS-$(CONFIG_ICH_SPI) += ich.o
COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
diff --git a/drivers/spi/altera_spi.c b/drivers/spi/altera_spi.c
index 138d6f4b45..b53607a4ec 100644
--- a/drivers/spi/altera_spi.c
+++ b/drivers/spi/altera_spi.c
@@ -83,12 +83,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (!spi_cs_is_valid(bus, cs))
return NULL;
- altspi = malloc(sizeof(*altspi));
+ altspi = spi_alloc_slave(struct altera_spi_slave, bus, cs);
if (!altspi)
return NULL;
- altspi->slave.bus = bus;
- altspi->slave.cs = cs;
altspi->base = altera_spi_base_list[bus];
debug("%s: bus:%i cs:%i base:%lx\n", __func__,
bus, cs, altspi->base);
diff --git a/drivers/spi/andes_spi.c b/drivers/spi/andes_spi.c
index fdde13954b..c56377b635 100644
--- a/drivers/spi/andes_spi.c
+++ b/drivers/spi/andes_spi.c
@@ -53,12 +53,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (!spi_cs_is_valid(bus, cs))
return NULL;
- ds = malloc(sizeof(*ds));
+ ds = spi_alloc_slave(struct andes_spi_slave, bus, cs);
if (!ds)
return NULL;
- ds->slave.bus = bus;
- ds->slave.cs = cs;
ds->regs = (struct andes_spi_regs *)CONFIG_SYS_SPI_BASE;
/*
diff --git a/drivers/spi/armada100_spi.c b/drivers/spi/armada100_spi.c
index 7384c9cd2c..afdbe0508c 100644
--- a/drivers/spi/armada100_spi.c
+++ b/drivers/spi/armada100_spi.c
@@ -120,12 +120,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
{
struct armd_spi_slave *pss;
- pss = malloc(sizeof(*pss));
+ pss = spi_alloc_slave(struct armd_spi_slave, bus, cs);
if (!pss)
return NULL;
- pss->slave.bus = bus;
- pss->slave.cs = cs;
pss->spi_reg = (struct ssp_reg *)SSP_REG_BASE(CONFIG_SYS_SSP_PORT);
pss->cr0 = SSCR0_MOTO | SSCR0_DATASIZE(DEFAULT_WORD_LEN) | SSCR0_SSE;
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index ce7d460855..f4b1bad22e 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -84,12 +84,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (mode & SPI_CPOL)
csrx |= ATMEL_SPI_CSRx_CPOL;
- as = malloc(sizeof(struct atmel_spi_slave));
+ as = spi_alloc_slave(struct atmel_spi_slave, bus, cs);
if (!as)
return NULL;
- as->slave.bus = bus;
- as->slave.cs = cs;
as->regs = regs;
as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS
#if defined(CONFIG_AT91SAM9X5) || defined(CONFIG_AT91SAM9M10G45)
diff --git a/drivers/spi/bfin_spi.c b/drivers/spi/bfin_spi.c
index e080bec705..ab2e8b998b 100644
--- a/drivers/spi/bfin_spi.c
+++ b/drivers/spi/bfin_spi.c
@@ -182,12 +182,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
default: return NULL;
}
- bss = malloc(sizeof(*bss));
+ bss = spi_alloc_slave(struct bfin_spi_slave, bus, cs);
if (!bss)
return NULL;
- bss->slave.bus = bus;
- bss->slave.cs = cs;
bss->mmr_base = (void *)mmr_base;
bss->ctl = SPE | MSTR | TDBR_CORE;
if (mode & SPI_CPHA) bss->ctl |= CPHA;
diff --git a/drivers/spi/bfin_spi6xx.c b/drivers/spi/bfin_spi6xx.c
index fde3447426..c25c4a9aea 100644
--- a/drivers/spi/bfin_spi6xx.c
+++ b/drivers/spi/bfin_spi6xx.c
@@ -178,12 +178,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
return NULL;
}
- bss = malloc(sizeof(*bss));
+ bss = spi_alloc_slave(struct bfin_spi_slave, bus, cs);
if (!bss)
return NULL;
- bss->slave.bus = bus;
- bss->slave.cs = cs;
bss->regs = (struct bfin_spi_regs *)reg_base;
bss->control = SPI_CTL_EN | SPI_CTL_MSTR;
if (mode & SPI_CPHA)
diff --git a/drivers/spi/cf_qspi.c b/drivers/spi/cf_qspi.c
index 72dd1a520d..a37ac4e526 100644
--- a/drivers/spi/cf_qspi.c
+++ b/drivers/spi/cf_qspi.c
@@ -120,13 +120,11 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (!spi_cs_is_valid(bus, cs))
return NULL;
- dev = malloc(sizeof(struct cf_qspi_slave));
+ dev = spi_alloc_slave(struct cf_qspi_slave, bus, cs);
if (!dev)
return NULL;
/* Initialize to known value */
- dev->slave.bus = bus;
- dev->slave.cs = cs;
dev->regs = (qspi_t *)MMAP_QSPI;
dev->qmr = 0;
dev->qwr = 0;
diff --git a/drivers/spi/cf_spi.c b/drivers/spi/cf_spi.c
index a883da9368..afe791737c 100644
--- a/drivers/spi/cf_spi.c
+++ b/drivers/spi/cf_spi.c
@@ -330,12 +330,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (!spi_cs_is_valid(bus, cs))
return NULL;
- cfslave = malloc(sizeof(struct cf_spi_slave));
+ cfslave = spi_alloc_slave(struct cf_spi_slave, bus, cs);
if (!cfslave)
return NULL;
- cfslave->slave.bus = bus;
- cfslave->slave.cs = cs;
cfslave->baudrate = max_hz;
/* specific setup */
diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c
index 13aca52c7e..74792af035 100644
--- a/drivers/spi/davinci_spi.c
+++ b/drivers/spi/davinci_spi.c
@@ -44,12 +44,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (!spi_cs_is_valid(bus, cs))
return NULL;
- ds = malloc(sizeof(*ds));
+ ds = spi_alloc_slave(struct davinci_spi_slave, bus, cs);
if (!ds)
return NULL;
- ds->slave.bus = bus;
- ds->slave.cs = cs;
ds->regs = (struct davinci_spi_regs *)CONFIG_SYS_SPI_BASE;
ds->freq = max_hz;
diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c
index be60ada2ba..51b3d30538 100644
--- a/drivers/spi/exynos_spi.c
+++ b/drivers/spi/exynos_spi.c
@@ -89,15 +89,13 @@ struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs,
return NULL;
}
- spi_slave = malloc(sizeof(*spi_slave));
+ spi_slave = spi_alloc_slave(struct exynos_spi_slave, busnum, cs);
if (!spi_slave) {
debug("%s: Could not allocate spi_slave\n", __func__);
return NULL;
}
bus = &spi_bus[busnum];
- spi_slave->slave.bus = busnum;
- spi_slave->slave.cs = cs;
spi_slave->regs = bus->regs;
spi_slave->mode = mode;
spi_slave->periph_id = bus->periph_id;
diff --git a/drivers/spi/fsl_espi.c b/drivers/spi/fsl_espi.c
index eb99e90bec..28609eefeb 100644
--- a/drivers/spi/fsl_espi.c
+++ b/drivers/spi/fsl_espi.c
@@ -79,12 +79,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (!spi_cs_is_valid(bus, cs))
return NULL;
- fsl = malloc(sizeof(struct fsl_spi_slave));
+ fsl = spi_alloc_slave(struct fsl_spi_slave, bus, cs);
if (!fsl)
return NULL;
- fsl->slave.bus = bus;
- fsl->slave.cs = cs;
fsl->mode = mode;
fsl->max_transfer_length = ESPI_MAX_DATA_TRANSFER_LEN;
diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c
new file mode 100644
index 0000000000..8865df5bef
--- /dev/null
+++ b/drivers/spi/ich.c
@@ -0,0 +1,754 @@
+/*
+ * Copyright (c) 2011-12 The Chromium OS Authors.
+ *
+ * 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
+ *
+ * This file is derived from the flashrom project.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <spi.h>
+#include <pci.h>
+#include <pci_ids.h>
+#include <asm/io.h>
+
+#include "ich.h"
+
+#define SPI_OPCODE_WREN 0x06
+#define SPI_OPCODE_FAST_READ 0x0b
+
+struct ich_ctlr {
+ pci_dev_t dev; /* PCI device number */
+ int ich_version; /* Controller version, 7 or 9 */
+ int ichspi_lock;
+ int locked;
+ uint8_t *opmenu;
+ int menubytes;
+ void *base; /* Base of register set */
+ uint16_t *preop;
+ uint16_t *optype;
+ uint32_t *addr;
+ uint8_t *data;
+ unsigned databytes;
+ uint8_t *status;
+ uint16_t *control;
+ uint32_t *bbar;
+ uint32_t *pr; /* only for ich9 */
+ uint8_t *speed; /* pointer to speed control */
+ ulong max_speed; /* Maximum bus speed in MHz */
+};
+
+struct ich_ctlr ctlr;
+
+static inline struct ich_spi_slave *to_ich_spi(struct spi_slave *slave)
+{
+ return container_of(slave, struct ich_spi_slave, slave);
+}
+
+static unsigned int ich_reg(const void *addr)
+{
+ return (unsigned)(addr - ctlr.base) & 0xffff;
+}
+
+static u8 ich_readb(const void *addr)
+{
+ u8 value = readb(addr);
+
+ debug("read %2.2x from %4.4x\n", value, ich_reg(addr));
+
+ return value;
+}
+
+static u16 ich_readw(const void *addr)
+{
+ u16 value = readw(addr);
+
+ debug("read %4.4x from %4.4x\n", value, ich_reg(addr));
+
+ return value;
+}
+
+static u32 ich_readl(const void *addr)
+{
+ u32 value = readl(addr);
+
+ debug("read %8.8x from %4.4x\n", value, ich_reg(addr));
+
+ return value;
+}
+
+static void ich_writeb(u8 value, void *addr)
+{
+ writeb(value, addr);
+ debug("wrote %2.2x to %4.4x\n", value, ich_reg(addr));
+}
+
+static void ich_writew(u16 value, void *addr)
+{
+ writew(value, addr);
+ debug("wrote %4.4x to %4.4x\n", value, ich_reg(addr));
+}
+
+static void ich_writel(u32 value, void *addr)
+{
+ writel(value, addr);
+ debug("wrote %8.8x to %4.4x\n", value, ich_reg(addr));
+}
+
+static void write_reg(const void *value, void *dest, uint32_t size)
+{
+ memcpy_toio(dest, value, size);
+}
+
+static void read_reg(const void *src, void *value, uint32_t size)
+{
+ memcpy_fromio(value, src, size);
+}
+
+static void ich_set_bbar(struct ich_ctlr *ctlr, uint32_t minaddr)
+{
+ const uint32_t bbar_mask = 0x00ffff00;
+ uint32_t ichspi_bbar;
+
+ minaddr &= bbar_mask;
+ ichspi_bbar = ich_readl(ctlr->bbar) & ~bbar_mask;
+ ichspi_bbar |= minaddr;
+ ich_writel(ichspi_bbar, ctlr->bbar);
+}
+
+int spi_cs_is_valid(unsigned int bus, unsigned int cs)
+{
+ puts("spi_cs_is_valid used but not implemented\n");
+ return 0;
+}
+
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+ unsigned int max_hz, unsigned int mode)
+{
+ struct ich_spi_slave *ich;
+
+ ich = spi_alloc_slave(struct ich_spi_slave, bus, cs);
+ if (!ich) {
+ puts("ICH SPI: Out of memory\n");
+ return NULL;
+ }
+
+ /*
+ * Yes this controller can only write a small number of bytes at
+ * once! The limit is typically 64 bytes.
+ */
+ ich->slave.max_write_size = ctlr.databytes;
+ ich->speed = max_hz;
+
+ return &ich->slave;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+ struct ich_spi_slave *ich = to_ich_spi(slave);
+
+ free(ich);
+}
+
+/*
+ * Check if this device ID matches one of supported Intel PCH devices.
+ *
+ * Return the ICH version if there is a match, or zero otherwise.
+ */
+static int get_ich_version(uint16_t device_id)
+{
+ if (device_id == PCI_DEVICE_ID_INTEL_TGP_LPC)
+ return 7;
+
+ if ((device_id >= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN &&
+ device_id <= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX) ||
+ (device_id >= PCI_DEVICE_ID_INTEL_PANTHERPOINT_LPC_MIN &&
+ device_id <= PCI_DEVICE_ID_INTEL_PANTHERPOINT_LPC_MAX))
+ return 9;
+
+ return 0;
+}
+
+/* @return 1 if the SPI flash supports the 33MHz speed */
+static int ich9_can_do_33mhz(pci_dev_t dev)
+{
+ u32 fdod, speed;
+
+ /* Observe SPI Descriptor Component Section 0 */
+ pci_write_config_dword(dev, 0xb0, 0x1000);
+
+ /* Extract the Write/Erase SPI Frequency from descriptor */
+ pci_read_config_dword(dev, 0xb4, &fdod);
+
+ /* Bits 23:21 have the fast read clock frequency, 0=20MHz, 1=33MHz */
+ speed = (fdod >> 21) & 7;
+
+ return speed == 1;
+}
+
+static int ich_find_spi_controller(pci_dev_t *devp, int *ich_versionp)
+{
+ int last_bus = pci_last_busno();
+ int bus;
+
+ if (last_bus == -1) {
+ debug("No PCI busses?\n");
+ return -1;
+ }
+
+ for (bus = 0; bus <= last_bus; bus++) {
+ uint16_t vendor_id, device_id;
+ uint32_t ids;
+ pci_dev_t dev;
+
+ dev = PCI_BDF(bus, 31, 0);
+ pci_read_config_dword(dev, 0, &ids);
+ vendor_id = ids;
+ device_id = ids >> 16;
+
+ if (vendor_id == PCI_VENDOR_ID_INTEL) {
+ *devp = dev;
+ *ich_versionp = get_ich_version(device_id);
+ return 0;
+ }
+ }
+
+ debug("ICH SPI: No ICH found.\n");
+ return -1;
+}
+
+static int ich_init_controller(struct ich_ctlr *ctlr)
+{
+ uint8_t *rcrb; /* Root Complex Register Block */
+ uint32_t rcba; /* Root Complex Base Address */
+
+ pci_read_config_dword(ctlr->dev, 0xf0, &rcba);
+ /* Bits 31-14 are the base address, 13-1 are reserved, 0 is enable. */
+ rcrb = (uint8_t *)(rcba & 0xffffc000);
+ if (ctlr->ich_version == 7) {
+ struct ich7_spi_regs *ich7_spi;
+
+ ich7_spi = (struct ich7_spi_regs *)(rcrb + 0x3020);
+ ctlr->ichspi_lock = ich_readw(&ich7_spi->spis) & SPIS_LOCK;
+ ctlr->opmenu = ich7_spi->opmenu;
+ ctlr->menubytes = sizeof(ich7_spi->opmenu);
+ ctlr->optype = &ich7_spi->optype;
+ ctlr->addr = &ich7_spi->spia;
+ ctlr->data = (uint8_t *)ich7_spi->spid;
+ ctlr->databytes = sizeof(ich7_spi->spid);
+ ctlr->status = (uint8_t *)&ich7_spi->spis;
+ ctlr->control = &ich7_spi->spic;
+ ctlr->bbar = &ich7_spi->bbar;
+ ctlr->preop = &ich7_spi->preop;
+ ctlr->base = ich7_spi;
+ } else if (ctlr->ich_version == 9) {
+ struct ich9_spi_regs *ich9_spi;
+
+ ich9_spi = (struct ich9_spi_regs *)(rcrb + 0x3800);
+ ctlr->ichspi_lock = ich_readw(&ich9_spi->hsfs) & HSFS_FLOCKDN;
+ ctlr->opmenu = ich9_spi->opmenu;
+ ctlr->menubytes = sizeof(ich9_spi->opmenu);
+ ctlr->optype = &ich9_spi->optype;
+ ctlr->addr = &ich9_spi->faddr;
+ ctlr->data = (uint8_t *)ich9_spi->fdata;
+ ctlr->databytes = sizeof(ich9_spi->fdata);
+ ctlr->status = &ich9_spi->ssfs;
+ ctlr->control = (uint16_t *)ich9_spi->ssfc;
+ ctlr->speed = ich9_spi->ssfc + 2;
+ ctlr->bbar = &ich9_spi->bbar;
+ ctlr->preop = &ich9_spi->preop;
+ ctlr->pr = &ich9_spi->pr[0];
+ ctlr->base = ich9_spi;
+ } else {
+ debug("ICH SPI: Unrecognized ICH version %d.\n",
+ ctlr->ich_version);
+ return -1;
+ }
+ debug("ICH SPI: Version %d detected\n", ctlr->ich_version);
+
+ /* Work out the maximum speed we can support */
+ ctlr->max_speed = 20000000;
+ if (ctlr->ich_version == 9 && ich9_can_do_33mhz(ctlr->dev))
+ ctlr->max_speed = 33000000;
+
+ ich_set_bbar(ctlr, 0);
+
+ return 0;
+}
+
+void spi_init(void)
+{
+ uint8_t bios_cntl;
+
+ if (ich_find_spi_controller(&ctlr.dev, &ctlr.ich_version)) {
+ printf("ICH SPI: Cannot find device\n");
+ return;
+ }
+
+ if (ich_init_controller(&ctlr)) {
+ printf("ICH SPI: Cannot setup controller\n");
+ return;
+ }
+
+ /*
+ * Disable the BIOS write protect so write commands are allowed. On
+ * v9, deassert SMM BIOS Write Protect Disable.
+ */
+ pci_read_config_byte(ctlr.dev, 0xdc, &bios_cntl);
+ if (ctlr.ich_version == 9)
+ bios_cntl &= ~(1 << 5);
+ pci_write_config_byte(ctlr.dev, 0xdc, bios_cntl | 0x1);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+ /* Handled by ICH automatically. */
+ return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+ /* Handled by ICH automatically. */
+}
+
+void spi_cs_activate(struct spi_slave *slave)
+{
+ /* Handled by ICH automatically. */
+}
+
+void spi_cs_deactivate(struct spi_slave *slave)
+{
+ /* Handled by ICH automatically. */
+}
+
+static inline void spi_use_out(struct spi_trans *trans, unsigned bytes)
+{
+ trans->out += bytes;
+ trans->bytesout -= bytes;
+}
+
+static inline void spi_use_in(struct spi_trans *trans, unsigned bytes)
+{
+ trans->in += bytes;
+ trans->bytesin -= bytes;
+}
+
+static void spi_setup_type(struct spi_trans *trans, int data_bytes)
+{
+ trans->type = 0xFF;
+
+ /* Try to guess spi type from read/write sizes. */
+ if (trans->bytesin == 0) {
+ if (trans->bytesout + data_bytes > 4)
+ /*
+ * If bytesin = 0 and bytesout > 4, we presume this is
+ * a write data operation, which is accompanied by an
+ * address.
+ */
+ trans->type = SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS;
+ else
+ trans->type = SPI_OPCODE_TYPE_WRITE_NO_ADDRESS;
+ return;
+ }
+
+ if (trans->bytesout == 1) { /* and bytesin is > 0 */
+ trans->type = SPI_OPCODE_TYPE_READ_NO_ADDRESS;
+ return;
+ }
+
+ if (trans->bytesout == 4) /* and bytesin is > 0 */
+ trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS;
+
+ /* Fast read command is called with 5 bytes instead of 4 */
+ if (trans->out[0] == SPI_OPCODE_FAST_READ && trans->bytesout == 5) {
+ trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS;
+ --trans->bytesout;
+ }
+}
+
+static int spi_setup_opcode(struct spi_trans *trans)
+{
+ uint16_t optypes;
+ uint8_t opmenu[ctlr.menubytes];
+
+ trans->opcode = trans->out[0];
+ spi_use_out(trans, 1);
+ if (!ctlr.ichspi_lock) {
+ /* The lock is off, so just use index 0. */
+ ich_writeb(trans->opcode, ctlr.opmenu);
+ optypes = ich_readw(ctlr.optype);
+ optypes = (optypes & 0xfffc) | (trans->type & 0x3);
+ ich_writew(optypes, ctlr.optype);
+ return 0;
+ } else {
+ /* The lock is on. See if what we need is on the menu. */
+ uint8_t optype;
+ uint16_t opcode_index;
+
+ /* Write Enable is handled as atomic prefix */
+ if (trans->opcode == SPI_OPCODE_WREN)
+ return 0;
+
+ read_reg(ctlr.opmenu, opmenu, sizeof(opmenu));
+ for (opcode_index = 0; opcode_index < ctlr.menubytes;
+ opcode_index++) {
+ if (opmenu[opcode_index] == trans->opcode)
+ break;
+ }
+
+ if (opcode_index == ctlr.menubytes) {
+ printf("ICH SPI: Opcode %x not found\n",
+ trans->opcode);
+ return -1;
+ }
+
+ optypes = ich_readw(ctlr.optype);
+ optype = (optypes >> (opcode_index * 2)) & 0x3;
+ if (trans->type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS &&
+ optype == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS &&
+ trans->bytesout >= 3) {
+ /* We guessed wrong earlier. Fix it up. */
+ trans->type = optype;
+ }
+ if (optype != trans->type) {
+ printf("ICH SPI: Transaction doesn't fit type %d\n",
+ optype);
+ return -1;
+ }
+ return opcode_index;
+ }
+}
+
+static int spi_setup_offset(struct spi_trans *trans)
+{
+ /* Separate the SPI address and data. */
+ switch (trans->type) {
+ case SPI_OPCODE_TYPE_READ_NO_ADDRESS:
+ case SPI_OPCODE_TYPE_WRITE_NO_ADDRESS:
+ return 0;
+ case SPI_OPCODE_TYPE_READ_WITH_ADDRESS:
+ case SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS:
+ trans->offset = ((uint32_t)trans->out[0] << 16) |
+ ((uint32_t)trans->out[1] << 8) |
+ ((uint32_t)trans->out[2] << 0);
+ spi_use_out(trans, 3);
+ return 1;
+ default:
+ printf("Unrecognized SPI transaction type %#x\n", trans->type);
+ return -1;
+ }
+}
+
+/*
+ * Wait for up to 6s til status register bit(s) turn 1 (in case wait_til_set
+ * below is True) or 0. In case the wait was for the bit(s) to set - write
+ * those bits back, which would cause resetting them.
+ *
+ * Return the last read status value on success or -1 on failure.
+ */
+static int ich_status_poll(u16 bitmask, int wait_til_set)
+{
+ int timeout = 600000; /* This will result in 6s */
+ u16 status = 0;
+
+ while (timeout--) {
+ status = ich_readw(ctlr.status);
+ if (wait_til_set ^ ((status & bitmask) == 0)) {
+ if (wait_til_set)
+ ich_writew((status & bitmask), ctlr.status);
+ return status;
+ }
+ udelay(10);
+ }
+
+ printf("ICH SPI: SCIP timeout, read %x, expected %x\n",
+ status, bitmask);
+ return -1;
+}
+
+/*
+int spi_xfer(struct spi_slave *slave, const void *dout,
+ unsigned int bitsout, void *din, unsigned int bitsin)
+*/
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
+ void *din, unsigned long flags)
+{
+ struct ich_spi_slave *ich = to_ich_spi(slave);
+ uint16_t control;
+ int16_t opcode_index;
+ int with_address;
+ int status;
+ int bytes = bitlen / 8;
+ struct spi_trans *trans = &ich->trans;
+ unsigned type = flags & (SPI_XFER_BEGIN | SPI_XFER_END);
+ int using_cmd = 0;
+ /* Align read transactions to 64-byte boundaries */
+ char buff[ctlr.databytes];
+
+ /* Ee don't support writing partial bytes. */
+ if (bitlen % 8) {
+ debug("ICH SPI: Accessing partial bytes not supported\n");
+ return -1;
+ }
+
+ /* An empty end transaction can be ignored */
+ if (type == SPI_XFER_END && !dout && !din)
+ return 0;
+
+ if (type & SPI_XFER_BEGIN)
+ memset(trans, '\0', sizeof(*trans));
+
+ /* Dp we need to come back later to finish it? */
+ if (dout && type == SPI_XFER_BEGIN) {
+ if (bytes > ICH_MAX_CMD_LEN) {
+ debug("ICH SPI: Command length limit exceeded\n");
+ return -1;
+ }
+ memcpy(trans->cmd, dout, bytes);
+ trans->cmd_len = bytes;
+ debug("ICH SPI: Saved %d bytes\n", bytes);
+ return 0;
+ }
+
+ /*
+ * We process a 'middle' spi_xfer() call, which has no
+ * SPI_XFER_BEGIN/END, as an independent transaction as if it had
+ * an end. We therefore repeat the command. This is because ICH
+ * seems to have no support for this, or because interest (in digging
+ * out the details and creating a special case in the code) is low.
+ */
+ if (trans->cmd_len) {
+ trans->out = trans->cmd;
+ trans->bytesout = trans->cmd_len;
+ using_cmd = 1;
+ debug("ICH SPI: Using %d bytes\n", trans->cmd_len);
+ } else {
+ trans->out = dout;
+ trans->bytesout = dout ? bytes : 0;
+ }
+
+ trans->in = din;
+ trans->bytesin = din ? bytes : 0;
+
+ /* There has to always at least be an opcode. */
+ if (!trans->bytesout) {
+ debug("ICH SPI: No opcode for transfer\n");
+ return -1;
+ }
+
+ if (ich_status_poll(SPIS_SCIP, 0) == -1)
+ return -1;
+
+ ich_writew(SPIS_CDS | SPIS_FCERR, ctlr.status);
+
+ spi_setup_type(trans, using_cmd ? bytes : 0);
+ opcode_index = spi_setup_opcode(trans);
+ if (opcode_index < 0)
+ return -1;
+ with_address = spi_setup_offset(trans);
+ if (with_address < 0)
+ return -1;
+
+ if (trans->opcode == SPI_OPCODE_WREN) {
+ /*
+ * Treat Write Enable as Atomic Pre-Op if possible
+ * in order to prevent the Management Engine from
+ * issuing a transaction between WREN and DATA.
+ */
+ if (!ctlr.ichspi_lock)
+ ich_writew(trans->opcode, ctlr.preop);
+ return 0;
+ }
+
+ if (ctlr.speed && ctlr.max_speed >= 33000000) {
+ int byte;
+
+ byte = ich_readb(ctlr.speed);
+ if (ich->speed >= 33000000)
+ byte |= SSFC_SCF_33MHZ;
+ else
+ byte &= ~SSFC_SCF_33MHZ;
+ ich_writeb(byte, ctlr.speed);
+ }
+
+ /* See if we have used up the command data */
+ if (using_cmd && dout && bytes) {
+ trans->out = dout;
+ trans->bytesout = bytes;
+ debug("ICH SPI: Moving to data, %d bytes\n", bytes);
+ }
+
+ /* Preset control fields */
+ control = ich_readw(ctlr.control);
+ control &= ~SSFC_RESERVED;
+ control = SPIC_SCGO | ((opcode_index & 0x07) << 4);
+
+ /* Issue atomic preop cycle if needed */
+ if (ich_readw(ctlr.preop))
+ control |= SPIC_ACS;
+
+ if (!trans->bytesout && !trans->bytesin) {
+ /* SPI addresses are 24 bit only */
+ if (with_address)
+ ich_writel(trans->offset & 0x00FFFFFF, ctlr.addr);
+
+ /*
+ * This is a 'no data' command (like Write Enable), its
+ * bitesout size was 1, decremented to zero while executing
+ * spi_setup_opcode() above. Tell the chip to send the
+ * command.
+ */
+ ich_writew(control, ctlr.control);
+
+ /* wait for the result */
+ status = ich_status_poll(SPIS_CDS | SPIS_FCERR, 1);
+ if (status == -1)
+ return -1;
+
+ if (status & SPIS_FCERR) {
+ debug("ICH SPI: Command transaction error\n");
+ return -1;
+ }
+
+ return 0;
+ }
+
+ /*
+ * Check if this is a write command atempting to transfer more bytes
+ * than the controller can handle. Iterations for writes are not
+ * supported here because each SPI write command needs to be preceded
+ * and followed by other SPI commands, and this sequence is controlled
+ * by the SPI chip driver.
+ */
+ if (trans->bytesout > ctlr.databytes) {
+ debug("ICH SPI: Too much to write. This should be prevented by the driver's max_write_size?\n");
+ return -1;
+ }
+
+ /*
+ * Read or write up to databytes bytes at a time until everything has
+ * been sent.
+ */
+ while (trans->bytesout || trans->bytesin) {
+ uint32_t data_length;
+ uint32_t aligned_offset;
+ uint32_t diff;
+
+ aligned_offset = trans->offset & ~(ctlr.databytes - 1);
+ diff = trans->offset - aligned_offset;
+
+ /* SPI addresses are 24 bit only */
+ ich_writel(aligned_offset & 0x00FFFFFF, ctlr.addr);
+
+ if (trans->bytesout)
+ data_length = min(trans->bytesout, ctlr.databytes);
+ else
+ data_length = min(trans->bytesin, ctlr.databytes);
+
+ /* Program data into FDATA0 to N */
+ if (trans->bytesout) {
+ write_reg(trans->out, ctlr.data, data_length);
+ spi_use_out(trans, data_length);
+ if (with_address)
+ trans->offset += data_length;
+ }
+
+ /* Add proper control fields' values */
+ control &= ~((ctlr.databytes - 1) << 8);
+ control |= SPIC_DS;
+ control |= (data_length - 1) << 8;
+
+ /* write it */
+ ich_writew(control, ctlr.control);
+
+ /* Wait for Cycle Done Status or Flash Cycle Error. */
+ status = ich_status_poll(SPIS_CDS | SPIS_FCERR, 1);
+ if (status == -1)
+ return -1;
+
+ if (status & SPIS_FCERR) {
+ debug("ICH SPI: Data transaction error\n");
+ return -1;
+ }
+
+ if (trans->bytesin) {
+ if (diff) {
+ data_length -= diff;
+ read_reg(ctlr.data, buff, ctlr.databytes);
+ memcpy(trans->in, buff + diff, data_length);
+ } else {
+ read_reg(ctlr.data, trans->in, data_length);
+ }
+ spi_use_in(trans, data_length);
+ if (with_address)
+ trans->offset += data_length;
+ }
+ }
+
+ /* Clear atomic preop now that xfer is done */
+ ich_writew(0, ctlr.preop);
+
+ return 0;
+}
+
+
+/*
+ * This uses the SPI controller from the Intel Cougar Point and Panther Point
+ * PCH to write-protect portions of the SPI flash until reboot. The changes
+ * don't actually take effect until the HSFS[FLOCKDN] bit is set, but that's
+ * done elsewhere.
+ */
+int spi_write_protect_region(uint32_t lower_limit, uint32_t length, int hint)
+{
+ uint32_t tmplong;
+ uint32_t upper_limit;
+
+ if (!ctlr.pr) {
+ printf("%s: operation not supported on this chipset\n",
+ __func__);
+ return -1;
+ }
+
+ if (length == 0 ||
+ lower_limit > (0xFFFFFFFFUL - length) + 1 ||
+ hint < 0 || hint > 4) {
+ printf("%s(0x%x, 0x%x, %d): invalid args\n", __func__,
+ lower_limit, length, hint);
+ return -1;
+ }
+
+ upper_limit = lower_limit + length - 1;
+
+ /*
+ * Determine bits to write, as follows:
+ * 31 Write-protection enable (includes erase operation)
+ * 30:29 reserved
+ * 28:16 Upper Limit (FLA address bits 24:12, with 11:0 == 0xfff)
+ * 15 Read-protection enable
+ * 14:13 reserved
+ * 12:0 Lower Limit (FLA address bits 24:12, with 11:0 == 0x000)
+ */
+ tmplong = 0x80000000 |
+ ((upper_limit & 0x01fff000) << 4) |
+ ((lower_limit & 0x01fff000) >> 12);
+
+ printf("%s: writing 0x%08x to %p\n", __func__, tmplong,
+ &ctlr.pr[hint]);
+ ctlr.pr[hint] = tmplong;
+
+ return 0;
+}
diff --git a/drivers/spi/ich.h b/drivers/spi/ich.h
new file mode 100644
index 0000000000..bd7bc12c60
--- /dev/null
+++ b/drivers/spi/ich.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ *
+ * 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
+ *
+ * This file is derived from the flashrom project.
+ */
+
+struct ich7_spi_regs {
+ uint16_t spis;
+ uint16_t spic;
+ uint32_t spia;
+ uint64_t spid[8];
+ uint64_t _pad;
+ uint32_t bbar;
+ uint16_t preop;
+ uint16_t optype;
+ uint8_t opmenu[8];
+} __packed;
+
+struct ich9_spi_regs {
+ uint32_t bfpr; /* 0x00 */
+ uint16_t hsfs;
+ uint16_t hsfc;
+ uint32_t faddr;
+ uint32_t _reserved0;
+ uint32_t fdata[16]; /* 0x10 */
+ uint32_t frap; /* 0x50 */
+ uint32_t freg[5];
+ uint32_t _reserved1[3];
+ uint32_t pr[5]; /* 0x74 */
+ uint32_t _reserved2[2];
+ uint8_t ssfs; /* 0x90 */
+ uint8_t ssfc[3];
+ uint16_t preop; /* 0x94 */
+ uint16_t optype;
+ uint8_t opmenu[8]; /* 0x98 */
+ uint32_t bbar;
+ uint8_t _reserved3[12];
+ uint32_t fdoc;
+ uint32_t fdod;
+ uint8_t _reserved4[8];
+ uint32_t afc;
+ uint32_t lvscc;
+ uint32_t uvscc;
+ uint8_t _reserved5[4];
+ uint32_t fpb;
+ uint8_t _reserved6[28];
+ uint32_t srdl;
+ uint32_t srdc;
+ uint32_t srd;
+} __packed;
+
+enum {
+ SPIS_SCIP = 0x0001,
+ SPIS_GRANT = 0x0002,
+ SPIS_CDS = 0x0004,
+ SPIS_FCERR = 0x0008,
+ SSFS_AEL = 0x0010,
+ SPIS_LOCK = 0x8000,
+ SPIS_RESERVED_MASK = 0x7ff0,
+ SSFS_RESERVED_MASK = 0x7fe2
+};
+
+enum {
+ SPIC_SCGO = 0x000002,
+ SPIC_ACS = 0x000004,
+ SPIC_SPOP = 0x000008,
+ SPIC_DBC = 0x003f00,
+ SPIC_DS = 0x004000,
+ SPIC_SME = 0x008000,
+ SSFC_SCF_MASK = 0x070000,
+ SSFC_RESERVED = 0xf80000,
+
+ /* Mask for speed byte, biuts 23:16 of SSFC */
+ SSFC_SCF_33MHZ = 0x01,
+};
+
+enum {
+ HSFS_FDONE = 0x0001,
+ HSFS_FCERR = 0x0002,
+ HSFS_AEL = 0x0004,
+ HSFS_BERASE_MASK = 0x0018,
+ HSFS_BERASE_SHIFT = 3,
+ HSFS_SCIP = 0x0020,
+ HSFS_FDOPSS = 0x2000,
+ HSFS_FDV = 0x4000,
+ HSFS_FLOCKDN = 0x8000
+};
+
+enum {
+ HSFC_FGO = 0x0001,
+ HSFC_FCYCLE_MASK = 0x0006,
+ HSFC_FCYCLE_SHIFT = 1,
+ HSFC_FDBC_MASK = 0x3f00,
+ HSFC_FDBC_SHIFT = 8,
+ HSFC_FSMIE = 0x8000
+};
+
+enum {
+ SPI_OPCODE_TYPE_READ_NO_ADDRESS = 0,
+ SPI_OPCODE_TYPE_WRITE_NO_ADDRESS = 1,
+ SPI_OPCODE_TYPE_READ_WITH_ADDRESS = 2,
+ SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS = 3
+};
+
+enum {
+ ICH_MAX_CMD_LEN = 5,
+};
+
+struct spi_trans {
+ uint8_t cmd[ICH_MAX_CMD_LEN];
+ int cmd_len;
+ const uint8_t *out;
+ uint32_t bytesout;
+ uint8_t *in;
+ uint32_t bytesin;
+ uint8_t type;
+ uint8_t opcode;
+ uint32_t offset;
+};
+
+struct ich_spi_slave {
+ struct spi_slave slave;
+ struct spi_trans trans; /* current transaction in progress */
+ int speed; /* SPI speed in Hz */
+};
diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c
index de81064b9d..caa91e3e81 100644
--- a/drivers/spi/kirkwood_spi.c
+++ b/drivers/spi/kirkwood_spi.c
@@ -49,13 +49,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (!spi_cs_is_valid(bus, cs))
return NULL;
- slave = malloc(sizeof(struct spi_slave));
+ slave = spi_alloc_slave_base(bus, cs);
if (!slave)
return NULL;
- slave->bus = bus;
- slave->cs = cs;
-
writel(~KWSPI_CSN_ACT | KWSPI_SMEMRDY, &spireg->ctrl);
/* calculate spi clock prescaller using max_hz */
diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c
index 3e96b3f9f3..4b50bca880 100644
--- a/drivers/spi/mpc52xx_spi.c
+++ b/drivers/spi/mpc52xx_spi.c
@@ -48,13 +48,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
{
struct spi_slave *slave;
- slave = malloc(sizeof(struct spi_slave));
+ slave = spi_alloc_slave_base(bus, cs);
if (!slave)
return NULL;
- slave->bus = bus;
- slave->cs = cs;
-
return slave;
}
diff --git a/drivers/spi/mpc8xxx_spi.c b/drivers/spi/mpc8xxx_spi.c
index 4e46041dff..6b0e3b46ec 100644
--- a/drivers/spi/mpc8xxx_spi.c
+++ b/drivers/spi/mpc8xxx_spi.c
@@ -45,13 +45,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (!spi_cs_is_valid(bus, cs))
return NULL;
- slave = malloc(sizeof(struct spi_slave));
+ slave = spi_alloc_slave_base(bus, cs);
if (!slave)
return NULL;
- slave->bus = bus;
- slave->cs = cs;
-
/*
* TODO: Some of the code in spi_init() should probably move
* here, or into spi_claim_bus() below.
diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c
index 859c43fee2..d792d8d493 100644
--- a/drivers/spi/mxc_spi.c
+++ b/drivers/spi/mxc_spi.c
@@ -408,7 +408,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (bus >= ARRAY_SIZE(spi_bases))
return NULL;
- mxcs = calloc(sizeof(struct mxc_spi_slave), 1);
+ mxcs = spi_alloc_slave(struct mxc_spi_slave, bus, cs);
if (!mxcs) {
puts("mxc_spi: SPI Slave not allocated !\n");
return NULL;
@@ -424,8 +424,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
cs = ret;
- mxcs->slave.bus = bus;
- mxcs->slave.cs = cs;
mxcs->base = spi_bases[bus];
ret = spi_cfg_mxc(mxcs, cs, max_hz, mode);
diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c
index ffa3c1d693..aa999f9a94 100644
--- a/drivers/spi/mxs_spi.c
+++ b/drivers/spi/mxs_spi.c
@@ -77,15 +77,13 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
return NULL;
}
- mxs_slave = calloc(sizeof(struct mxs_spi_slave), 1);
+ mxs_slave = spi_alloc_slave(struct mxs_spi_slave, bus, cs);
if (!mxs_slave)
return NULL;
if (mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + bus))
goto err_init;
- mxs_slave->slave.bus = bus;
- mxs_slave->slave.cs = cs;
mxs_slave->max_khz = max_hz / 1000;
mxs_slave->mode = mode;
mxs_slave->regs = mxs_ssp_regs_by_bus(bus);
diff --git a/drivers/spi/oc_tiny_spi.c b/drivers/spi/oc_tiny_spi.c
index fc01fb83a2..6f7b1edd60 100644
--- a/drivers/spi/oc_tiny_spi.c
+++ b/drivers/spi/oc_tiny_spi.c
@@ -90,13 +90,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (!spi_cs_is_valid(bus, cs) || gpio_request(cs, "tiny_spi"))
return NULL;
- tiny_spi = malloc(sizeof(*tiny_spi));
+ tiny_spi = spi_alloc_slave(struct tiny_spi_slave, bus, cs);
if (!tiny_spi)
return NULL;
- memset(tiny_spi, 0, sizeof(*tiny_spi));
- tiny_spi->slave.bus = bus;
- tiny_spi->slave.cs = cs;
tiny_spi->host = &tiny_spi_host_list[bus];
tiny_spi->mode = mode & (SPI_CPOL | SPI_CPHA);
tiny_spi->flg = mode & SPI_CS_HIGH ? 1 : 0;
diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
index 344d5b8a7e..80a4e4776c 100644
--- a/drivers/spi/omap3_spi.c
+++ b/drivers/spi/omap3_spi.c
@@ -80,12 +80,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct omap3_spi_slave *ds;
-
- ds = malloc(sizeof(struct omap3_spi_slave));
- if (!ds) {
- printf("SPI error: malloc of SPI structure failed\n");
- return NULL;
- }
+ struct mcspi *regs;
/*
* OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules)
@@ -98,21 +93,21 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
switch (bus) {
case 0:
- ds->regs = (struct mcspi *)OMAP3_MCSPI1_BASE;
+ regs = (struct mcspi *)OMAP3_MCSPI1_BASE;
break;
#ifdef OMAP3_MCSPI2_BASE
case 1:
- ds->regs = (struct mcspi *)OMAP3_MCSPI2_BASE;
+ regs = (struct mcspi *)OMAP3_MCSPI2_BASE;
break;
#endif
#ifdef OMAP3_MCSPI3_BASE
case 2:
- ds->regs = (struct mcspi *)OMAP3_MCSPI3_BASE;
+ regs = (struct mcspi *)OMAP3_MCSPI3_BASE;
break;
#endif
#ifdef OMAP3_MCSPI4_BASE
case 3:
- ds->regs = (struct mcspi *)OMAP3_MCSPI4_BASE;
+ regs = (struct mcspi *)OMAP3_MCSPI4_BASE;
break;
#endif
default:
@@ -120,7 +115,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
Supported busses 0 - 3\n", bus);
return NULL;
}
- ds->slave.bus = bus;
if (((bus == 0) && (cs > 3)) ||
((bus == 1) && (cs > 1)) ||
@@ -130,19 +124,26 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
on bus %i\n", cs, bus);
return NULL;
}
- ds->slave.cs = cs;
if (max_hz > OMAP3_MCSPI_MAX_FREQ) {
printf("SPI error: unsupported frequency %i Hz. \
Max frequency is 48 Mhz\n", max_hz);
return NULL;
}
- ds->freq = max_hz;
if (mode > SPI_MODE_3) {
printf("SPI error: unsupported SPI mode %i\n", mode);
return NULL;
}
+
+ ds = spi_alloc_slave(struct omap3_spi_slave, bus, cs);
+ if (!ds) {
+ printf("SPI error: malloc of SPI structure failed\n");
+ return NULL;
+ }
+
+ ds->regs = regs;
+ ds->freq = max_hz;
ds->mode = mode;
return &ds->slave;
diff --git a/drivers/spi/sh_spi.c b/drivers/spi/sh_spi.c
index e944b23c2d..744afe3295 100644
--- a/drivers/spi/sh_spi.c
+++ b/drivers/spi/sh_spi.c
@@ -103,12 +103,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (!spi_cs_is_valid(bus, cs))
return NULL;
- ss = malloc(sizeof(struct spi_slave));
+ ss = spi_alloc_slave(struct sh_spi, bus, cs);
if (!ss)
return NULL;
- ss->slave.bus = bus;
- ss->slave.cs = cs;
ss->regs = (struct sh_spi_regs *)CONFIG_SH_SPI_BASE;
/* SPI sycle stop */
diff --git a/drivers/spi/soft_spi.c b/drivers/spi/soft_spi.c
index 13df8cb7de..a1b84b6e37 100644
--- a/drivers/spi/soft_spi.c
+++ b/drivers/spi/soft_spi.c
@@ -73,12 +73,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (!spi_cs_is_valid(bus, cs))
return NULL;
- ss = malloc(sizeof(struct soft_spi_slave));
+ ss = spi_alloc_slave(struct soft_spi_slave, bus, cs);
if (!ss)
return NULL;
- ss->slave.bus = bus;
- ss->slave.cs = cs;
ss->mode = mode;
/* TODO: Use max_hz to limit the SCK rate */
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
new file mode 100644
index 0000000000..cb36c5e6eb
--- /dev/null
+++ b/drivers/spi/spi.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ *
+ * 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.h>
+
+void *spi_do_alloc_slave(int offset, int size, unsigned int bus,
+ unsigned int cs)
+{
+ struct spi_slave *slave;
+ void *ptr;
+
+ ptr = malloc(size);
+ if (ptr) {
+ memset(ptr, '\0', size);
+ slave = (struct spi_slave *)(ptr + offset);
+ slave->bus = bus;
+ slave->cs = cs;
+ }
+
+ return ptr;
+}
diff --git a/drivers/spi/tegra_slink.c b/drivers/spi/tegra_slink.c
index 2c41fabe28..9da58774db 100644
--- a/drivers/spi/tegra_slink.c
+++ b/drivers/spi/tegra_slink.c
@@ -81,13 +81,11 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
return NULL;
}
- spi = malloc(sizeof(struct tegra_spi_slave));
+ spi = spi_alloc_slave(struct tegra_spi_slave, bus, cs);
if (!spi) {
printf("SPI error: malloc of SPI structure failed\n");
return NULL;
}
- spi->slave.bus = bus;
- spi->slave.cs = cs;
spi->ctrl = &spi_ctrls[bus];
if (!spi->ctrl) {
printf("SPI error: could not find controller for bus %d\n",
diff --git a/drivers/spi/tegra_spi.c b/drivers/spi/tegra_spi.c
index ce19095af0..05027af3bc 100644
--- a/drivers/spi/tegra_spi.c
+++ b/drivers/spi/tegra_spi.c
@@ -81,13 +81,11 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
return NULL;
}
- spi = malloc(sizeof(struct tegra_spi_slave));
+ spi = spi_alloc_slave(struct tegra_spi_slave, bus, cs);
if (!spi) {
printf("SPI error: malloc of SPI structure failed\n");
return NULL;
}
- spi->slave.bus = bus;
- spi->slave.cs = cs;
#ifdef CONFIG_OF_CONTROL
int node = fdtdec_next_compatible(gd->fdt_blob, 0,
COMPAT_NVIDIA_TEGRA20_SFLASH);
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
index db01cc25f7..a82b056948 100644
--- a/drivers/spi/xilinx_spi.c
+++ b/drivers/spi/xilinx_spi.c
@@ -85,14 +85,12 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
return NULL;
}
- xilspi = malloc(sizeof(*xilspi));
+ xilspi = spi_alloc_slave(struct xilinx_spi_slave, bus, cs);
if (!xilspi) {
printf("XILSPI error: %s: malloc of SPI structure failed\n",
__func__);
return NULL;
}
- xilspi->slave.bus = bus;
- xilspi->slave.cs = cs;
xilspi->regs = (struct xilinx_spi_reg *)xilinx_spi_base_list[bus];
xilspi->freq = max_hz;
xilspi->mode = mode;