From 48263504c8d501678acaa90c075f3f7cda17c316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Tue, 23 Jan 2018 17:14:55 +0100 Subject: wait_bit: use wait_for_bit_le32 and remove wait_for_bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wait_for_bit callers use the 32 bit LE version Signed-off-by: Álvaro Fernández Rojas Reviewed-by: Daniel Schwierzeck Reviewed-by: Jagan Teki --- drivers/spi/atmel_spi.c | 4 ++-- drivers/spi/cadence_qspi_apb.c | 14 +++++++------- drivers/spi/fsl_qspi.c | 20 ++++++++++---------- drivers/spi/mvebu_a3700_spi.c | 20 +++++++++++--------- 4 files changed, 30 insertions(+), 28 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 228e714..8010ab4 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -394,8 +394,8 @@ out: * Wait until the transfer is completely done before * we deactivate CS. */ - wait_for_bit(__func__, ®_base->sr, - ATMEL_SPI_SR_TXEMPTY, true, 1000, false); + wait_for_bit_le32(®_base->sr, + ATMEL_SPI_SR_TXEMPTY, true, 1000, false); atmel_spi_cs_deactivate(dev); } diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index e02f221..dca3fdf 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -675,8 +675,8 @@ int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, } /* Check indirect done status */ - ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTRD, - CQSPI_REG_INDIRECTRD_DONE, 1, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_INDIRECTRD, + CQSPI_REG_INDIRECTRD_DONE, 1, 10, 0); if (ret) { printf("Indirect read completion error (%i)\n", ret); goto failrd; @@ -762,9 +762,9 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, bb_txbuf + rounddown(write_bytes, 4), write_bytes % 4); - ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_SDRAMLEVEL, - CQSPI_REG_SDRAMLEVEL_WR_MASK << - CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_SDRAMLEVEL, + CQSPI_REG_SDRAMLEVEL_WR_MASK << + CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0); if (ret) { printf("Indirect write timed out (%i)\n", ret); goto failwr; @@ -775,8 +775,8 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, } /* Check indirect done status */ - ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTWR, - CQSPI_REG_INDIRECTWR_DONE, 1, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_INDIRECTWR, + CQSPI_REG_INDIRECTWR_DONE, 1, 10, 0); if (ret) { printf("Indirect write completion error (%i)\n", ret); goto failwr; diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index 0f3f7d9..eed52c1 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -1011,11 +1011,11 @@ static int fsl_qspi_probe(struct udevice *bus) priv->num_chipselect = plat->num_chipselect; /* make sure controller is not busy anywhere */ - ret = wait_for_bit(__func__, &priv->regs->sr, - QSPI_SR_BUSY_MASK | - QSPI_SR_AHB_ACC_MASK | - QSPI_SR_IP_ACC_MASK, - false, 100, false); + ret = wait_for_bit_le32(&priv->regs->sr, + QSPI_SR_BUSY_MASK | + QSPI_SR_AHB_ACC_MASK | + QSPI_SR_IP_ACC_MASK, + false, 100, false); if (ret) { debug("ERROR : The controller is busy\n"); @@ -1173,11 +1173,11 @@ static int fsl_qspi_claim_bus(struct udevice *dev) priv = dev_get_priv(bus); /* make sure controller is not busy anywhere */ - ret = wait_for_bit(__func__, &priv->regs->sr, - QSPI_SR_BUSY_MASK | - QSPI_SR_AHB_ACC_MASK | - QSPI_SR_IP_ACC_MASK, - false, 100, false); + ret = wait_for_bit_le32(&priv->regs->sr, + QSPI_SR_BUSY_MASK | + QSPI_SR_AHB_ACC_MASK | + QSPI_SR_IP_ACC_MASK, + false, 100, false); if (ret) { debug("ERROR : The controller is busy\n"); diff --git a/drivers/spi/mvebu_a3700_spi.c b/drivers/spi/mvebu_a3700_spi.c index ec49073..d1708a8 100644 --- a/drivers/spi/mvebu_a3700_spi.c +++ b/drivers/spi/mvebu_a3700_spi.c @@ -95,8 +95,9 @@ static int spi_legacy_shift_byte(struct spi_reg *reg, unsigned int bytelen, din_8 = din; while (bytelen) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true,100, false); if (ret) return ret; @@ -109,9 +110,9 @@ static int spi_legacy_shift_byte(struct spi_reg *reg, unsigned int bytelen, writel(pending_dout, ®->dout); if (din) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, - true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true, 100, false); if (ret) return ret; @@ -160,8 +161,9 @@ static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen, /* Deactivate CS */ if (flags & SPI_XFER_END) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true, 100, false); if (ret) return ret; @@ -231,8 +233,8 @@ static int mvebu_spi_probe(struct udevice *bus) /* Flush read/write FIFO */ data = readl(®->cfg); writel(data | MVEBU_SPI_A3700_FIFO_FLUSH, ®->cfg); - ret = wait_for_bit(__func__, ®->cfg, MVEBU_SPI_A3700_FIFO_FLUSH, - false, 1000, false); + ret = wait_for_bit_le32(®->cfg, MVEBU_SPI_A3700_FIFO_FLUSH, + false, 1000, false); if (ret) return ret; -- cgit v1.1 From 5ac07d2969e7f1ea2582f97ccacbe9ad9c9d62fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Tue, 23 Jan 2018 17:14:58 +0100 Subject: dm: spi: add BCM63xx SPI driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Signed-off-by: Álvaro Fernández Rojas Reviewed-by: Simon Glass Reviewed-by: Daniel Schwierzeck Reviewed-by: Jagan Teki --- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 442 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 494639f..ebc71c2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321. +config BCM63XX_SPI + bool "BCM6348 SPI driver" + depends on ARCH_BMIPS + help + Enable the BCM6348/BCM6358 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding these Broadcom + SPI cores. + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e3184db..5770b3f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -18,6 +18,7 @@ endif obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000..f0df687 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas + * + * Derived from linux/drivers/spi/spi-bcm63xx.c: + * Copyright (C) 2009-2012 Florian Fainelli + * Copyright (C) 2010 Tanguy Bouzeloc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* BCM6348 SPI core */ +#define SPI_6348_CLK 0x06 +#define SPI_6348_CMD 0x00 +#define SPI_6348_CTL 0x40 +#define SPI_6348_CTL_SHIFT 6 +#define SPI_6348_FILL 0x07 +#define SPI_6348_IR_MASK 0x04 +#define SPI_6348_IR_STAT 0x02 +#define SPI_6348_RX 0x80 +#define SPI_6348_RX_SIZE 0x3f +#define SPI_6348_TX 0x41 +#define SPI_6348_TX_SIZE 0x3f + +/* BCM6358 SPI core */ +#define SPI_6358_CLK 0x706 +#define SPI_6358_CMD 0x700 +#define SPI_6358_CTL 0x000 +#define SPI_6358_CTL_SHIFT 14 +#define SPI_6358_FILL 0x707 +#define SPI_6358_IR_MASK 0x702 +#define SPI_6358_IR_STAT 0x704 +#define SPI_6358_RX 0x400 +#define SPI_6358_RX_SIZE 0x220 +#define SPI_6358_TX 0x002 +#define SPI_6358_TX_SIZE 0x21e + +/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT) + +/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT) + +/* SPI Control register */ +#define SPI_CTL_TYPE_FD_RW 0 +#define SPI_CTL_TYPE_HD_W 1 +#define SPI_CTL_TYPE_HD_R 2 + +/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\ + SPI_IR_RXOVER_MASK |\ + SPI_IR_TXUNDER_MASK |\ + SPI_IR_TXOVER_MASK |\ + SPI_IR_RXUNDER_MASK) + +enum bcm63xx_regs_spi { + SPI_CLK, + SPI_CMD, + SPI_CTL, + SPI_CTL_SHIFT, + SPI_FILL, + SPI_IR_MASK, + SPI_IR_STAT, + SPI_RX, + SPI_RX_SIZE, + SPI_TX, + SPI_TX_SIZE, +}; + +struct bcm63xx_spi_priv { + const unsigned long *regs; + void __iomem *base; + size_t tx_bytes; + uint8_t num_cs; +}; + +#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = { + { 25000000, SPI_CLK_25MHZ }, + { 20000000, SPI_CLK_20MHZ }, + { 12500000, SPI_CLK_12_50MHZ }, + { 6250000, SPI_CLK_6_250MHZ }, + { 3125000, SPI_CLK_3_125MHZ }, + { 1563000, SPI_CLK_1_563MHZ }, + { 781000, SPI_CLK_0_781MHZ }, + { 391000, SPI_CLK_0_391MHZ } +}; + +static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + + if (cs >= priv->num_cs) { + printf("no cs %u\n", cs); + return -ENODEV; + } + + return 0; +} + +static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const unsigned long *regs = priv->regs; + + if (mode & SPI_LSB_FIRST) + setbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); + else + clrbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); + + return 0; +} + +static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const unsigned long *regs = priv->regs; + uint8_t clk_cfg; + int i; + + /* default to lowest clock configuration */ + clk_cfg = SPI_CLK_0_391MHZ; + + /* find the closest clock configuration */ + for (i = 0; i < SPI_CLK_CNT; i++) { + if (speed >= bcm63xx_spi_freq_table[i][0]) { + clk_cfg = bcm63xx_spi_freq_table[i][1]; + break; + } + } + + /* write clock configuration */ + clrsetbits_8(priv->base + regs[SPI_CLK], + SPI_CLK_SSOFF_MASK | SPI_CLK_MASK, + clk_cfg | SPI_CLK_SSOFF_2); + + return 0; +} + +/* + * BCM63xx SPI driver doesn't allow keeping CS active between transfers since + * they are HW controlled. + * However, it provides a mechanism to prepend write transfers prior to read + * transfers (with a maximum prepend of 15 bytes), which is usually enough for + * SPI-connected flashes since reading requires prepending a write transfer of + * 5 bytes. + * + * This implementation takes advantage of the prepend mechanism and combines + * multiple transfers into a single one where possible (single/multiple write + * transfer(s) followed by a final read/write transfer). + * However, it's not possible to buffer reads, which means that read transfers + * should always be done as the final ones. + * On the other hand, take into account that combining write transfers into + * a single one is just buffering and doesn't require prepend mechanism. + */ +static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const unsigned long *regs = priv->regs; + size_t data_bytes = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + /* clear prepends */ + priv->tx_bytes = 0; + + /* initialize hardware */ + writeb_be(0, priv->base + regs[SPI_IR_MASK]); + } + + if (din) { + /* buffering reads not possible since cs is hw controlled */ + if (!(flags & SPI_XFER_END)) { + printf("unable to buffer reads\n"); + return -EINVAL; + } + + /* check rx size */ + if (data_bytes > regs[SPI_RX_SIZE]) { + printf("max rx bytes exceeded\n"); + return -EMSGSIZE; + } + } + + if (dout) { + /* check tx size */ + if (priv->tx_bytes + data_bytes > regs[SPI_TX_SIZE]) { + printf("max tx bytes exceeded\n"); + return -EMSGSIZE; + } + + /* copy tx data */ + memcpy_toio(priv->base + regs[SPI_TX] + priv->tx_bytes, + dout, data_bytes); + priv->tx_bytes += data_bytes; + } + + if (flags & SPI_XFER_END) { + struct dm_spi_slave_platdata *plat = + dev_get_parent_platdata(dev); + uint16_t val, cmd; + int ret; + + /* determine control config */ + if (dout && !din) { + /* buffered write transfers */ + val = priv->tx_bytes; + val |= (SPI_CTL_TYPE_HD_W << regs[SPI_CTL_SHIFT]); + priv->tx_bytes = 0; + } else { + if (dout && din && (flags & SPI_XFER_ONCE)) { + /* full duplex read/write */ + val = data_bytes; + val |= (SPI_CTL_TYPE_FD_RW << + regs[SPI_CTL_SHIFT]); + priv->tx_bytes = 0; + } else { + /* prepended write transfer */ + val = data_bytes; + val |= (SPI_CTL_TYPE_HD_R << + regs[SPI_CTL_SHIFT]); + if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) { + printf("max prepend bytes exceeded\n"); + return -EMSGSIZE; + } + } + } + + if (regs[SPI_CTL_SHIFT] >= 8) + writew_be(val, priv->base + regs[SPI_CTL]); + else + writeb_be(val, priv->base + regs[SPI_CTL]); + + /* clear interrupts */ + writeb_be(SPI_IR_CLEAR_MASK, priv->base + regs[SPI_IR_STAT]); + + /* issue the transfer */ + cmd = SPI_CMD_OP_START; + cmd |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK; + cmd |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT); + if (plat->mode & SPI_3WIRE) + cmd |= SPI_CMD_3WIRE_MASK; + writew_be(cmd, priv->base + regs[SPI_CMD]); + + /* enable interrupts */ + writeb_be(SPI_IR_DONE_MASK, priv->base + regs[SPI_IR_MASK]); + + ret = wait_for_bit_8(priv->base + regs[SPI_IR_STAT], + SPI_IR_DONE_MASK, true, 1000, false); + if (ret) { + printf("interrupt timeout\n"); + return ret; + } + + /* copy rx data */ + if (din) + memcpy_fromio(din, priv->base + regs[SPI_RX], + data_bytes); + } + + return 0; +} + +static const struct dm_spi_ops bcm63xx_spi_ops = { + .cs_info = bcm63xx_spi_cs_info, + .set_mode = bcm63xx_spi_set_mode, + .set_speed = bcm63xx_spi_set_speed, + .xfer = bcm63xx_spi_xfer, +}; + +static const unsigned long bcm6348_spi_regs[] = { + [SPI_CLK] = SPI_6348_CLK, + [SPI_CMD] = SPI_6348_CMD, + [SPI_CTL] = SPI_6348_CTL, + [SPI_CTL_SHIFT] = SPI_6348_CTL_SHIFT, + [SPI_FILL] = SPI_6348_FILL, + [SPI_IR_MASK] = SPI_6348_IR_MASK, + [SPI_IR_STAT] = SPI_6348_IR_STAT, + [SPI_RX] = SPI_6348_RX, + [SPI_RX_SIZE] = SPI_6348_RX_SIZE, + [SPI_TX] = SPI_6348_TX, + [SPI_TX_SIZE] = SPI_6348_TX_SIZE, +}; + +static const unsigned long bcm6358_spi_regs[] = { + [SPI_CLK] = SPI_6358_CLK, + [SPI_CMD] = SPI_6358_CMD, + [SPI_CTL] = SPI_6358_CTL, + [SPI_CTL_SHIFT] = SPI_6358_CTL_SHIFT, + [SPI_FILL] = SPI_6358_FILL, + [SPI_IR_MASK] = SPI_6358_IR_MASK, + [SPI_IR_STAT] = SPI_6358_IR_STAT, + [SPI_RX] = SPI_6358_RX, + [SPI_RX_SIZE] = SPI_6358_RX_SIZE, + [SPI_TX] = SPI_6358_TX, + [SPI_TX_SIZE] = SPI_6358_TX_SIZE, +}; + +static const struct udevice_id bcm63xx_spi_ids[] = { + { + .compatible = "brcm,bcm6348-spi", + .data = (ulong)&bcm6348_spi_regs, + }, { + .compatible = "brcm,bcm6358-spi", + .data = (ulong)&bcm6358_spi_regs, + }, { /* sentinel */ } +}; + +static int bcm63xx_spi_child_pre_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const unsigned long *regs = priv->regs; + struct spi_slave *slave = dev_get_parent_priv(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); + + /* check cs */ + if (plat->cs >= priv->num_cs) { + printf("no cs %u\n", plat->cs); + return -ENODEV; + } + + /* max read/write sizes */ + slave->max_read_size = regs[SPI_RX_SIZE]; + slave->max_write_size = regs[SPI_TX_SIZE]; + + return 0; +} + +static int bcm63xx_spi_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev); + const unsigned long *regs = + (const unsigned long *)dev_get_driver_data(dev); + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = devfdt_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = regs; + priv->base = ioremap(addr, size); + priv->num_cs = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "num-cs", 8); + + /* enable clock */ + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + + ret = clk_enable(&clk); + if (ret < 0) + return ret; + + ret = clk_free(&clk); + if (ret < 0) + return ret; + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + + ret = reset_deassert(&rst_ctl); + if (ret < 0) + return ret; + + ret = reset_free(&rst_ctl); + if (ret < 0) + return ret; + + /* initialize hardware */ + writeb_be(0, priv->base + regs[SPI_IR_MASK]); + + /* set fill register */ + writeb_be(0xff, priv->base + regs[SPI_FILL]); + + return 0; +} + +U_BOOT_DRIVER(bcm63xx_spi) = { + .name = "bcm63xx_spi", + .id = UCLASS_SPI, + .of_match = bcm63xx_spi_ids, + .ops = &bcm63xx_spi_ops, + .priv_auto_alloc_size = sizeof(struct bcm63xx_spi_priv), + .child_pre_probe = bcm63xx_spi_child_pre_probe, + .probe = bcm63xx_spi_probe, +}; -- cgit v1.1 From 29cc4368ad4b8d67ae457681e9249e2008d6fee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Sat, 20 Jan 2018 02:13:38 +0100 Subject: dm: spi: add BCM63xx HSSPI driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver is a simplified version of linux/drivers/spi/spi-bcm63xx-hsspi.c Signed-off-by: Álvaro Fernández Rojas Reviewed-by: Simon Glass Reviewed-by: Daniel Schwierzeck Reviewed-by: Jagan Teki --- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_hsspi.c | 414 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 423 insertions(+) create mode 100644 drivers/spi/bcm63xx_hsspi.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ebc71c2..28ddcf8 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321. +config BCM63XX_HSSPI + bool "BCM63XX HSSPI driver" + depends on ARCH_BMIPS + help + Enable the BCM6328 HSSPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding this Broadcom + SPI core. + config BCM63XX_SPI bool "BCM6348 SPI driver" depends on ARCH_BMIPS diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 5770b3f..4b6000f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -18,6 +18,7 @@ endif obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_HSSPI) += bcm63xx_hsspi.o obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o diff --git a/drivers/spi/bcm63xx_hsspi.c b/drivers/spi/bcm63xx_hsspi.c new file mode 100644 index 0000000..3393166 --- /dev/null +++ b/drivers/spi/bcm63xx_hsspi.c @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas + * + * Derived from linux/drivers/spi/spi-bcm63xx-hsspi.c: + * Copyright (C) 2000-2010 Broadcom Corporation + * Copyright (C) 2012-2013 Jonas Gorski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define HSSPI_PP 0 + +#define SPI_MAX_SYNC_CLOCK 30000000 + +/* SPI Control register */ +#define SPI_CTL_REG 0x000 +#define SPI_CTL_CS_POL_SHIFT 0 +#define SPI_CTL_CS_POL_MASK (0xff << SPI_CTL_CS_POL_SHIFT) +#define SPI_CTL_CLK_GATE_SHIFT 16 +#define SPI_CTL_CLK_GATE_MASK (1 << SPI_CTL_CLK_GATE_SHIFT) +#define SPI_CTL_CLK_POL_SHIFT 17 +#define SPI_CTL_CLK_POL_MASK (1 << SPI_CTL_CLK_POL_SHIFT) + +/* SPI Interrupts registers */ +#define SPI_IR_STAT_REG 0x008 +#define SPI_IR_ST_MASK_REG 0x00c +#define SPI_IR_MASK_REG 0x010 + +#define SPI_IR_CLEAR_ALL 0xff001f1f + +/* SPI Ping-Pong Command registers */ +#define SPI_CMD_REG (0x080 + (0x40 * (HSSPI_PP)) + 0x00) +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x1 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_PFL_SHIFT 8 +#define SPI_CMD_PFL_MASK (0x7 << SPI_CMD_PFL_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 12 +#define SPI_CMD_SLAVE_MASK (0x7 << SPI_CMD_SLAVE_SHIFT) + +/* SPI Ping-Pong Status registers */ +#define SPI_STAT_REG (0x080 + (0x40 * (HSSPI_PP)) + 0x04) +#define SPI_STAT_SRCBUSY_SHIFT 1 +#define SPI_STAT_SRCBUSY_MASK (1 << SPI_STAT_SRCBUSY_SHIFT) + +/* SPI Profile Clock registers */ +#define SPI_PFL_CLK_REG(x) (0x100 + (0x20 * (x)) + 0x00) +#define SPI_PFL_CLK_FREQ_SHIFT 0 +#define SPI_PFL_CLK_FREQ_MASK (0x3fff << SPI_PFL_CLK_FREQ_SHIFT) +#define SPI_PFL_CLK_RSTLOOP_SHIFT 15 +#define SPI_PFL_CLK_RSTLOOP_MASK (1 << SPI_PFL_CLK_RSTLOOP_SHIFT) + +/* SPI Profile Signal registers */ +#define SPI_PFL_SIG_REG(x) (0x100 + (0x20 * (x)) + 0x04) +#define SPI_PFL_SIG_LATCHRIS_SHIFT 12 +#define SPI_PFL_SIG_LATCHRIS_MASK (1 << SPI_PFL_SIG_LATCHRIS_SHIFT) +#define SPI_PFL_SIG_LAUNCHRIS_SHIFT 13 +#define SPI_PFL_SIG_LAUNCHRIS_MASK (1 << SPI_PFL_SIG_LAUNCHRIS_SHIFT) +#define SPI_PFL_SIG_ASYNCIN_SHIFT 16 +#define SPI_PFL_SIG_ASYNCIN_MASK (1 << SPI_PFL_SIG_ASYNCIN_SHIFT) + +/* SPI Profile Mode registers */ +#define SPI_PFL_MODE_REG(x) (0x100 + (0x20 * (x)) + 0x08) +#define SPI_PFL_MODE_FILL_SHIFT 0 +#define SPI_PFL_MODE_FILL_MASK (0xff << SPI_PFL_MODE_FILL_SHIFT) +#define SPI_PFL_MODE_MDRDSZ_SHIFT 16 +#define SPI_PFL_MODE_MDRDSZ_MASK (1 << SPI_PFL_MODE_MDRDSZ_SHIFT) +#define SPI_PFL_MODE_MDWRSZ_SHIFT 18 +#define SPI_PFL_MODE_MDWRSZ_MASK (1 << SPI_PFL_MODE_MDWRSZ_SHIFT) +#define SPI_PFL_MODE_3WIRE_SHIFT 20 +#define SPI_PFL_MODE_3WIRE_MASK (1 << SPI_PFL_MODE_3WIRE_SHIFT) + +/* SPI Ping-Pong FIFO registers */ +#define HSSPI_FIFO_SIZE 0x200 +#define HSSPI_FIFO_BASE (0x200 + \ + (HSSPI_FIFO_SIZE * HSSPI_PP)) + +/* SPI Ping-Pong FIFO OP register */ +#define HSSPI_FIFO_OP_SIZE 0x2 +#define HSSPI_FIFO_OP_REG (HSSPI_FIFO_BASE + 0x00) +#define HSSPI_FIFO_OP_BYTES_SHIFT 0 +#define HSSPI_FIFO_OP_BYTES_MASK (0x3ff << HSSPI_FIFO_OP_BYTES_SHIFT) +#define HSSPI_FIFO_OP_MBIT_SHIFT 11 +#define HSSPI_FIFO_OP_MBIT_MASK (1 << HSSPI_FIFO_OP_MBIT_SHIFT) +#define HSSPI_FIFO_OP_CODE_SHIFT 13 +#define HSSPI_FIFO_OP_READ_WRITE (1 << HSSPI_FIFO_OP_CODE_SHIFT) +#define HSSPI_FIFO_OP_CODE_W (2 << HSSPI_FIFO_OP_CODE_SHIFT) +#define HSSPI_FIFO_OP_CODE_R (3 << HSSPI_FIFO_OP_CODE_SHIFT) + +struct bcm63xx_hsspi_priv { + void __iomem *regs; + ulong clk_rate; + uint8_t num_cs; + uint8_t cs_pols; + uint speed; +}; + +static int bcm63xx_hsspi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct bcm63xx_hsspi_priv *priv = dev_get_priv(bus); + + if (cs >= priv->num_cs) { + printf("no cs %u\n", cs); + return -ENODEV; + } + + return 0; +} + +static int bcm63xx_hsspi_set_mode(struct udevice *bus, uint mode) +{ + struct bcm63xx_hsspi_priv *priv = dev_get_priv(bus); + + /* clock polarity */ + if (mode & SPI_CPOL) + setbits_be32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_POL_MASK); + else + clrbits_be32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_POL_MASK); + + return 0; +} + +static int bcm63xx_hsspi_set_speed(struct udevice *bus, uint speed) +{ + struct bcm63xx_hsspi_priv *priv = dev_get_priv(bus); + + priv->speed = speed; + + return 0; +} + +static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv, + struct dm_spi_slave_platdata *plat) +{ + uint32_t clr, set; + + /* profile clock */ + set = DIV_ROUND_UP(priv->clk_rate, priv->speed); + set = DIV_ROUND_UP(2048, set); + set &= SPI_PFL_CLK_FREQ_MASK; + set |= SPI_PFL_CLK_RSTLOOP_MASK; + writel_be(set, priv->regs + SPI_PFL_CLK_REG(plat->cs)); + + /* profile signal */ + set = 0; + clr = SPI_PFL_SIG_LAUNCHRIS_MASK | + SPI_PFL_SIG_LATCHRIS_MASK | + SPI_PFL_SIG_ASYNCIN_MASK; + + /* latch/launch config */ + if (plat->mode & SPI_CPHA) + set |= SPI_PFL_SIG_LAUNCHRIS_MASK; + else + set |= SPI_PFL_SIG_LATCHRIS_MASK; + + /* async clk */ + if (priv->speed > SPI_MAX_SYNC_CLOCK) + set |= SPI_PFL_SIG_ASYNCIN_MASK; + + clrsetbits_be32(priv->regs + SPI_PFL_SIG_REG(plat->cs), clr, set); + + /* global control */ + set = 0; + clr = 0; + + /* invert cs polarity */ + if (priv->cs_pols & BIT(plat->cs)) + clr |= BIT(plat->cs); + else + set |= BIT(plat->cs); + + /* invert dummy cs polarity */ + if (priv->cs_pols & BIT(!plat->cs)) + clr |= BIT(!plat->cs); + else + set |= BIT(!plat->cs); + + clrsetbits_be32(priv->regs + SPI_CTL_REG, clr, set); +} + +static void bcm63xx_hsspi_deactivate_cs(struct bcm63xx_hsspi_priv *priv) +{ + /* restore cs polarities */ + clrsetbits_be32(priv->regs + SPI_CTL_REG, SPI_CTL_CS_POL_MASK, + priv->cs_pols); +} + +/* + * BCM63xx HSSPI driver doesn't allow keeping CS active between transfers + * because they are controlled by HW. + * However, it provides a mechanism to prepend write transfers prior to read + * transfers (with a maximum prepend of 15 bytes), which is usually enough for + * SPI-connected flashes since reading requires prepending a write transfer of + * 5 bytes. On the other hand it also provides a way to invert each CS + * polarity, not only between transfers like the older BCM63xx SPI driver, but + * also the rest of the time. + * + * Instead of using the prepend mechanism, this implementation inverts the + * polarity of both the desired CS and another dummy CS when the bus is + * claimed. This way, the dummy CS is restored to its inactive value when + * transfers are issued and the desired CS is preserved in its active value + * all the time. This hack is also used in the upstream linux driver and + * allows keeping CS active between trasnfers even if the HW doesn't give + * this possibility. + */ +static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); + size_t data_bytes = bitlen / 8; + size_t step_size = HSSPI_FIFO_SIZE; + uint16_t opcode = 0; + uint32_t val; + const uint8_t *tx = dout; + uint8_t *rx = din; + + if (flags & SPI_XFER_BEGIN) + bcm63xx_hsspi_activate_cs(priv, plat); + + /* fifo operation */ + if (tx && rx) + opcode = HSSPI_FIFO_OP_READ_WRITE; + else if (rx) + opcode = HSSPI_FIFO_OP_CODE_R; + else if (tx) + opcode = HSSPI_FIFO_OP_CODE_W; + + if (opcode != HSSPI_FIFO_OP_CODE_R) + step_size -= HSSPI_FIFO_OP_SIZE; + + /* dual mode */ + if ((opcode == HSSPI_FIFO_OP_CODE_R && plat->mode == SPI_RX_DUAL) || + (opcode == HSSPI_FIFO_OP_CODE_W && plat->mode == SPI_TX_DUAL)) + opcode |= HSSPI_FIFO_OP_MBIT_MASK; + + /* profile mode */ + val = SPI_PFL_MODE_FILL_MASK | + SPI_PFL_MODE_MDRDSZ_MASK | + SPI_PFL_MODE_MDWRSZ_MASK; + if (plat->mode & SPI_3WIRE) + val |= SPI_PFL_MODE_3WIRE_MASK; + writel_be(val, priv->regs + SPI_PFL_MODE_REG(plat->cs)); + + /* transfer loop */ + while (data_bytes > 0) { + size_t curr_step = min(step_size, data_bytes); + int ret; + + /* copy tx data */ + if (tx) { + memcpy_toio(priv->regs + HSSPI_FIFO_BASE + + HSSPI_FIFO_OP_SIZE, tx, curr_step); + tx += curr_step; + } + + /* set fifo operation */ + writew_be(opcode | (curr_step & HSSPI_FIFO_OP_BYTES_MASK), + priv->regs + HSSPI_FIFO_OP_REG); + + /* issue the transfer */ + val = SPI_CMD_OP_START; + val |= (plat->cs << SPI_CMD_PFL_SHIFT) & + SPI_CMD_PFL_MASK; + val |= (!plat->cs << SPI_CMD_SLAVE_SHIFT) & + SPI_CMD_SLAVE_MASK; + writel_be(val, priv->regs + SPI_CMD_REG); + + /* wait for completion */ + ret = wait_for_bit_be32(priv->regs + SPI_STAT_REG, + SPI_STAT_SRCBUSY_MASK, false, + 1000, false); + if (ret) { + printf("interrupt timeout\n"); + return ret; + } + + /* copy rx data */ + if (rx) { + memcpy_fromio(rx, priv->regs + HSSPI_FIFO_BASE, + curr_step); + rx += curr_step; + } + + data_bytes -= curr_step; + } + + if (flags & SPI_XFER_END) + bcm63xx_hsspi_deactivate_cs(priv); + + return 0; +} + +static const struct dm_spi_ops bcm63xx_hsspi_ops = { + .cs_info = bcm63xx_hsspi_cs_info, + .set_mode = bcm63xx_hsspi_set_mode, + .set_speed = bcm63xx_hsspi_set_speed, + .xfer = bcm63xx_hsspi_xfer, +}; + +static const struct udevice_id bcm63xx_hsspi_ids[] = { + { .compatible = "brcm,bcm6328-hsspi", }, + { /* sentinel */ } +}; + +static int bcm63xx_hsspi_child_pre_probe(struct udevice *dev) +{ + struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); + + /* check cs */ + if (plat->cs >= priv->num_cs) { + printf("no cs %u\n", plat->cs); + return -ENODEV; + } + + /* cs polarity */ + if (plat->mode & SPI_CS_HIGH) + priv->cs_pols |= BIT(plat->cs); + else + priv->cs_pols &= ~BIT(plat->cs); + + return 0; +} + +static int bcm63xx_hsspi_probe(struct udevice *dev) +{ + struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev); + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = devfdt_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = ioremap(addr, size); + priv->num_cs = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "num-cs", 8); + + /* enable clock */ + ret = clk_get_by_name(dev, "hsspi", &clk); + if (ret < 0) + return ret; + + ret = clk_enable(&clk); + if (ret < 0) + return ret; + + ret = clk_free(&clk); + if (ret < 0) + return ret; + + /* get clock rate */ + ret = clk_get_by_name(dev, "pll", &clk); + if (ret < 0) + return ret; + + priv->clk_rate = clk_get_rate(&clk); + + ret = clk_free(&clk); + if (ret < 0) + return ret; + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + + ret = reset_deassert(&rst_ctl); + if (ret < 0) + return ret; + + ret = reset_free(&rst_ctl); + if (ret < 0) + return ret; + + /* initialize hardware */ + writel_be(0, priv->regs + SPI_IR_MASK_REG); + + /* clear pending interrupts */ + writel_be(SPI_IR_CLEAR_ALL, priv->regs + SPI_IR_STAT_REG); + + /* enable clk gate */ + setbits_be32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_GATE_MASK); + + /* read default cs polarities */ + priv->cs_pols = readl_be(priv->regs + SPI_CTL_REG) & + SPI_CTL_CS_POL_MASK; + + return 0; +} + +U_BOOT_DRIVER(bcm63xx_hsspi) = { + .name = "bcm63xx_hsspi", + .id = UCLASS_SPI, + .of_match = bcm63xx_hsspi_ids, + .ops = &bcm63xx_hsspi_ops, + .priv_auto_alloc_size = sizeof(struct bcm63xx_hsspi_priv), + .child_pre_probe = bcm63xx_hsspi_child_pre_probe, + .probe = bcm63xx_hsspi_probe, +}; -- cgit v1.1 From 547bcc3d18ddcc107b8aa7ca393830590c27978f Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 15 Jan 2018 11:08:35 +0100 Subject: spi: Fix style violation and improve code This patch fixes a printf specifier style violation, reduces the scope of a variable, and turns a void pointer that is used with pointer arithmetic into a u8 pointer. Signed-off-by: Mario Six Reviewed-by: Jagan Teki --- drivers/spi/spi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 7d81fbd..dea8dcd 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -12,7 +12,7 @@ int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen) { if (wordlen == 0 || wordlen > 32) { - printf("spi: invalid wordlen %d\n", wordlen); + printf("spi: invalid wordlen %u\n", wordlen); return -1; } @@ -24,11 +24,12 @@ int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen) void *spi_do_alloc_slave(int offset, int size, unsigned int bus, unsigned int cs) { - struct spi_slave *slave; - void *ptr; + u8 *ptr; ptr = malloc(size); if (ptr) { + struct spi_slave *slave; + memset(ptr, '\0', size); slave = (struct spi_slave *)(ptr + offset); slave->bus = bus; -- cgit v1.1 From c5b88f29ba46997e1cae39153980cae475b87b82 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 15 Jan 2018 11:08:36 +0100 Subject: spi: Remove obsolete spi_base_setup_slave_fdt 0efc024 ("spi_flash: Add spi_flash_probe_fdt() to locate SPI by FDT node") added a helper function spi_base_setup_slave_fdt to to set up a SPI slave from a given FDT blob. The only user was the exynos SPI driver. But commit 73186c9 ("dm: exynos: Convert SPI to driver model") removed the use of this function, hence rendering it obsolete. Remove this function, as well as the CONFIG_OF_SPI option, which guarded only this function. Reviewed-by: Simon Glass Reviewed-by: Jagan Teki Signed-off-by: Mario Six --- drivers/spi/spi.c | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index dea8dcd..45e73d2 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -39,23 +39,3 @@ void *spi_do_alloc_slave(int offset, int size, unsigned int bus, return ptr; } - -#ifdef CONFIG_OF_SPI -struct spi_slave *spi_base_setup_slave_fdt(const void *blob, int busnum, - int node) -{ - int cs, max_hz, mode = 0; - - cs = fdtdec_get_int(blob, node, "reg", -1); - max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", 100000); - if (fdtdec_get_bool(blob, node, "spi-cpol")) - mode |= SPI_CPOL; - if (fdtdec_get_bool(blob, node, "spi-cpha")) - mode |= SPI_CPHA; - if (fdtdec_get_bool(blob, node, "spi-cs-high")) - mode |= SPI_CS_HIGH; - if (fdtdec_get_bool(blob, node, "spi-half-duplex")) - mode |= SPI_PREAMBLE; - return spi_setup_slave(busnum, cs, max_hz, mode); -} -#endif -- cgit v1.1 From 184fa1c8da54d3c5305b3e1975e284e01de68bea Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 15 Jan 2018 11:08:38 +0100 Subject: spi: Remove spi_setup_slave_fdt A previous patch removed the spi_flash_probe_fdt function, which contained the last call of the spi_setup_slave_fdt function, which is now equally obsolete. This patch removes the function. Reviewed-by: Simon Glass Reviewed-by: Jagan Teki Signed-off-by: Mario Six --- drivers/spi/spi-uclass.c | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index e06a603..6db0eb0 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -348,22 +348,6 @@ err: } /* Compatibility function - to be removed */ -struct spi_slave *spi_setup_slave_fdt(const void *blob, int node, - int bus_node) -{ - struct udevice *bus, *dev; - int ret; - - ret = uclass_get_device_by_of_offset(UCLASS_SPI, bus_node, &bus); - if (ret) - return NULL; - ret = device_get_child_by_of_offset(bus, node, &dev); - if (ret) - return NULL; - return dev_get_parent_priv(dev); -} - -/* Compatibility function - to be removed */ struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, unsigned int speed, unsigned int mode) { -- cgit v1.1 From 24fc1ec2ee71cd852e556f90bd352cc809ddeef9 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 15 Jan 2018 11:08:41 +0100 Subject: spi: spi-uclass: Fix style violations Remove a superfluous newline, and reduce the scope of a variable. Reviewed-by: Simon Glass Reviewed-by: Jagan Teki Signed-off-by: Mario Six --- drivers/spi/spi-uclass.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 6db0eb0..15d90a5 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -50,7 +50,6 @@ int dm_spi_claim_bus(struct udevice *dev) struct dm_spi_bus *spi = dev_get_uclass_priv(bus); struct spi_slave *slave = dev_get_parent_priv(dev); int speed; - int ret; speed = slave->max_hz; if (spi->max_hz) { @@ -62,7 +61,8 @@ int dm_spi_claim_bus(struct udevice *dev) if (!speed) speed = 100000; if (speed != slave->speed) { - ret = spi_set_speed_mode(bus, speed, slave->mode); + int ret = spi_set_speed_mode(bus, speed, slave->mode); + if (ret) return ret; slave->speed = speed; @@ -129,7 +129,6 @@ static int spi_post_probe(struct udevice *bus) #if defined(CONFIG_NEEDS_MANUAL_RELOC) struct dm_spi_ops *ops = spi_get_ops(bus); - if (ops->claim_bus) ops->claim_bus += gd->reloc_off; if (ops->release_bus) -- cgit v1.1 From df16881cea50a787c37591bd2168c8ea656217bd Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Mon, 22 Jan 2018 22:44:20 +1300 Subject: spi: kirkwood_spi: implement workaround for FE-9144572 Erratum NO. FE-9144572: The device SPI interface supports frequencies of up to 50 MHz. However, due to this erratum, when the device core clock is 250 MHz and the SPI interfaces is configured for 50MHz SPI clock and CPOL=CPHA=1 there might occur data corruption on reads from the SPI device. Implement the workaround by setting the TMISO_SAMPLE value to 0x2 in the timing1 register. Signed-off-by: Chris Packham Reviewed-by: Stefan Roese Reviewed-by: Jagan Teki --- drivers/spi/kirkwood_spi.c | 69 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c index 0c6bd29..1ad8cde 100644 --- a/drivers/spi/kirkwood_spi.c +++ b/drivers/spi/kirkwood_spi.c @@ -243,6 +243,10 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, /* Here now the DM part */ +struct mvebu_spi_dev { + bool is_errata_50mhz_ac; +}; + struct mvebu_spi_platdata { struct kwspi_registers *spireg; }; @@ -269,10 +273,44 @@ static int mvebu_spi_set_speed(struct udevice *bus, uint hz) return 0; } +static void mvebu_spi_50mhz_ac_timing_erratum(struct udevice *bus, uint mode) +{ + struct mvebu_spi_platdata *plat = dev_get_platdata(bus); + struct kwspi_registers *reg = plat->spireg; + u32 data; + + /* + * Erratum description: (Erratum NO. FE-9144572) The device + * SPI interface supports frequencies of up to 50 MHz. + * However, due to this erratum, when the device core clock is + * 250 MHz and the SPI interfaces is configured for 50MHz SPI + * clock and CPOL=CPHA=1 there might occur data corruption on + * reads from the SPI device. + * Erratum Workaround: + * Work in one of the following configurations: + * 1. Set CPOL=CPHA=0 in "SPI Interface Configuration + * Register". + * 2. Set TMISO_SAMPLE value to 0x2 in "SPI Timing Parameters 1 + * Register" before setting the interface. + */ + data = readl(®->timing1); + data &= ~KW_SPI_TMISO_SAMPLE_MASK; + + if (CONFIG_SYS_TCLK == 250000000 && + mode & SPI_CPOL && + mode & SPI_CPHA) + data |= KW_SPI_TMISO_SAMPLE_2; + else + data |= KW_SPI_TMISO_SAMPLE_1; + + writel(data, ®->timing1); +} + static int mvebu_spi_set_mode(struct udevice *bus, uint mode) { struct mvebu_spi_platdata *plat = dev_get_platdata(bus); struct kwspi_registers *reg = plat->spireg; + const struct mvebu_spi_dev *drvdata; u32 data = readl(®->cfg); data &= ~(KWSPI_CPHA | KWSPI_CPOL | KWSPI_RXLSBF | KWSPI_TXLSBF); @@ -286,6 +324,10 @@ static int mvebu_spi_set_mode(struct udevice *bus, uint mode) writel(data, ®->cfg); + drvdata = (struct mvebu_spi_dev *)dev_get_driver_data(bus); + if (drvdata->is_errata_50mhz_ac) + mvebu_spi_50mhz_ac_timing_erratum(bus, mode); + return 0; } @@ -343,10 +385,31 @@ static const struct dm_spi_ops mvebu_spi_ops = { */ }; +static const struct mvebu_spi_dev armada_xp_spi_dev_data = { + .is_errata_50mhz_ac = false, +}; + +static const struct mvebu_spi_dev armada_375_spi_dev_data = { + .is_errata_50mhz_ac = false, +}; + +static const struct mvebu_spi_dev armada_380_spi_dev_data = { + .is_errata_50mhz_ac = true, +}; + static const struct udevice_id mvebu_spi_ids[] = { - { .compatible = "marvell,armada-375-spi" }, - { .compatible = "marvell,armada-380-spi" }, - { .compatible = "marvell,armada-xp-spi" }, + { + .compatible = "marvell,armada-375-spi", + .data = (ulong)&armada_375_spi_dev_data + }, + { + .compatible = "marvell,armada-380-spi", + .data = (ulong)&armada_380_spi_dev_data + }, + { + .compatible = "marvell,armada-xp-spi", + .data = (ulong)&armada_xp_spi_dev_data + }, { } }; -- cgit v1.1 From 15a70a5da33229de884f60684a562ea60fe505b2 Mon Sep 17 00:00:00 2001 From: Jason Rush Date: Tue, 23 Jan 2018 17:13:09 -0600 Subject: spi: cadence_spi: Sync DT bindings with Linux Adopt the Linux DT bindings. This also fixes an issue with the indaddrtrig register on the Cadence QSPI device being programmed with the wrong value for the socfpga arch. Tested on TI K2G platform: Tested-by: Vignesh R Tested on a socfpga-cyclonev board: Tested-by: Simon Goldschmidt Signed-off-by: Jason Rush Reviewed-by: Jagan Teki Acked-by: Simon Goldschmidt Acked-by: Marek Vasut --- drivers/spi/cadence_qspi.c | 20 ++++++++++++-------- drivers/spi/cadence_qspi.h | 6 +++++- drivers/spi/cadence_qspi_apb.c | 15 ++++----------- 3 files changed, 21 insertions(+), 20 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index 9a6e41f..7b312f8 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -212,7 +212,7 @@ static int cadence_spi_xfer(struct udevice *dev, unsigned int bitlen, /* Set Chip select */ cadence_qspi_apb_chipselect(base, spi_chip_select(dev), - CONFIG_CQSPI_DECODER); + plat->is_decoded_cs); if ((flags & SPI_XFER_END) || (flags == 0)) { if (priv->cmd_len == 0) { @@ -296,7 +296,11 @@ static int cadence_spi_ofdata_to_platdata(struct udevice *bus) plat->regbase = (void *)data[0]; plat->ahbbase = (void *)data[2]; - plat->sram_size = fdtdec_get_int(blob, node, "sram-size", 128); + plat->is_decoded_cs = fdtdec_get_bool(blob, node, "cdns,is-decoded-cs"); + plat->fifo_depth = fdtdec_get_uint(blob, node, "cdns,fifo-depth", 128); + plat->fifo_width = fdtdec_get_uint(blob, node, "cdns,fifo-width", 4); + plat->trigger_address = fdtdec_get_uint(blob, node, + "cdns,trigger-address", 0); /* All other paramters are embedded in the child node */ subnode = fdt_first_subnode(blob, node); @@ -310,12 +314,12 @@ static int cadence_spi_ofdata_to_platdata(struct udevice *bus) 500000); /* Read other parameters from DT */ - plat->page_size = fdtdec_get_int(blob, subnode, "page-size", 256); - plat->block_size = fdtdec_get_int(blob, subnode, "block-size", 16); - plat->tshsl_ns = fdtdec_get_int(blob, subnode, "tshsl-ns", 200); - plat->tsd2d_ns = fdtdec_get_int(blob, subnode, "tsd2d-ns", 255); - plat->tchsh_ns = fdtdec_get_int(blob, subnode, "tchsh-ns", 20); - plat->tslch_ns = fdtdec_get_int(blob, subnode, "tslch-ns", 20); + plat->page_size = fdtdec_get_uint(blob, subnode, "page-size", 256); + plat->block_size = fdtdec_get_uint(blob, subnode, "block-size", 16); + plat->tshsl_ns = fdtdec_get_uint(blob, subnode, "cdns,tshsl-ns", 200); + plat->tsd2d_ns = fdtdec_get_uint(blob, subnode, "cdns,tsd2d-ns", 255); + plat->tchsh_ns = fdtdec_get_uint(blob, subnode, "cdns,tchsh-ns", 20); + plat->tslch_ns = fdtdec_get_uint(blob, subnode, "cdns,tslch-ns", 20); debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n", __func__, plat->regbase, plat->ahbbase, plat->max_hz, diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h index d1927a4..9106b09 100644 --- a/drivers/spi/cadence_qspi.h +++ b/drivers/spi/cadence_qspi.h @@ -18,14 +18,18 @@ struct cadence_spi_platdata { unsigned int max_hz; void *regbase; void *ahbbase; + bool is_decoded_cs; + u32 fifo_depth; + u32 fifo_width; + u32 trigger_address; + /* Flash parameters */ u32 page_size; u32 block_size; u32 tshsl_ns; u32 tsd2d_ns; u32 tchsh_ns; u32 tslch_ns; - u32 sram_size; }; struct cadence_spi_priv { diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index dca3fdf..c3dd329 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -37,10 +37,6 @@ #define CQSPI_REG_RETRY 10000 #define CQSPI_POLL_IDLE_RETRY 3 -#define CQSPI_FIFO_WIDTH 4 - -#define CQSPI_REG_SRAM_THRESHOLD_WORDS 50 - /* Transfer mode */ #define CQSPI_INST_TYPE_SINGLE 0 #define CQSPI_INST_TYPE_DUAL 1 @@ -51,9 +47,6 @@ #define CQSPI_DUMMY_CLKS_PER_BYTE 8 #define CQSPI_DUMMY_BYTES_MAX 4 -#define CQSPI_REG_SRAM_FILL_THRESHOLD \ - ((CQSPI_REG_SRAM_SIZE_WORD / 2) * CQSPI_FIFO_WIDTH) - /**************************************************************************** * Controller's configuration and status register (offset from QSPI_BASE) ****************************************************************************/ @@ -400,7 +393,7 @@ void cadence_qspi_apb_controller_init(struct cadence_spi_platdata *plat) writel(0, plat->regbase + CQSPI_REG_REMAP); /* Indirect mode configurations */ - writel((plat->sram_size/2), plat->regbase + CQSPI_REG_SRAMPARTITION); + writel(plat->fifo_depth / 2, plat->regbase + CQSPI_REG_SRAMPARTITION); /* Disable all interrupts */ writel(0, plat->regbase + CQSPI_REG_IRQMASK); @@ -560,7 +553,7 @@ int cadence_qspi_apb_indirect_read_setup(struct cadence_spi_platdata *plat, addr_bytes = cmdlen - 1; /* Setup the indirect trigger address */ - writel((u32)plat->ahbbase, + writel(plat->trigger_address, plat->regbase + CQSPI_REG_INDIRECTTRIGGER); /* Configure the opcode */ @@ -659,7 +652,7 @@ int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, bytes_to_read = ret; while (bytes_to_read != 0) { - bytes_to_read *= CQSPI_FIFO_WIDTH; + bytes_to_read *= plat->fifo_width; bytes_to_read = bytes_to_read > remaining ? remaining : bytes_to_read; readsl(plat->ahbbase, bb_rxbuf, bytes_to_read >> 2); @@ -710,7 +703,7 @@ int cadence_qspi_apb_indirect_write_setup(struct cadence_spi_platdata *plat, return -EINVAL; } /* Setup the indirect trigger address */ - writel((u32)plat->ahbbase, + writel(plat->trigger_address, plat->regbase + CQSPI_REG_INDIRECTTRIGGER); /* Configure the opcode */ -- cgit v1.1 From 948ad4f07598a729a0de523ed3d779115b2fa2f2 Mon Sep 17 00:00:00 2001 From: Goldschmidt Simon Date: Wed, 24 Jan 2018 10:44:05 +0530 Subject: Revert "spi: cadence_qspi_apb: Use 32 bit indirect read transaction when possible" This reverts commit b63b46313ed29e9b0c36b3d6b9407f6eade40c8f. This commit changed cadence_qspi_apb to use bouncebuf.c, which invalidates the data cache after reading. This is meant for dma transfers only and breaks the cadence_qspi driver which copies via cpu only: data that is copied by the cpu is in cache only and the cache invalidation at the end throws away this data. Signed-off-by: Simon Goldschmidt Signed-off-by: Vignesh R Acked-by: Marek Vasut Reviewed-by: Jason Rush Acked-by: Jason Rush Reviewed-by: Jagan Teki --- drivers/spi/cadence_qspi_apb.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index c3dd329..128c41d 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -627,8 +627,6 @@ int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, { unsigned int remaining = n_rx; unsigned int bytes_to_read = 0; - struct bounce_buffer bb; - u8 *bb_rxbuf; int ret; writel(n_rx, plat->regbase + CQSPI_REG_INDIRECTRDBYTES); @@ -637,11 +635,6 @@ int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, writel(CQSPI_REG_INDIRECTRD_START, plat->regbase + CQSPI_REG_INDIRECTRD); - ret = bounce_buffer_start(&bb, (void *)rxbuf, n_rx, GEN_BB_WRITE); - if (ret) - return ret; - bb_rxbuf = bb.bounce_buffer; - while (remaining > 0) { ret = cadence_qspi_wait_for_data(plat); if (ret < 0) { @@ -655,13 +648,16 @@ int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, bytes_to_read *= plat->fifo_width; bytes_to_read = bytes_to_read > remaining ? remaining : bytes_to_read; - readsl(plat->ahbbase, bb_rxbuf, bytes_to_read >> 2); - if (bytes_to_read % 4) - readsb(plat->ahbbase, - bb_rxbuf + rounddown(bytes_to_read, 4), - bytes_to_read % 4); - - bb_rxbuf += bytes_to_read; + /* + * Handle non-4-byte aligned access to avoid + * data abort. + */ + if (((uintptr_t)rxbuf % 4) || (bytes_to_read % 4)) + readsb(plat->ahbbase, rxbuf, bytes_to_read); + else + readsl(plat->ahbbase, rxbuf, + bytes_to_read >> 2); + rxbuf += bytes_to_read; remaining -= bytes_to_read; bytes_to_read = cadence_qspi_get_rd_sram_level(plat); } @@ -678,7 +674,6 @@ int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, /* Clear indirect completion status */ writel(CQSPI_REG_INDIRECTRD_DONE, plat->regbase + CQSPI_REG_INDIRECTRD); - bounce_buffer_stop(&bb); return 0; @@ -686,7 +681,6 @@ failrd: /* Cancel the indirect read */ writel(CQSPI_REG_INDIRECTRD_CANCEL, plat->regbase + CQSPI_REG_INDIRECTRD); - bounce_buffer_stop(&bb); return ret; } -- cgit v1.1 From a743e2ba3837db5e8499b03f0f57c3610d03a570 Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Wed, 24 Jan 2018 10:44:06 +0530 Subject: Revert "spi: cadence_qspi_apb: Use 32 bit indirect write transaction when possible" This reverts commit 57897c13de03ac0136d64641a3eab526c6810387. Using bounce_buf.c to handle non-DMA alignment problems is bad as bounce_buf.c does cache manipulations which is not required. Therefore revert this patch in favour of local bounce buffer solution in the next patch. Signed-off-by: Vignesh R Acked-by: Marek Vasut Acked-by: Simon Goldschmidt Reviewed-by: Jason Rush Acked-by: Jason Rush Reviewed-by: Jagan Teki --- drivers/spi/cadence_qspi_apb.c | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index 128c41d..70d0f43 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -30,7 +30,6 @@ #include #include #include -#include #include "cadence_qspi.h" #define CQSPI_REG_POLL_US 1 /* 1us */ @@ -722,17 +721,6 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, unsigned int remaining = n_tx; unsigned int write_bytes; int ret; - struct bounce_buffer bb; - u8 *bb_txbuf; - - /* - * Handle non-4-byte aligned accesses via bounce buffer to - * avoid data abort. - */ - ret = bounce_buffer_start(&bb, (void *)txbuf, n_tx, GEN_BB_READ); - if (ret) - return ret; - bb_txbuf = bb.bounce_buffer; /* Configure the indirect read transfer bytes */ writel(n_tx, plat->regbase + CQSPI_REG_INDIRECTWRBYTES); @@ -743,11 +731,11 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, while (remaining > 0) { write_bytes = remaining > page_size ? page_size : remaining; - writesl(plat->ahbbase, bb_txbuf, write_bytes >> 2); - if (write_bytes % 4) - writesb(plat->ahbbase, - bb_txbuf + rounddown(write_bytes, 4), - write_bytes % 4); + /* Handle non-4-byte aligned access to avoid data abort. */ + if (((uintptr_t)txbuf % 4) || (write_bytes % 4)) + writesb(plat->ahbbase, txbuf, write_bytes); + else + writesl(plat->ahbbase, txbuf, write_bytes >> 2); ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_SDRAMLEVEL, CQSPI_REG_SDRAMLEVEL_WR_MASK << @@ -757,7 +745,7 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, goto failwr; } - bb_txbuf += write_bytes; + txbuf += write_bytes; remaining -= write_bytes; } @@ -768,7 +756,6 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, printf("Indirect write completion error (%i)\n", ret); goto failwr; } - bounce_buffer_stop(&bb); /* Clear indirect completion status */ writel(CQSPI_REG_INDIRECTWR_DONE, @@ -779,7 +766,6 @@ failwr: /* Cancel the indirect write */ writel(CQSPI_REG_INDIRECTWR_CANCEL, plat->regbase + CQSPI_REG_INDIRECTWR); - bounce_buffer_stop(&bb); return ret; } -- cgit v1.1 From aaa21d3ffc023623a3a8247e5fa25d0db2bfb630 Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Wed, 24 Jan 2018 10:44:07 +0530 Subject: spi: cadence_qspi_apb: Make flash writes 32 bit aligned Make flash writes 32 bit aligned by using bounce buffers to deal with non 32 bit aligned buffers. This is required because as per TI K2G TRM[1], the external master is only permitted to issue 32-bit data interface writes until the last word of an indirect transfer. Otherwise indirect writes is known to fail sometimes. [1] http://www.ti.com/lit/ug/spruhy8g/spruhy8g.pdf Signed-off-by: Vignesh R Acked-by: Marek Vasut Acked-by: Simon Goldschmidt Reviewed-by: Jason Rush Acked-by: Jason Rush Reviewed-by: Jagan Teki --- drivers/spi/cadence_qspi_apb.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index 70d0f43..aa3a9ff 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "cadence_qspi.h" #define CQSPI_REG_POLL_US 1 /* 1us */ @@ -719,9 +720,23 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, { unsigned int page_size = plat->page_size; unsigned int remaining = n_tx; + const u8 *bb_txbuf = txbuf; + void *bounce_buf = NULL; unsigned int write_bytes; int ret; + /* + * Use bounce buffer for non 32 bit aligned txbuf to avoid data + * aborts + */ + if ((uintptr_t)txbuf % 4) { + bounce_buf = malloc(n_tx); + if (!bounce_buf) + return -ENOMEM; + memcpy(bounce_buf, txbuf, n_tx); + bb_txbuf = bounce_buf; + } + /* Configure the indirect read transfer bytes */ writel(n_tx, plat->regbase + CQSPI_REG_INDIRECTWRBYTES); @@ -731,11 +746,11 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, while (remaining > 0) { write_bytes = remaining > page_size ? page_size : remaining; - /* Handle non-4-byte aligned access to avoid data abort. */ - if (((uintptr_t)txbuf % 4) || (write_bytes % 4)) - writesb(plat->ahbbase, txbuf, write_bytes); - else - writesl(plat->ahbbase, txbuf, write_bytes >> 2); + writesl(plat->ahbbase, bb_txbuf, write_bytes >> 2); + if (write_bytes % 4) + writesb(plat->ahbbase, + bb_txbuf + rounddown(write_bytes, 4), + write_bytes % 4); ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_SDRAMLEVEL, CQSPI_REG_SDRAMLEVEL_WR_MASK << @@ -745,7 +760,7 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, goto failwr; } - txbuf += write_bytes; + bb_txbuf += write_bytes; remaining -= write_bytes; } @@ -760,12 +775,16 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, /* Clear indirect completion status */ writel(CQSPI_REG_INDIRECTWR_DONE, plat->regbase + CQSPI_REG_INDIRECTWR); + if (bounce_buf) + free(bounce_buf); return 0; failwr: /* Cancel the indirect write */ writel(CQSPI_REG_INDIRECTWR_CANCEL, plat->regbase + CQSPI_REG_INDIRECTWR); + if (bounce_buf) + free(bounce_buf); return ret; } -- cgit v1.1 From 58c125b9e2b232ce73ed7b24ba7b1ca5ff41c5bd Mon Sep 17 00:00:00 2001 From: Eugeniy Paltsev Date: Thu, 28 Dec 2017 15:09:03 +0300 Subject: DW SPI: Get clock value from Device Tree Add option to set spi controller clock frequency via device tree using standard clock bindings. Define dw_spi_get_clk function as 'weak' as some targets (like SOCFPGA_GEN5 and SOCFPGA_ARRIA10) don't use standard clock API and implement dw_spi_get_clk their own way in their clock manager. Get rid of clock_manager.h include as we don't use cm_get_spi_controller_clk_hz function anymore. (we use redefined dw_spi_get_clk in SOCFPGA clock managers instead) Reviewed-by: Marek Vasut Signed-off-by: Eugeniy Paltsev Reviewed-by: Jagan Teki --- drivers/spi/designware_spi.c | 45 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c index 5aa507b..c501aee 100644 --- a/drivers/spi/designware_spi.c +++ b/drivers/spi/designware_spi.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -18,7 +19,6 @@ #include #include #include -#include DECLARE_GLOBAL_DATA_PTR; @@ -94,6 +94,8 @@ struct dw_spi_priv { void __iomem *regs; unsigned int freq; /* Default frequency */ unsigned int mode; + struct clk clk; + unsigned long bus_clk_rate; int bits_per_word; u8 cs; /* chip select pin */ @@ -176,14 +178,53 @@ static void spi_hw_init(struct dw_spi_priv *priv) debug("%s: fifo_len=%d\n", __func__, priv->fifo_len); } +/* + * We define dw_spi_get_clk function as 'weak' as some targets + * (like SOCFPGA_GEN5 and SOCFPGA_ARRIA10) don't use standard clock API + * and implement dw_spi_get_clk their own way in their clock manager. + */ +__weak int dw_spi_get_clk(struct udevice *bus, ulong *rate) +{ + struct dw_spi_priv *priv = dev_get_priv(bus); + int ret; + + ret = clk_get_by_index(bus, 0, &priv->clk); + if (ret) + return ret; + + ret = clk_enable(&priv->clk); + if (ret && ret != -ENOSYS && ret != -ENOTSUPP) + return ret; + + *rate = clk_get_rate(&priv->clk); + if (!*rate) + goto err_rate; + + debug("%s: get spi controller clk via device tree: %lu Hz\n", + __func__, *rate); + + return 0; + +err_rate: + clk_disable(&priv->clk); + clk_free(&priv->clk); + + return -EINVAL; +} + static int dw_spi_probe(struct udevice *bus) { struct dw_spi_platdata *plat = dev_get_platdata(bus); struct dw_spi_priv *priv = dev_get_priv(bus); + int ret; priv->regs = plat->regs; priv->freq = plat->frequency; + ret = dw_spi_get_clk(bus, &priv->bus_clk_rate); + if (ret) + return ret; + /* Currently only bits_per_word == 8 supported */ priv->bits_per_word = 8; @@ -369,7 +410,7 @@ static int dw_spi_set_speed(struct udevice *bus, uint speed) spi_enable_chip(priv, 0); /* clk_div doesn't support odd number */ - clk_div = cm_get_spi_controller_clk_hz() / speed; + clk_div = priv->bus_clk_rate / speed; clk_div = (clk_div + 1) & 0xfffe; dw_writel(priv, DW_SPI_BAUDR, clk_div); -- cgit v1.1