From 4397a2a80baefadc7454c70282c14d2af16ffe30 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 19 Mar 2013 04:58:51 +0000 Subject: fdt: Add fdtdec_get_addr_size() to read reg properties It is common to have a "reg =
" property in the FDT. Add a function to handle this, similar to the existing fdtdec_get_addr(); Signed-off-by: Simon Glass --- include/fdtdec.h | 15 +++++++++++++++ lib/fdtdec.c | 27 +++++++++++++++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/include/fdtdec.h b/include/fdtdec.h index 21894835d1..5ca84a0c72 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -38,11 +38,13 @@ */ #ifdef CONFIG_PHYS_64BIT typedef u64 fdt_addr_t; +typedef u64 fdt_size_t; #define FDT_ADDR_T_NONE (-1ULL) #define fdt_addr_to_cpu(reg) be64_to_cpu(reg) #define fdt_size_to_cpu(reg) be64_to_cpu(reg) #else typedef u32 fdt_addr_t; +typedef u32 fdt_size_t; #define FDT_ADDR_T_NONE (-1U) #define fdt_addr_to_cpu(reg) be32_to_cpu(reg) #define fdt_size_to_cpu(reg) be32_to_cpu(reg) @@ -199,6 +201,19 @@ int fdtdec_next_compatible_subnode(const void *blob, int node, fdt_addr_t fdtdec_get_addr(const void *blob, int node, const char *prop_name); +/** + * Look up an address property in a node and return it as an address. + * The property must hold one address with a length. This is only tested + * on 32-bit machines. + * + * @param blob FDT blob + * @param node node to examine + * @param prop_name name of property to find + * @return address, if found, or FDT_ADDR_T_NONE if not + */ +fdt_addr_t fdtdec_get_addr_size(const void *blob, int node, + const char *prop_name, fdt_size_t *sizep); + /** * Look up a 32-bit integer property in a node and return it. The property * must have at least 4 bytes of data. The value of the first cell is diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 43f29f5c6b..cffba94bf6 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -68,25 +68,40 @@ const char *fdtdec_get_compatible(enum fdt_compat_id id) return compat_names[id]; } -fdt_addr_t fdtdec_get_addr(const void *blob, int node, - const char *prop_name) +fdt_addr_t fdtdec_get_addr_size(const void *blob, int node, + const char *prop_name, fdt_size_t *sizep) { const fdt_addr_t *cell; int len; debug("%s: %s: ", __func__, prop_name); cell = fdt_getprop(blob, node, prop_name, &len); - if (cell && (len == sizeof(fdt_addr_t) || - len == sizeof(fdt_addr_t) * 2)) { + if (cell && ((!sizep && len == sizeof(fdt_addr_t)) || + len == sizeof(fdt_addr_t) * 2)) { fdt_addr_t addr = fdt_addr_to_cpu(*cell); - - debug("%p\n", (void *)addr); + if (sizep) { + const fdt_size_t *size; + + size = (fdt_size_t *)((char *)cell + + sizeof(fdt_addr_t)); + *sizep = fdt_size_to_cpu(*size); + debug("addr=%p, size=%p\n", (void *)addr, + (void *)*sizep); + } else { + debug("%p\n", (void *)addr); + } return addr; } debug("(not found)\n"); return FDT_ADDR_T_NONE; } +fdt_addr_t fdtdec_get_addr(const void *blob, int node, + const char *prop_name) +{ + return fdtdec_get_addr_size(blob, node, prop_name, NULL); +} + s32 fdtdec_get_int(const void *blob, int node, const char *prop_name, s32 default_val) { -- cgit v1.2.1 From ba6c3ce9bd0ac572592dc909878117dce219c564 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 11 Mar 2013 06:08:00 +0000 Subject: spi: Add function to allocate a new SPI slave At present it is difficult to extend the SPI structure since all drivers allocate it themselves, and few of them zero all fields. Add a new function spi_alloc_slave() which can be used by SPI drivers to perform this allocation, and thus ensure that all drivers can better cope with SPI structure changes. Signed-off-by: Simon Glass --- drivers/spi/Makefile | 3 +++ drivers/spi/spi.c | 39 +++++++++++++++++++++++++++++++++++++++ include/spi.h | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 drivers/spi/spi.c diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index b8264df3a9..45862ec938 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 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 +#include +#include + +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/include/spi.h b/include/spi.h index 60e85db9a4..ebc9652160 100644 --- a/include/spi.h +++ b/include/spi.h @@ -62,6 +62,47 @@ struct spi_slave { */ void spi_init(void); +/** + * spi_do_alloc_slave - Allocate a new SPI slave (internal) + * + * Allocate and zero all fields in the spi slave, and set the bus/chip + * select. Use the helper macro spi_alloc_slave() to call this. + * + * @offset: Offset of struct spi_slave within slave structure + * @size: Size of slave structure + * @bus: Bus ID of the slave chip. + * @cs: Chip select ID of the slave chip on the specified bus. + */ +void *spi_do_alloc_slave(int offset, int size, unsigned int bus, + unsigned int cs); + +/** + * spi_alloc_slave - Allocate a new SPI slave + * + * Allocate and zero all fields in the spi slave, and set the bus/chip + * select. + * + * @_struct: Name of structure to allocate (e.g. struct tegra_spi). This + * structure must contain a member 'struct spi_slave *slave'. + * @bus: Bus ID of the slave chip. + * @cs: Chip select ID of the slave chip on the specified bus. + */ +#define spi_alloc_slave(_struct, bus, cs) \ + spi_do_alloc_slave(offsetof(_struct, slave), \ + sizeof(_struct), bus, cs) + +/** + * spi_alloc_slave_base - Allocate a new SPI slave with no private data + * + * Allocate and zero all fields in the spi slave, and set the bus/chip + * select. + * + * @bus: Bus ID of the slave chip. + * @cs: Chip select ID of the slave chip on the specified bus. + */ +#define spi_alloc_slave_base(bus, cs) \ + spi_do_alloc_slave(0, sizeof(struct spi_slave), bus, cs) + /*----------------------------------------------------------------------- * Set up communications parameters for a SPI slave. * -- cgit v1.2.1 From d3504fee73ec626117427afa08116d1dde21ba9d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 18 Mar 2013 19:23:40 +0000 Subject: spi: Use spi_alloc_slave() in each SPI driver Rather than each driver having its own way to allocate a SPI slave, use the new allocation function everywhere. This will make it easier to extend the interface without breaking drivers. Signed-off-by: Simon Glass --- drivers/spi/altera_spi.c | 4 +--- drivers/spi/andes_spi.c | 4 +--- drivers/spi/armada100_spi.c | 4 +--- drivers/spi/atmel_spi.c | 4 +--- drivers/spi/bfin_spi.c | 4 +--- drivers/spi/bfin_spi6xx.c | 4 +--- drivers/spi/cf_qspi.c | 4 +--- drivers/spi/cf_spi.c | 4 +--- drivers/spi/davinci_spi.c | 4 +--- drivers/spi/exynos_spi.c | 4 +--- drivers/spi/fsl_espi.c | 4 +--- drivers/spi/kirkwood_spi.c | 5 +---- drivers/spi/mpc52xx_spi.c | 5 +---- drivers/spi/mpc8xxx_spi.c | 5 +---- drivers/spi/mxc_spi.c | 4 +--- drivers/spi/mxs_spi.c | 4 +--- drivers/spi/oc_tiny_spi.c | 5 +---- drivers/spi/omap3_spi.c | 27 ++++++++++++++------------- drivers/spi/sh_spi.c | 4 +--- drivers/spi/soft_spi.c | 4 +--- drivers/spi/tegra_slink.c | 4 +--- drivers/spi/tegra_spi.c | 4 +--- drivers/spi/xilinx_spi.c | 4 +--- 23 files changed, 36 insertions(+), 83 deletions(-) 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/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/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; -- cgit v1.2.1 From b5aec1424d191c51f694ba85d5577e7a635363d9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 11 Mar 2013 06:08:02 +0000 Subject: sf: Add spi_flash_alloc() to create a new SPI flash struct At present it is difficult to extend the SPI flash structure since all devices allocate it themselves, and few of them zero all fields. Add a new function spi_flash_alloc() which can be used by SPI devices to perform this allocation, and thus ensure that all devices can better cope with SPI structure changes. Signed-off-by: Simon Glass --- drivers/mtd/spi/spi_flash.c | 25 +++++++++++++++++++++++++ include/spi_flash.h | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 00aece9291..17f3d3cb14 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -401,6 +401,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/include/spi_flash.h b/include/spi_flash.h index 9da90624f2..030d49cb71 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.h @@ -47,6 +47,44 @@ struct spi_flash { size_t len); }; +/** + * spi_flash_do_alloc - Allocate a new spi flash structure + * + * The structure is allocated and cleared with default values for + * read, write and erase, which the caller can modify. The caller must set + * up size, page_size and sector_size. + * + * Use the helper macro spi_flash_alloc() to call this. + * + * @offset: Offset of struct spi_slave within slave structure + * @size: Size of slave structure + * @spi: SPI slave + * @name: Name of SPI flash device + */ +void *spi_flash_do_alloc(int offset, int size, struct spi_slave *spi, + const char *name); + +/** + * spi_flash_alloc - Allocate a new SPI flash structure + * + * @_struct: Name of structure to allocate (e.g. struct ramtron_spi_fram). This + * structure must contain a member 'struct spi_flash *flash'. + * @spi: SPI slave + * @name: Name of SPI flash device + */ +#define spi_flash_alloc(_struct, spi, name) \ + spi_flash_do_alloc(offsetof(_struct, flash), sizeof(_struct), \ + spi, name) + +/** + * spi_flash_alloc_base - Allocate a new SPI flash structure with no private data + * + * @spi: SPI slave + * @name: Name of SPI flash device + */ +#define spi_flash_alloc_base(spi, name) \ + spi_flash_do_alloc(0, sizeof(struct spi_flash), spi, name) + struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int spi_mode); void spi_flash_free(struct spi_flash *flash); -- cgit v1.2.1 From c0f87dd4ffa0c012d1dc8f737412c58200a93c93 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 11 Mar 2013 06:08:03 +0000 Subject: sf: Use spi_flash_alloc() in each SPI flash driver Rather than each device having its own way to allocate a SPI flash structure, use the new allocation function everywhere. This will make it easier to extend the interface without breaking devices. Signed-off-by: Simon Glass --- drivers/mtd/spi/atmel.c | 8 +------- drivers/mtd/spi/eon.c | 8 +------- drivers/mtd/spi/macronix.c | 8 +------- drivers/mtd/spi/ramtron.c | 4 +--- drivers/mtd/spi/spansion.c | 8 +------- drivers/mtd/spi/sst.c | 8 +------- drivers/mtd/spi/stmicro.c | 8 +------- drivers/mtd/spi/winbond.c | 8 +------- 8 files changed, 8 insertions(+), 52 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/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; -- cgit v1.2.1 From 1853030e2164c849c09f1ba051d243265e2591ec Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 19 Mar 2013 04:58:56 +0000 Subject: x86: spi: Add Intel ICH driver This supports Intel ICH7/9. The Intel controller is a little unusual in that it is mostly intended for use with SPI flash, and has some optimisations and features specifically for that application. In particular it is not possible to support ongoing transactions that continue over many calls with SPI_XFER_BEGIN and SPI_XFER_END. This driver supports writes of up to 64 bytes at a time, the limit for the controller. Future work will improve this. Signed-off-by: Bernie Thompson Signed-off-by: Duncan Laurie Signed-off-by: Bill Richardson Signed-off-by: Vadim Bendebury Signed-off-by: Gabe Black Signed-off-by: Simon Glass --- drivers/spi/Makefile | 1 + drivers/spi/ich.c | 749 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/ich.h | 143 ++++++++++ 3 files changed, 893 insertions(+) create mode 100644 drivers/spi/ich.c create mode 100644 drivers/spi/ich.h diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 45862ec938..42685955dc 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -39,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/ich.c b/drivers/spi/ich.c new file mode 100644 index 0000000000..ef1ce1d96c --- /dev/null +++ b/drivers/spi/ich.c @@ -0,0 +1,749 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#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; + } + + 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 */ +}; -- cgit v1.2.1 From 0c456cee952f3fa5ae6f5c42f960eeaa39140b62 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 11 Mar 2013 06:08:05 +0000 Subject: spi: Add parameter for maximum write size Some SPI controllers (e.g. Intel ICH) have a limit on the number of SPI bytes that can be written at a time. Add this as a parameter so that clients of the SPI interface can respect this value. Signed-off-by: Simon Glass --- include/spi.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/spi.h b/include/spi.h index ebc9652160..3fe2e1eab2 100644 --- a/include/spi.h +++ b/include/spi.h @@ -49,10 +49,13 @@ * * bus: ID of the bus that the slave is attached to. * cs: ID of the chip select connected to the slave. + * max_write_size: If non-zero, the maximum number of bytes which can + * be written at once, excluding command bytes. */ struct spi_slave { unsigned int bus; unsigned int cs; + unsigned int max_write_size; }; /*----------------------------------------------------------------------- -- cgit v1.2.1 From 1e566bc6dbcfc9f13909d5cb40dd74925bcdd579 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 11 Mar 2013 06:08:06 +0000 Subject: sf: Respect maximum SPI write size Some SPI flash controllers (e.g. Intel ICH) have a limit on the number of bytes that can be in a write transaction. Support this by breaking the writes into multiple transactions. Signed-off-by: Simon Glass --- drivers/mtd/spi/spi_flash.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 17f3d3cb14..b82011d0fd 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -87,6 +87,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 +114,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", -- cgit v1.2.1 From 5e6fb69778fa41e685add00b73ed5f22c7a96166 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 11 Mar 2013 06:08:07 +0000 Subject: x86: spi: Set maximum write size for ICH This SPI controller can only write 64 bytes at a time. Add this restriction in so that 'sf write' works correct for blocks larger than 64 bytes. Signed-off-by: Simon Glass --- drivers/spi/ich.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c index ef1ce1d96c..8865df5bef 100644 --- a/drivers/spi/ich.c +++ b/drivers/spi/ich.c @@ -147,6 +147,11 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, 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; -- cgit v1.2.1 From bb8215f437a7c948eec82a6abe754c226978bd6d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 11 Mar 2013 06:08:08 +0000 Subject: sf: Enable FDT-based configuration and memory mapping Enable device tree control of SPI flash, and use this to implement memory-mapped SPI flash, which is supported on Intel chips. Signed-off-by: Simon Glass --- drivers/mtd/spi/spi_flash.c | 46 ++++++++++++++++++++++++++++++++++++++++++++- include/fdtdec.h | 1 + include/spi_flash.h | 1 + lib/fdtdec.c | 1 + 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index b82011d0fd..111185af17 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -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 */ @@ -146,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; @@ -275,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 * @@ -391,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); diff --git a/include/fdtdec.h b/include/fdtdec.h index 5ca84a0c72..3b363be036 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -86,6 +86,7 @@ enum fdt_compat_id { COMPAT_SAMSUNG_EXYNOS_EHCI, /* Exynos EHCI controller */ COMPAT_SAMSUNG_EXYNOS_USB_PHY, /* Exynos phy controller for usb2.0 */ COMPAT_MAXIM_MAX77686_PMIC, /* MAX77686 PMIC */ + COMPAT_GENERIC_SPI_FLASH, /* Generic SPI Flash chip */ COMPAT_COUNT, }; diff --git a/include/spi_flash.h b/include/spi_flash.h index 030d49cb71..3b6a44edce 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.h @@ -39,6 +39,7 @@ struct spi_flash { /* Erase (sector) size */ u32 sector_size; + void *memory_map; /* Address of read-only SPI flash access */ int (*read)(struct spi_flash *flash, u32 offset, size_t len, void *buf); int (*write)(struct spi_flash *flash, u32 offset, diff --git a/lib/fdtdec.c b/lib/fdtdec.c index cffba94bf6..c95c2c28fa 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -59,6 +59,7 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(SAMSUNG_EXYNOS_EHCI, "samsung,exynos-ehci"), COMPAT(SAMSUNG_EXYNOS_USB_PHY, "samsung,exynos-usb-phy"), COMPAT(MAXIM_MAX77686_PMIC, "maxim,max77686_pmic"), + COMPAT(GENERIC_SPI_FLASH, "spi-flash"), }; const char *fdtdec_get_compatible(enum fdt_compat_id id) -- cgit v1.2.1 From 192868b0614e68d165fffe9996c6c45db1c47fcb Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 11 Mar 2013 06:08:09 +0000 Subject: x86: Move PCI init before SPI init It is possible that our PCI bus will provide the SPI controller, so change the init order to make this work. Signed-off-by: Simon Glass --- arch/x86/lib/board.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/lib/board.c b/arch/x86/lib/board.c index 452e5d8262..f372898f61 100644 --- a/arch/x86/lib/board.c +++ b/arch/x86/lib/board.c @@ -164,13 +164,13 @@ init_fnc_t *init_sequence_r[] = { #ifndef CONFIG_SYS_NO_FLASH flash_init_r, #endif -#ifdef CONFIG_SPI - init_func_spi; -#endif - env_relocate_r, #ifdef CONFIG_PCI pci_init_r, #endif +#ifdef CONFIG_SPI + init_func_spi, +#endif + env_relocate_r, stdio_init, jumptable_init_r, console_init_r, -- cgit v1.2.1 From 7ea01d1808b349011f3442b40138f7f8a7c58aa6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 11 Mar 2013 06:08:10 +0000 Subject: x86: Add FDT SPI node for link Add a memory-mapped 8GB SPI chip. Signed-off-by: Simon Glass --- board/chromebook-x86/dts/link.dts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/board/chromebook-x86/dts/link.dts b/board/chromebook-x86/dts/link.dts index ae8217d02e..d0738cbf46 100644 --- a/board/chromebook-x86/dts/link.dts +++ b/board/chromebook-x86/dts/link.dts @@ -21,4 +21,15 @@ chosen { }; memory { device_type = "memory"; reg = <0 0>; }; + + spi { + #address-cells = <1>; + #size-cells = <0>; + compatible = "intel,ich9"; + spi-flash@0 { + reg = <0>; + compatible = "winbond,w25q64", "spi-flash"; + memory-map = <0xff800000 0x00800000>; + }; + }; }; -- cgit v1.2.1 From e30bd5cfee742b93de8e6794a1f896ee501ef738 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 11 Mar 2013 06:08:11 +0000 Subject: x86: Enable SPI flash support for coreboot Turn on SPI flash support and related commands. Signed-off-by: Simon Glass --- include/configs/coreboot.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/include/configs/coreboot.h b/include/configs/coreboot.h index 49f05decc0..48267563e5 100644 --- a/include/configs/coreboot.h +++ b/include/configs/coreboot.h @@ -257,10 +257,16 @@ /*----------------------------------------------------------------------- * FLASH configuration */ +#define CONFIG_ICH_SPI +#define CONFIG_SPI_FLASH +#define CONFIG_SPI_FLASH_MACRONIX +#define CONFIG_SPI_FLASH_WINBOND +#define CONFIG_SPI_FLASH_GIGADEVICE #define CONFIG_SYS_NO_FLASH -#undef CONFIG_FLASH_CFI_DRIVER -#define CONFIG_SYS_MAX_FLASH_SECT 1 -#define CONFIG_SYS_MAX_FLASH_BANKS 1 +#define CONFIG_CMD_SF +#define CONFIG_CMD_SF_TEST +#define CONFIG_CMD_SPI +#define CONFIG_SPI /*----------------------------------------------------------------------- * Environment configuration -- cgit v1.2.1 From 363464f9939633bd0dd5516e486e791bb395c745 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 11 Mar 2013 06:08:12 +0000 Subject: x86: Enable time command for coreboot This command is useful for measuring SPI flash load times and the like. Enable gettime as well to obtain absolute time tick values. Signed-off-by: Simon Glass --- include/configs/coreboot.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/configs/coreboot.h b/include/configs/coreboot.h index 48267563e5..1a763abcce 100644 --- a/include/configs/coreboot.h +++ b/include/configs/coreboot.h @@ -179,6 +179,8 @@ #define CONFIG_CMD_SAVEENV #define CONFIG_CMD_SETGETDCR #define CONFIG_CMD_SOURCE +#define CONFIG_CMD_TIME +#define CONFIG_CMD_GETTIME #define CONFIG_CMD_XIMG #define CONFIG_CMD_SCSI -- cgit v1.2.1 From 1e7133e99e27c4f02998e7eb87fa43424d48152b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 11 Mar 2013 06:08:13 +0000 Subject: sf: Use unsigned type for buffers The verify code is broken on archs with signed char. Fix it. Signed-off-by: Simon Glass --- common/cmd_sf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/cmd_sf.c b/common/cmd_sf.c index b1753587d3..3f0d414954 100644 --- a/common/cmd_sf.c +++ b/common/cmd_sf.c @@ -369,8 +369,8 @@ static void spi_test_next_stage(struct test_info *test) * @param vbuf Verification buffer * @return 0 if ok, -1 on error */ -static int spi_flash_test(struct spi_flash *flash, char *buf, ulong len, - ulong offset, char *vbuf) +static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len, + ulong offset, uint8_t *vbuf) { struct test_info test; int i; @@ -431,9 +431,9 @@ static int do_spi_flash_test(int argc, char * const argv[]) { unsigned long offset; unsigned long len; - char *buf = (char *)CONFIG_SYS_TEXT_BASE; + uint8_t *buf = (uint8_t *)CONFIG_SYS_TEXT_BASE; char *endp; - char *vbuf; + uint8_t *vbuf; int ret; offset = simple_strtoul(argv[1], &endp, 16); -- cgit v1.2.1