[U-Boot] [PATCH 0/15] Enhance SPI/SPI flash probing, add support for Intel ICH controller

Adding new fields to struct spi_slave and struct spi_flash is painful, because most drivers don't zero the fields they don't use. Anyway it seems better to have the SPI/SPI flash infrastructure provide a simple way of doing this that all drivers can use.
So the first part of this series adds spi_alloc_slave(), for SPI, and spi_flash_alloc() for SPI flash.
Support is added for the Intel ICH SPI controller, possibly the oddest SPI controller in U-Boot. It is designed for use with SPI flash only, and has a number of high-level features which are dedicated to flash. As such it is a bit of a challenge to get it to behave just like a normal U-Boot SPI device.
The ICH driver has two interesting features. Firstly it is impossible to read or write more than 64 bytes at a time! For SPI reading it is possible to hide this within the SPI driver. For SPI writing it unfortunately isn't, since the spi_flash layer has to send an unlock command and a new address for every write. It would be an egregious hack to try to fake this in the driver. So a new property is added to spi_flash to allow the maximum transfer size to be set.
Secondly, the ICH SPI flash can be memory mapped. On a lot of x86 devices this improves performance significantly. For example, the standard driver gets maybe 12Mbps throughput from a 33Mbps dual interface, roughly 20% utilisation. With memory mapping, many platforms can achieve about 40Mbps. To implement memory mapping, a new property is provided in the device tree to set the memory map address, which varies by platform. Some x86 platforms will see a speed increase with memory mapping, some won't. The memory mapping feature only works for reading. When in use, the spi_flash layer bypasses the SPI driver completely, and just copies the flash data from the correct place in the memory map.
Simon Glass (15): fdt: Use sed instead of cpp to pre-process the dtc fdt: Add fdtdec_get_addr_size() to read reg properties spi: Add function to allocate a new SPI slave spi: Use spi_alloc_slave() in each SPI driver sf: Add spi_flash_alloc() to create a new SPI flash struct sf: Use spi_flash_alloc() in each SPI flash driver x86: spi: Add Intel ICH driver spi: Add parameter for maximum write size sf: Respect maximum SPI write size x86: spi: Set maximum write size for ICH sf: Enable FDT-based configuration and memory mapping x86: Move PCI init before SPI init x86: Add FDT SPI node for link x86: Enable SPI flash support for coreboot x86: Enable time command for coreboot
arch/x86/lib/board.c | 8 +- board/chromebook-x86/dts/link.dts | 11 + 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/spi_flash.c | 81 ++++- drivers/mtd/spi/sst.c | 8 +- drivers/mtd/spi/stmicro.c | 8 +- drivers/mtd/spi/winbond.c | 8 +- drivers/spi/Makefile | 4 + 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/cf_qspi.c | 4 +- drivers/spi/cf_spi.c | 4 +- drivers/spi/davinci_spi.c | 4 +- drivers/spi/fsl_espi.c | 4 +- drivers/spi/ich.c | 752 +++++++++++++++++++++++++++++++++++++ drivers/spi/ich.h | 144 +++++++ 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/spi.c | 39 ++ drivers/spi/tegra_spi.c | 4 +- drivers/spi/xilinx_spi.c | 4 +- dts/Makefile | 10 +- include/configs/coreboot.h | 13 +- include/fdtdec.h | 16 + include/spi.h | 44 +++ include/spi_flash.h | 39 ++ lib/fdtdec.c | 27 ++- 41 files changed, 1208 insertions(+), 147 deletions(-) create mode 100644 drivers/spi/ich.c create mode 100644 drivers/spi/ich.h create mode 100644 drivers/spi/spi.c

Include file support in dtc is still not available in common distributions so we need to keep our preprocessing arrangement around for a little longer.
But # is commonly used in FDT files, so use sed instead of cpp for this preprocessing.
Signed-off-by: Simon Glass sjg@chromium.org --- dts/Makefile | 10 ++++------ 1 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/dts/Makefile b/dts/Makefile index 785104e..76f1461 100644 --- a/dts/Makefile +++ b/dts/Makefile @@ -35,11 +35,6 @@ $(if $(CONFIG_ARCH_DEVICE_TREE),,\ $(error Your architecture does not have device tree support enabled. \ Please define CONFIG_ARCH_DEVICE_TREE))
-# We preprocess the device tree file provide a useful define -DTS_CPPFLAGS := -ansi \ - -DARCH_CPU_DTS="$(SRCTREE)/arch/$(ARCH)/dts/$(CONFIG_ARCH_DEVICE_TREE).dtsi" \ - -DBOARD_DTS="$(SRCTREE)/board/$(VENDOR)/$(BOARD)/dts/$(DEVICE_TREE).dts" - all: $(obj).depend $(LIB)
# Use a constant name for this so we can access it from C code. @@ -49,7 +44,10 @@ DT_BIN := $(obj)dt.dtb
$(DT_BIN): $(TOPDIR)/board/$(VENDOR)/dts/$(DEVICE_TREE).dts rc=$$( \ - cat $< | $(CPP) -P $(DTS_CPPFLAGS) - | \ + cat $< \ + | sed '{s#ARCH_CPU_DTS#"$(SRCTREE)/arch/$(ARCH)/dts/$(CONFIG_ARCH_DEVICE_TREE).dtsi"#; \ + s#BOARD_DTS#$(SRCTREE)/board/$(VENDOR)/$(BOARD)/dts/$(DEVICE_TREE).dts#}' | \ + tee dts.tmp | \ { { $(DTC) -R 4 -p 0x1000 -O dtb -o ${DT_BIN} - 2>&1 ; \ echo $$? >&3 ; } | \ grep -v '^DTC: dts->dtb on file' ; \

On 12/26/2012 03:28 PM, Simon Glass wrote:
Include file support in dtc is still not available in common distributions so we need to keep our preprocessing arrangement around for a little longer.
But # is commonly used in FDT files, so use sed instead of cpp for this preprocessing.
This sounds like the wrong approach to me. I'd suggest using what I proposed for the kernel:
cmd_dtc_cpp = $(CPP) $(cpp_flags) -D__DTS__ -x assembler-with-cpp -o $(dtc-tmp) $< ; \ $(objtree)/scripts/dtc/dtc -O dtb -o $@ -b 0 $(DTC_FLAGS) $(dtc-tmp)
The "-x assembler-with-cpp" is what solves the # problem IIRC.

Hi Stephen,
On Thu, Dec 27, 2012 at 4:03 PM, Stephen Warren swarren@wwwdotorg.org wrote:
On 12/26/2012 03:28 PM, Simon Glass wrote:
Include file support in dtc is still not available in common distributions so we need to keep our preprocessing arrangement around for a little longer.
But # is commonly used in FDT files, so use sed instead of cpp for this preprocessing.
This sounds like the wrong approach to me. I'd suggest using what I proposed for the kernel:
cmd_dtc_cpp = $(CPP) $(cpp_flags) -D__DTS__ -x assembler-with-cpp -o $(dtc-tmp) $< ; \ $(objtree)/scripts/dtc/dtc -O dtb -o $@ -b 0 $(DTC_FLAGS) $(dtc-tmp)
The "-x assembler-with-cpp" is what solves the # problem IIRC.
I originally used CPP as an expedient means of converting the ARCH_CPU_DTS symbol until we all have a dtc with include path support.
Are you saying that we want to actually use the CPP on tthe device tree and (presumably) use U-Boot include files within the FDT?
Regards, Simon

On Friday 28 December 2012 09:55:52 Simon Glass wrote:
On Thu, Dec 27, 2012 at 4:03 PM, Stephen Warren wrote:
On 12/26/2012 03:28 PM, Simon Glass wrote:
Include file support in dtc is still not available in common distributions so we need to keep our preprocessing arrangement around for a little longer.
But # is commonly used in FDT files, so use sed instead of cpp for this preprocessing.
This sounds like the wrong approach to me. I'd suggest using what I
proposed for the kernel:
cmd_dtc_cpp = $(CPP) $(cpp_flags) -D__DTS__ -x assembler-with-cpp -o $(dtc-tmp) $< ; \
$(objtree)/scripts/dtc/dtc -O dtb -o $@ -b 0 $(DTC_FLAGS) $(dtc-tmp)
The "-x assembler-with-cpp" is what solves the # problem IIRC.
I originally used CPP as an expedient means of converting the ARCH_CPU_DTS symbol until we all have a dtc with include path support.
Are you saying that we want to actually use the CPP on tthe device tree and (presumably) use U-Boot include files within the FDT?
sounds reasonable to me. we already do it with linker scripts, and if the kernel is doing it, it means we can (possibly) share more. -mike

Hi,
On Fri, Dec 28, 2012 at 8:42 AM, Mike Frysinger vapier@gentoo.org wrote:
On Friday 28 December 2012 09:55:52 Simon Glass wrote:
On Thu, Dec 27, 2012 at 4:03 PM, Stephen Warren wrote:
On 12/26/2012 03:28 PM, Simon Glass wrote:
Include file support in dtc is still not available in common distributions so we need to keep our preprocessing arrangement around for a little longer.
But # is commonly used in FDT files, so use sed instead of cpp for this preprocessing.
This sounds like the wrong approach to me. I'd suggest using what I
proposed for the kernel:
cmd_dtc_cpp = $(CPP) $(cpp_flags) -D__DTS__ -x assembler-with-cpp -o $(dtc-tmp) $< ; \
$(objtree)/scripts/dtc/dtc -O dtb -o $@ -b 0 $(DTC_FLAGS) $(dtc-tmp)
The "-x assembler-with-cpp" is what solves the # problem IIRC.
I originally used CPP as an expedient means of converting the ARCH_CPU_DTS symbol until we all have a dtc with include path support.
Are you saying that we want to actually use the CPP on tthe device tree and (presumably) use U-Boot include files within the FDT?
sounds reasonable to me. we already do it with linker scripts, and if the kernel is doing it, it means we can (possibly) share more.
OK. Stephen, what is the kernel actually doing with the preprocessor? Have you given up on the dtc symbol stuff for now and plan to use CPP instead?
Regards, Simon
-mike

On 12/28/2012 11:07 AM, Simon Glass wrote:
Hi,
On Fri, Dec 28, 2012 at 8:42 AM, Mike Frysinger vapier@gentoo.org wrote:
On Friday 28 December 2012 09:55:52 Simon Glass wrote:
On Thu, Dec 27, 2012 at 4:03 PM, Stephen Warren wrote:
On 12/26/2012 03:28 PM, Simon Glass wrote:
Include file support in dtc is still not available in common distributions so we need to keep our preprocessing arrangement around for a little longer.
But # is commonly used in FDT files, so use sed instead of cpp for this preprocessing.
This sounds like the wrong approach to me. I'd suggest using what I
proposed for the kernel:
cmd_dtc_cpp = $(CPP) $(cpp_flags) -D__DTS__ -x assembler-with-cpp -o $(dtc-tmp) $< ; \
$(objtree)/scripts/dtc/dtc -O dtb -o $@ -b 0 $(DTC_FLAGS) $(dtc-tmp)
The "-x assembler-with-cpp" is what solves the # problem IIRC.
I originally used CPP as an expedient means of converting the ARCH_CPU_DTS symbol until we all have a dtc with include path support.
Are you saying that we want to actually use the CPP on tthe device tree and (presumably) use U-Boot include files within the FDT?
Yes, I'd explicitly like to be able to use C-style header files to define named constants etc.
sounds reasonable to me. we already do it with linker scripts, and if the kernel is doing it, it means we can (possibly) share more.
OK. Stephen, what is the kernel actually doing with the preprocessor? Have you given up on the dtc symbol stuff for now and plan to use CPP instead?
Yes, I've given up on getting any kind of pre-processor or macro language into dtc itself. I haven't managed to get the kernel to accept the logic I quoted above either yet; this has all been a very long and tortuous process. I hope to repost the patch that implements this in the kernel within the next week or so.

Hi Stephen,
On Fri, Dec 28, 2012 at 3:47 PM, Stephen Warren swarren@wwwdotorg.org wrote:
On 12/28/2012 11:07 AM, Simon Glass wrote:
Hi,
On Fri, Dec 28, 2012 at 8:42 AM, Mike Frysinger vapier@gentoo.org wrote:
On Friday 28 December 2012 09:55:52 Simon Glass wrote:
On Thu, Dec 27, 2012 at 4:03 PM, Stephen Warren wrote:
On 12/26/2012 03:28 PM, Simon Glass wrote:
Include file support in dtc is still not available in common distributions so we need to keep our preprocessing arrangement around for a little longer.
But # is commonly used in FDT files, so use sed instead of cpp for this preprocessing.
This sounds like the wrong approach to me. I'd suggest using what I
proposed for the kernel:
cmd_dtc_cpp = $(CPP) $(cpp_flags) -D__DTS__ -x assembler-with-cpp -o $(dtc-tmp) $< ; \
$(objtree)/scripts/dtc/dtc -O dtb -o $@ -b 0 $(DTC_FLAGS) $(dtc-tmp)
The "-x assembler-with-cpp" is what solves the # problem IIRC.
I originally used CPP as an expedient means of converting the ARCH_CPU_DTS symbol until we all have a dtc with include path support.
Are you saying that we want to actually use the CPP on tthe device tree and (presumably) use U-Boot include files within the FDT?
Yes, I'd explicitly like to be able to use C-style header files to define named constants etc.
sounds reasonable to me. we already do it with linker scripts, and if the kernel is doing it, it means we can (possibly) share more.
OK. Stephen, what is the kernel actually doing with the preprocessor? Have you given up on the dtc symbol stuff for now and plan to use CPP instead?
Yes, I've given up on getting any kind of pre-processor or macro language into dtc itself. I haven't managed to get the kernel to accept the logic I quoted above either yet; this has all been a very long and tortuous process. I hope to repost the patch that implements this in the kernel within the next week or so.
OK good luck with the cat-herding. I will update this patch along the lines you describe above and resend. I hope we are not opening a can of worms using the full power of the pre-processor, but clearly we are not getting anywhere with dtc, so this is the only reasonable option.
Regards, Simon

It is common to have a "reg = <address size>" property in the FDT. Add a function to handle this, similar to the existing fdtdec_get_addr();
Signed-off-by: Simon Glass sjg@chromium.org --- include/fdtdec.h | 15 +++++++++++++++ lib/fdtdec.c | 26 +++++++++++++++++++++----- 2 files changed, 36 insertions(+), 5 deletions(-)
diff --git a/include/fdtdec.h b/include/fdtdec.h index 70d0e97..570d3ac 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) @@ -186,6 +188,19 @@ 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 * returned. diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 6dba438..d0bc848 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -54,25 +54,41 @@ 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) || + 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); + fdt_addr_t addr = fdt_addr_to_cpu(*cell); + 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) {

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 sjg@chromium.org --- drivers/spi/Makefile | 3 +++ drivers/spi/spi.c | 39 +++++++++++++++++++++++++++++++++++++++ include/spi.h | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 0 deletions(-) create mode 100644 drivers/spi/spi.c
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 824d357..4e8de5d 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 0000000..cb36c5e --- /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/include/spi.h b/include/spi.h index 60e85db..ebc9652 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. *

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 sjg@chromium.org --- 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/cf_qspi.c | 4 +--- drivers/spi/cf_spi.c | 4 +--- drivers/spi/davinci_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_spi.c | 4 +--- drivers/spi/xilinx_spi.c | 4 +--- 20 files changed, 33 insertions(+), 74 deletions(-)
diff --git a/drivers/spi/altera_spi.c b/drivers/spi/altera_spi.c index 138d6f4..b53607a 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 fdde139..c56377b 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 7384c9c..afdbe05 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 c7a51f7..0bca22f 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) diff --git a/drivers/spi/bfin_spi.c b/drivers/spi/bfin_spi.c index e080bec..ab2e8b9 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/cf_qspi.c b/drivers/spi/cf_qspi.c index 72dd1a5..a37ac4e 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 a883da9..afe7917 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 13aca52..74792af 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/fsl_espi.c b/drivers/spi/fsl_espi.c index eb99e90..28609ee 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 a7cda75..5a226e3 100644 --- a/drivers/spi/kirkwood_spi.c +++ b/drivers/spi/kirkwood_spi.c @@ -46,13 +46,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 3e96b3f..4b50bca 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 4e46041..6b0e3b4 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 859c43f..d792d8d 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 42e4c99..fb8aa08 100644 --- a/drivers/spi/mxs_spi.c +++ b/drivers/spi/mxs_spi.c @@ -89,7 +89,7 @@ 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;
@@ -98,8 +98,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
addr = MXS_SSP0_BASE + (bus * MXS_SPI_PORT_OFFSET);
- mxs_slave->slave.bus = bus; - mxs_slave->slave.cs = cs; mxs_slave->max_khz = max_hz / 1000; mxs_slave->mode = mode; mxs_slave->regs = (struct mxs_ssp_regs *)addr; diff --git a/drivers/spi/oc_tiny_spi.c b/drivers/spi/oc_tiny_spi.c index fc01fb8..6f7b1ed 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 6791a7e..cb6be44 100644 --- a/drivers/spi/omap3_spi.c +++ b/drivers/spi/omap3_spi.c @@ -66,12 +66,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) @@ -84,21 +79,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: @@ -106,7 +101,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)) || @@ -116,19 +110,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 e944b23..744afe3 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 13df8cb..a1b84b6 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_spi.c b/drivers/spi/tegra_spi.c index 9bb34e2..3b9f293 100644 --- a/drivers/spi/tegra_spi.c +++ b/drivers/spi/tegra_spi.c @@ -77,13 +77,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->freq = max_hz; spi->regs = (struct spi_tegra *)NV_PA_SPI_BASE; spi->mode = mode; diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 52a4134..9798831 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;

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 sjg@chromium.org --- drivers/mtd/spi/spi_flash.c | 25 +++++++++++++++++++++++++ include/spi_flash.h | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 0 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 00aece9..17f3d3c 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 9da9062..030d49c 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);

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 sjg@chromium.org --- 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 006f6d5..6a92c4b 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 691ed4e..b16e7ab 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 c97a39d..036c30d 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 0999781..5299a6d 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 32b76e0..afd6a8c 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 ced4f24..95f5490 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 30b626a..44fc8aa 100644 --- a/drivers/mtd/spi/stmicro.c +++ b/drivers/mtd/spi/stmicro.c @@ -146,18 +146,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 f6aab3d..f112ae1 100644 --- a/drivers/mtd/spi/winbond.c +++ b/drivers/mtd/spi/winbond.c @@ -87,18 +87,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;

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 bhthompson@chromium.org Signed-off-by: Duncan Laurie dlaurie@chromium.org Signed-off-by: Bill Richardson wfrichar@chromium.org Signed-off-by: Vadim Bendebury vbendeb@chromium.org Signed-off-by: Gabe Black gabeblack@chromium.org Signed-off-by: Simon Glass sjg@chromium.org --- drivers/spi/Makefile | 1 + drivers/spi/ich.c | 747 ++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/ich.h | 144 ++++++++++ 3 files changed, 892 insertions(+), 0 deletions(-) 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 4e8de5d..cfdc5d3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -38,6 +38,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 0000000..31f9482 --- /dev/null +++ b/drivers/spi/ich.c @@ -0,0 +1,747 @@ +/* + * 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 */ +}; + +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) +{ + const uint8_t *bvalue = value; + uint8_t *bdest = dest; + + while (size >= 4) { + ich_writel(*(const uint32_t *)bvalue, bdest); + bdest += 4; bvalue += 4; size -= 4; + } + while (size) { + ich_writeb(*bvalue, bdest); + bdest++; bvalue++; size--; + } +} + +static void read_reg(const void *src, void *value, uint32_t size) +{ + const uint8_t *bsrc = src; + uint8_t *bvalue = value; + + while (size >= 4) { + *(uint32_t *)bvalue = ich_readl(bsrc); + bsrc += 4; bvalue += 4; size -= 4; + } + while (size) { + *bvalue = ich_readb(bsrc); + bsrc++; bvalue++; 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; +} + +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); + + 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) { + 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 0000000..f90d859 --- /dev/null +++ b/drivers/spi/ich.h @@ -0,0 +1,144 @@ +/* + * 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 */ +}; +

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 sjg@chromium.org --- include/spi.h | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/include/spi.h b/include/spi.h index ebc9652..3fe2e1e 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; };
/*-----------------------------------------------------------------------

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 sjg@chromium.org --- drivers/mtd/spi/spi_flash.c | 10 ++++++++-- 1 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 17f3d3c..b82011d 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",

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 sjg@chromium.org --- drivers/spi/ich.c | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c index 31f9482..17defa4 100644 --- a/drivers/spi/ich.c +++ b/drivers/spi/ich.c @@ -166,6 +166,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;

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 sjg@chromium.org --- 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 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index b82011d..111185a 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 */ @@ -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 570d3ac..bfd1dda 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -72,6 +72,7 @@ enum fdt_compat_id { COMPAT_NVIDIA_TEGRA20_NAND, /* Tegra2 NAND controller */ COMPAT_NVIDIA_TEGRA20_PWM, /* Tegra 2 PWM controller */ COMPAT_NVIDIA_TEGRA20_DC, /* Tegra 2 Display controller */ + COMPAT_GENERIC_SPI_FLASH, /* Generic SPI Flash chip */
COMPAT_COUNT, }; diff --git a/include/spi_flash.h b/include/spi_flash.h index 030d49c..3b6a44e 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 d0bc848..55305b4 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -45,6 +45,7 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(NVIDIA_TEGRA20_NAND, "nvidia,tegra20-nand"), COMPAT(NVIDIA_TEGRA20_PWM, "nvidia,tegra20-pwm"), COMPAT(NVIDIA_TEGRA20_DC, "nvidia,tegra20-dc"), + COMPAT(GENERIC_SPI_FLASH, "spi-flash"), };
const char *fdtdec_get_compatible(enum fdt_compat_id id)

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 sjg@chromium.org --- arch/x86/lib/board.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/x86/lib/board.c b/arch/x86/lib/board.c index 555301a..c2407e4 100644 --- a/arch/x86/lib/board.c +++ b/arch/x86/lib/board.c @@ -163,13 +163,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,

Add a memory-mapped 8GB SPI chip.
Signed-off-by: Simon Glass sjg@chromium.org --- board/chromebook-x86/dts/link.dts | 11 +++++++++++ 1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/board/chromebook-x86/dts/link.dts b/board/chromebook-x86/dts/link.dts index ae8217d..d0738cb 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>; + }; + }; };

Turn on SPI flash support and related commands.
Signed-off-by: Simon Glass sjg@chromium.org --- include/configs/coreboot.h | 12 +++++++++--- 1 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/include/configs/coreboot.h b/include/configs/coreboot.h index fd383ff..ea92ffe 100644 --- a/include/configs/coreboot.h +++ b/include/configs/coreboot.h @@ -258,10 +258,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

This command is useful for measuring SPI flash load times and the like.
Signed-off-by: Simon Glass sjg@chromium.org --- include/configs/coreboot.h | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/include/configs/coreboot.h b/include/configs/coreboot.h index ea92ffe..4b6eb73 100644 --- a/include/configs/coreboot.h +++ b/include/configs/coreboot.h @@ -180,6 +180,7 @@ #define CONFIG_CMD_SAVEENV #define CONFIG_CMD_SETGETDCR #define CONFIG_CMD_SOURCE +#define CONFIG_CMD_TIME #define CONFIG_CMD_XIMG #define CONFIG_CMD_SCSI

Hi,
On Wed, Dec 26, 2012 at 2:28 PM, Simon Glass sjg@chromium.org wrote:
Adding new fields to struct spi_slave and struct spi_flash is painful, because most drivers don't zero the fields they don't use. Anyway it seems better to have the SPI/SPI flash infrastructure provide a simple way of doing this that all drivers can use.
So the first part of this series adds spi_alloc_slave(), for SPI, and spi_flash_alloc() for SPI flash.
Support is added for the Intel ICH SPI controller, possibly the oddest SPI controller in U-Boot. It is designed for use with SPI flash only, and has a number of high-level features which are dedicated to flash. As such it is a bit of a challenge to get it to behave just like a normal U-Boot SPI device.
The ICH driver has two interesting features. Firstly it is impossible to read or write more than 64 bytes at a time! For SPI reading it is possible to hide this within the SPI driver. For SPI writing it unfortunately isn't, since the spi_flash layer has to send an unlock command and a new address for every write. It would be an egregious hack to try to fake this in the driver. So a new property is added to spi_flash to allow the maximum transfer size to be set.
Secondly, the ICH SPI flash can be memory mapped. On a lot of x86 devices this improves performance significantly. For example, the standard driver gets maybe 12Mbps throughput from a 33Mbps dual interface, roughly 20% utilisation. With memory mapping, many platforms can achieve about 40Mbps. To implement memory mapping, a new property is provided in the device tree to set the memory map address, which varies by platform. Some x86 platforms will see a speed increase with memory mapping, some won't. The memory mapping feature only works for reading. When in use, the spi_flash layer bypasses the SPI driver completely, and just copies the flash data from the correct place in the memory map.
This series includes some generic changes to the SPI and SPI flash layers.
It also includes an x86 SPI driver for Intel.
Are there any comments, particularlly on the addition of spi_alloc_aloc() and spi_flsah_alloc()? This affects all SPI flash drivers.
The first patch needs to be dropped since it seems we are sticking with cpp.
Simon Glass (15): fdt: Use sed instead of cpp to pre-process the dtc fdt: Add fdtdec_get_addr_size() to read reg properties spi: Add function to allocate a new SPI slave spi: Use spi_alloc_slave() in each SPI driver sf: Add spi_flash_alloc() to create a new SPI flash struct sf: Use spi_flash_alloc() in each SPI flash driver x86: spi: Add Intel ICH driver spi: Add parameter for maximum write size sf: Respect maximum SPI write size x86: spi: Set maximum write size for ICH sf: Enable FDT-based configuration and memory mapping x86: Move PCI init before SPI init x86: Add FDT SPI node for link x86: Enable SPI flash support for coreboot x86: Enable time command for coreboot
arch/x86/lib/board.c | 8 +- board/chromebook-x86/dts/link.dts | 11 + 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/spi_flash.c | 81 ++++- drivers/mtd/spi/sst.c | 8 +- drivers/mtd/spi/stmicro.c | 8 +- drivers/mtd/spi/winbond.c | 8 +- drivers/spi/Makefile | 4 + 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/cf_qspi.c | 4 +- drivers/spi/cf_spi.c | 4 +- drivers/spi/davinci_spi.c | 4 +- drivers/spi/fsl_espi.c | 4 +- drivers/spi/ich.c | 752 +++++++++++++++++++++++++++++++++++++ drivers/spi/ich.h | 144 +++++++ 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/spi.c | 39 ++ drivers/spi/tegra_spi.c | 4 +- drivers/spi/xilinx_spi.c | 4 +- dts/Makefile | 10 +- include/configs/coreboot.h | 13 +- include/fdtdec.h | 16 + include/spi.h | 44 +++ include/spi_flash.h | 39 ++ lib/fdtdec.c | 27 ++- 41 files changed, 1208 insertions(+), 147 deletions(-) create mode 100644 drivers/spi/ich.c create mode 100644 drivers/spi/ich.h create mode 100644 drivers/spi/spi.c
-- 1.7.7.3
Regards, Simon
participants (3)
-
Mike Frysinger
-
Simon Glass
-
Stephen Warren