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 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 drivers/spi/spi.c (limited to 'drivers') 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; +} -- 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(-) (limited to 'drivers') 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 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers') 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); -- 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(-) (limited to 'drivers') 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 (limited to 'drivers') 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 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(-) (limited to 'drivers') 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(+) (limited to 'drivers') 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 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) (limited to 'drivers') 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); -- cgit v1.2.1