aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini2023-07-13 20:39:10 -0400
committerTom Rini2023-07-13 20:39:10 -0400
commitcef36755094f0c5463ff34ac89de8d88ef68982b (patch)
tree9c49b6e79e099a40cf58b01794b1d6293ecfdc11
parentc990ecba4d8b73a0fcf6f33d758ff1ed176e44ae (diff)
parent4a31e145217cecc3d421f96eafcd2cfd9c670929 (diff)
Merge https://gitlab.denx.de/u-boot/custodians/u-boot-spi
- Add xtxtech spi-nor chip parts (Bruce Suen) - Add bcm63xx-hsspi driver fixes (William Zhang)
-rw-r--r--doc/device-tree-bindings/spi/brcm,bcm63xx-hsspi.yaml134
-rw-r--r--doc/device-tree-bindings/spi/soft-spi.txt12
-rw-r--r--drivers/mtd/spi/spi-nor-ids.c43
-rw-r--r--drivers/spi/Kconfig13
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/bcm63xx_hsspi.c277
-rw-r--r--drivers/spi/bcmbca_hsspi.c414
-rw-r--r--drivers/spi/npcm_pspi.c12
-rw-r--r--drivers/spi/pl022_spi.c51
-rw-r--r--drivers/spi/soft_spi.c21
-rw-r--r--drivers/spi/spi-synquacer.c8
-rw-r--r--include/dm/platform_data/spi_pl022.h21
12 files changed, 932 insertions, 75 deletions
diff --git a/doc/device-tree-bindings/spi/brcm,bcm63xx-hsspi.yaml b/doc/device-tree-bindings/spi/brcm,bcm63xx-hsspi.yaml
new file mode 100644
index 00000000000..6554978583f
--- /dev/null
+++ b/doc/device-tree-bindings/spi/brcm,bcm63xx-hsspi.yaml
@@ -0,0 +1,134 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/brcm,bcm63xx-hsspi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Broadcom Broadband SoC High Speed SPI controller
+
+maintainers:
+ - William Zhang <william.zhang@broadcom.com>
+ - Kursad Oney <kursad.oney@broadcom.com>
+ - Jonas Gorski <jonas.gorski@gmail.com>
+
+description: |
+ Broadcom Broadband SoC supports High Speed SPI master controller since the
+ early MIPS based chips such as BCM6328 and BCM63268. This initial rev 1.0
+ controller was carried over to recent ARM based chips, such as BCM63138,
+ BCM4908 and BCM6858. The old MIPS based chip should continue to use the
+ brcm,bcm6328-hsspi compatible string. The recent ARM based chip is required to
+ use the brcm,bcmbca-hsspi-v1.0 as part of its compatible string list as
+ defined below to match the specific chip along with ip revision info.
+
+ This rev 1.0 controller has a limitation that can not keep the chip select line
+ active between the SPI transfers within the same SPI message. This can
+ terminate the transaction to some SPI devices prematurely. The issue can be
+ worked around by either the controller's prepend mode or using the dummy chip
+ select workaround. Driver automatically picks the suitable mode based on
+ transfer type so it is transparent to the user.
+
+ The newer SoCs such as BCM6756, BCM4912 and BCM6855 include an updated SPI
+ controller rev 1.1 that add the capability to allow the driver to control chip
+ select explicitly. This solves the issue in the old controller.
+
+properties:
+ compatible:
+ oneOf:
+ - const: brcm,bcm6328-hsspi
+ - items:
+ - enum:
+ - brcm,bcm47622-hsspi
+ - brcm,bcm4908-hsspi
+ - brcm,bcm63138-hsspi
+ - brcm,bcm63146-hsspi
+ - brcm,bcm63148-hsspi
+ - brcm,bcm63158-hsspi
+ - brcm,bcm63178-hsspi
+ - brcm,bcm6846-hsspi
+ - brcm,bcm6856-hsspi
+ - brcm,bcm6858-hsspi
+ - brcm,bcm6878-hsspi
+ - const: brcm,bcmbca-hsspi-v1.0
+ - items:
+ - enum:
+ - brcm,bcm4912-hsspi
+ - brcm,bcm6756-hsspi
+ - brcm,bcm6813-hsspi
+ - brcm,bcm6855-hsspi
+ - const: brcm,bcmbca-hsspi-v1.1
+
+ reg:
+ items:
+ - description: main registers
+ - description: miscellaneous control registers
+ minItems: 1
+
+ reg-names:
+ items:
+ - const: hsspi
+ - const: spim-ctrl
+ minItems: 1
+
+ clocks:
+ items:
+ - description: SPI master reference clock
+ - description: SPI master pll clock
+
+ clock-names:
+ items:
+ - const: hsspi
+ - const: pll
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - interrupts
+
+allOf:
+ - $ref: spi-controller.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - brcm,bcm6328-hsspi
+ - brcm,bcmbca-hsspi-v1.0
+ then:
+ properties:
+ reg:
+ maxItems: 1
+ reg-names:
+ maxItems: 1
+ else:
+ properties:
+ reg:
+ minItems: 2
+ maxItems: 2
+ reg-names:
+ minItems: 2
+ maxItems: 2
+ required:
+ - reg-names
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ spi@ff801000 {
+ compatible = "brcm,bcm6756-hsspi", "brcm,bcmbca-hsspi-v1.1";
+ reg = <0xff801000 0x1000>,
+ <0xff802610 0x4>;
+ reg-names = "hsspi", "spim-ctrl";
+ interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&hsspi>, <&hsspi_pll>;
+ clock-names = "hsspi", "pll";
+ num-cs = <8>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
diff --git a/doc/device-tree-bindings/spi/soft-spi.txt b/doc/device-tree-bindings/spi/soft-spi.txt
index dfb50664732..bdf7e86befb 100644
--- a/doc/device-tree-bindings/spi/soft-spi.txt
+++ b/doc/device-tree-bindings/spi/soft-spi.txt
@@ -9,10 +9,10 @@ The soft SPI node requires the following properties:
Mandatory properties:
compatible: "spi-gpio"
cs-gpios: GPIOs to use for SPI chip select (output)
-gpio-sck: GPIO to use for SPI clock (output)
+sck-gpios: GPIO to use for SPI clock (output)
And at least one of:
-gpio-mosi: GPIO to use for SPI MOSI line (output)
-gpio-miso: GPIO to use for SPI MISO line (input)
+mosi-gpios: GPIO to use for SPI MOSI line (output)
+miso-gpios: GPIO to use for SPI MISO line (input)
Optional propertie:
spi-delay-us: Number of microseconds of delay between each CS transition
@@ -27,9 +27,9 @@ Example:
soft-spi {
compatible = "spi-gpio";
cs-gpios = <&gpio 235 0>; /* Y43 */
- gpio-sck = <&gpio 225 0>; /* Y31 */
- gpio-mosi = <&gpio 227 0>; /* Y33 */
- gpio-miso = <&gpio 224 0>; /* Y30 */
+ sck-gpios = <&gpio 225 0>; /* Y31 */
+ mosi-gpios = <&gpio 227 0>; /* Y33 */
+ miso-gpios = <&gpio 224 0>; /* Y30 */
spi-delay-us = <1>;
#address-cells = <1>;
#size-cells = <0>;
diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c
index 3f8b7967893..45872159842 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -446,6 +446,11 @@ const struct flash_info spi_nor_ids[] = {
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
+ {
+ INFO("w25q256jwm", 0xef8019, 0, 64 * 1024, 512,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
{ INFO("w25x64", 0xef3017, 0, 64 * 1024, 128, SECT_4K) },
{
INFO("w25q64dw", 0xef6017, 0, 64 * 1024, 128,
@@ -528,8 +533,42 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("XM25QH128A", 0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
#endif
#ifdef CONFIG_SPI_FLASH_XTX
- /* XTX Technology (Shenzhen) Limited */
- { INFO("xt25f128b", 0x0b4018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ /* XTX Technology Limited */
+ /* adding these 3V QSPI flash parts */
+ { INFO("xt25f08", 0x0b4014, 0, 64 * 1024, 16,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { INFO("xt25f16", 0x0b4015, 0, 64 * 1024, 32,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { INFO("xt25f32", 0x0b4016, 0, 64 * 1024, 64,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { INFO("xt25f64", 0x0b4017, 0, 64 * 1024, 128,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { INFO("xt25f128", 0x0b4018, 0, 64 * 1024, 256,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { INFO("xt25f256", 0x0b4019, 0, 64 * 1024, 512,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+ /* adding these 1.8V QSPI flash parts */
+ { INFO("xt25q08", 0x0b6014, 0, 64 * 1024, 16,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { INFO("xt25q16", 0x0b6015, 0, 64 * 1024, 32,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { INFO("xt25q32", 0x0b6016, 0, 64 * 1024, 64,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { INFO("xt25q64", 0x0b6017, 0, 64 * 1024, 128,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { INFO("xt25q128", 0x0b6018, 0, 64 * 1024, 256,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { INFO("xt25q256", 0x0b6019, 0, 64 * 1024, 512,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+ { INFO("xt25q512", 0x0b601A, 0, 64 * 1024, 1024,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+ { INFO("xt25q01g", 0x0b601B, 0, 64 * 1024, 2048,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+ /* adding these wide voltage QSPI flash parts */
+ { INFO("xt25w512", 0x0b651A, 0, 64 * 1024, 1024,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+ { INFO("xt25w01g", 0x0b651B, 0, 64 * 1024, 2048,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
#endif
{ },
};
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 453a5983b2a..854b8b88daf 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -101,12 +101,21 @@ config ATMEL_SPI
config BCM63XX_HSSPI
bool "BCM63XX HSSPI driver"
- depends on (ARCH_BMIPS || BCM6856 || BCM6858 || BCM63158)
+ depends on (ARCH_BMIPS || ARCH_BCMBCA)
help
- Enable the BCM6328 HSSPI driver. This driver can be used to
+ Enable the BCM63XX HSSPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Broadcom
SPI core.
+config BCMBCA_HSSPI
+ bool "BCMBCA HSSPI driver"
+ depends on ARCH_BCMBCA && HAVE_SPI_CS_CTRL
+ help
+ This enables support for the High Speed SPI controller present on
+ newer Broadcom BCMBCA SoCs. These SoCs include an updated SPI controller
+ that adds the capability to allow the driver to control chip select
+ explicitly.
+
config BCM63XX_SPI
bool "BCM6348 SPI driver"
depends on ARCH_BMIPS
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 95dba9ac455..c27b3327c33 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_ATH79_SPI) += ath79_spi.o
obj-$(CONFIG_ATMEL_QSPI) += atmel-quadspi.o
obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o
obj-$(CONFIG_BCM63XX_HSSPI) += bcm63xx_hsspi.o
+obj-$(CONFIG_BCMBCA_HSSPI) += bcmbca_hsspi.o
obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o
obj-$(CONFIG_BCMSTB_SPI) += bcmstb_spi.o
obj-$(CONFIG_CF_SPI) += cf_spi.o
diff --git a/drivers/spi/bcm63xx_hsspi.c b/drivers/spi/bcm63xx_hsspi.c
index 4d714adc4af..a24bb430cbb 100644
--- a/drivers/spi/bcm63xx_hsspi.c
+++ b/drivers/spi/bcm63xx_hsspi.c
@@ -20,7 +20,13 @@
#define HSSPI_PP 0
-#define SPI_MAX_SYNC_CLOCK 30000000
+/*
+ * The maximum frequency for SPI synchronous mode is 30MHz for some chips and
+ * 25MHz for some others. This depends on the chip layout and SPI signals
+ * distance to the pad. We use the lower of these values to cover all relevant
+ * chips.
+ */
+#define SPI_MAX_SYNC_CLOCK 25000000
/* SPI Control register */
#define SPI_CTL_REG 0x000
@@ -72,12 +78,16 @@
#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_MDRDST_SHIFT 8
+#define SPI_PFL_MODE_MDWRST_SHIFT 12
#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)
+#define SPI_PFL_MODE_PREPCNT_SHIFT 24
+#define SPI_PFL_MODE_PREPCNT_MASK (4 << SPI_PFL_MODE_PREPCNT_SHIFT)
/* SPI Ping-Pong FIFO registers */
#define HSSPI_FIFO_SIZE 0x200
@@ -96,12 +106,21 @@
#define HSSPI_FIFO_OP_CODE_W (2 << HSSPI_FIFO_OP_CODE_SHIFT)
#define HSSPI_FIFO_OP_CODE_R (3 << HSSPI_FIFO_OP_CODE_SHIFT)
+#define HSSPI_MAX_DATA_SIZE (HSSPI_FIFO_SIZE - HSSPI_FIFO_OP_SIZE)
+#define HSSPI_MAX_PREPEND_SIZE 15
+
+#define HSSPI_XFER_MODE_PREPEND 0
+#define HSSPI_XFER_MODE_DUMMYCS 1
+
struct bcm63xx_hsspi_priv {
void __iomem *regs;
ulong clk_rate;
uint8_t num_cs;
uint8_t cs_pols;
uint speed;
+ uint xfer_mode;
+ uint32_t prepend_cnt;
+ uint8_t prepend_buf[HSSPI_MAX_PREPEND_SIZE];
};
static int bcm63xx_hsspi_cs_info(struct udevice *bus, uint cs,
@@ -143,9 +162,16 @@ static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv,
struct dm_spi_slave_plat *plat)
{
uint32_t clr, set;
+ uint speed = priv->speed;
+
+ if (priv->xfer_mode == HSSPI_XFER_MODE_DUMMYCS &&
+ speed > SPI_MAX_SYNC_CLOCK) {
+ speed = SPI_MAX_SYNC_CLOCK;
+ debug("Force to dummy cs mode. Reduce the speed to %dHz\n", speed);
+ }
/* profile clock */
- set = DIV_ROUND_UP(priv->clk_rate, priv->speed);
+ set = DIV_ROUND_UP(priv->clk_rate, speed);
set = DIV_ROUND_UP(2048, set);
set &= SPI_PFL_CLK_FREQ_MASK;
set |= SPI_PFL_CLK_RSTLOOP_MASK;
@@ -164,7 +190,7 @@ static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv,
set |= SPI_PFL_SIG_LATCHRIS_MASK;
/* async clk */
- if (priv->speed > SPI_MAX_SYNC_CLOCK)
+ if (speed > SPI_MAX_SYNC_CLOCK)
set |= SPI_PFL_SIG_ASYNCIN_MASK;
clrsetbits_32(priv->regs + SPI_PFL_SIG_REG(plat->cs), clr, set);
@@ -173,17 +199,24 @@ static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv,
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);
+ if (priv->xfer_mode == HSSPI_XFER_MODE_PREPEND) {
+ if (priv->cs_pols & BIT(plat->cs))
+ set |= BIT(plat->cs);
+ else
+ clr |= BIT(plat->cs);
+ } else {
+ /* 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_32(priv->regs + SPI_CTL_REG, clr, set);
}
@@ -212,16 +245,21 @@ static void bcm63xx_hsspi_deactivate_cs(struct bcm63xx_hsspi_priv *priv)
* all the time. This hack is also used in the upstream linux driver and
* allows keeping CS active between transfers even if the HW doesn't give
* this possibility.
+ *
+ * This workaround only works when the dummy CS (usually CS1 when the actual
+ * CS is 0) pinmuxed to SPI chip select function if SPI clock is faster than
+ * SPI_MAX_SYNC_CLOCK. In old broadcom chip, CS1 pin is default to chip select
+ * function. But this is not the case for new chips. To make this function
+ * always work, it should be called with maximum clock of SPI_MAX_SYNC_CLOCK.
*/
-static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen,
- const void *dout, void *din, unsigned long flags)
+static int bcm63xx_hsspi_xfer_dummy_cs(struct udevice *dev, unsigned int data_bytes,
+ const void *dout, void *din, unsigned long flags)
{
struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
- size_t data_bytes = bitlen / 8;
size_t step_size = HSSPI_FIFO_SIZE;
uint16_t opcode = 0;
- uint32_t val;
+ uint32_t val = SPI_PFL_MODE_FILL_MASK;
const uint8_t *tx = dout;
uint8_t *rx = din;
@@ -240,14 +278,17 @@ static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen,
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))
+ 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;
+ /* profile mode */
+ if (plat->mode & SPI_RX_DUAL)
+ val |= SPI_PFL_MODE_MDRDSZ_MASK;
+ if (plat->mode & SPI_TX_DUAL)
+ val |= SPI_PFL_MODE_MDWRSZ_MASK;
+ }
+
if (plat->mode & SPI_3WIRE)
val |= SPI_PFL_MODE_3WIRE_MASK;
writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs));
@@ -301,6 +342,182 @@ static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen,
return 0;
}
+static int bcm63xx_prepare_prepend_transfer(struct bcm63xx_hsspi_priv *priv,
+ unsigned int data_bytes, const void *dout, void *din,
+ unsigned long flags)
+{
+ /*
+ * only support multiple half duplex write transfer + optional
+ * full duplex read/write at the end.
+ */
+ if (flags & SPI_XFER_BEGIN) {
+ /* clear prepends */
+ priv->prepend_cnt = 0;
+ }
+
+ if (din) {
+ /* buffering reads not possible for prepend mode */
+ if (!(flags & SPI_XFER_END)) {
+ debug("unable to buffer reads\n");
+ return HSSPI_XFER_MODE_DUMMYCS;
+ }
+
+ /* check rx size */
+ if (data_bytes > HSSPI_MAX_DATA_SIZE) {
+ debug("max rx bytes exceeded\n");
+ return HSSPI_XFER_MODE_DUMMYCS;
+ }
+ }
+
+ if (dout) {
+ /* check tx size */
+ if (flags & SPI_XFER_END) {
+ if (priv->prepend_cnt + data_bytes > HSSPI_MAX_DATA_SIZE) {
+ debug("max tx bytes exceeded\n");
+ return HSSPI_XFER_MODE_DUMMYCS;
+ }
+ } else {
+ if (priv->prepend_cnt + data_bytes > HSSPI_MAX_PREPEND_SIZE) {
+ debug("max prepend bytes exceeded\n");
+ return HSSPI_XFER_MODE_DUMMYCS;
+ }
+
+ /*
+ * buffer transfer data in the prepend buf in case we have to fall
+ * back to dummy cs mode.
+ */
+ memcpy(&priv->prepend_buf[priv->prepend_cnt], dout, data_bytes);
+ priv->prepend_cnt += data_bytes;
+ }
+ }
+
+ return HSSPI_XFER_MODE_PREPEND;
+}
+
+static int bcm63xx_hsspi_xfer_prepend(struct udevice *dev, unsigned int data_bytes,
+ const void *dout, void *din, unsigned long flags)
+{
+ struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent);
+ struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
+ uint16_t opcode = 0;
+ uint32_t val, offset;
+ int ret;
+
+ if (flags & SPI_XFER_END) {
+ offset = HSSPI_FIFO_BASE + HSSPI_FIFO_OP_SIZE;
+ if (priv->prepend_cnt) {
+ /* copy prepend data */
+ memcpy_toio(priv->regs + offset,
+ priv->prepend_buf, priv->prepend_cnt);
+ }
+
+ if (dout && data_bytes) {
+ /* copy tx data */
+ offset += priv->prepend_cnt;
+ memcpy_toio(priv->regs + offset, dout, data_bytes);
+ }
+
+ bcm63xx_hsspi_activate_cs(priv, plat);
+ if (dout && !din) {
+ /* all half-duplex write. merge to single write */
+ data_bytes += priv->prepend_cnt;
+ opcode = HSSPI_FIFO_OP_CODE_W;
+ priv->prepend_cnt = 0;
+ } else if (!dout && din) {
+ /* half-duplex read with prepend write */
+ opcode = HSSPI_FIFO_OP_CODE_R;
+ } else {
+ /* full duplex read/write */
+ opcode = HSSPI_FIFO_OP_READ_WRITE;
+ }
+
+ /* profile mode */
+ val = SPI_PFL_MODE_FILL_MASK;
+ if (plat->mode & SPI_3WIRE)
+ val |= SPI_PFL_MODE_3WIRE_MASK;
+
+ /* 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;
+
+ if (plat->mode & SPI_RX_DUAL) {
+ val |= SPI_PFL_MODE_MDRDSZ_MASK;
+ val |= priv->prepend_cnt << SPI_PFL_MODE_MDRDST_SHIFT;
+ }
+ if (plat->mode & SPI_TX_DUAL) {
+ val |= SPI_PFL_MODE_MDWRSZ_MASK;
+ val |= priv->prepend_cnt << SPI_PFL_MODE_MDWRST_SHIFT;
+ }
+ }
+ val |= (priv->prepend_cnt << SPI_PFL_MODE_PREPCNT_SHIFT);
+ writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs));
+
+ /* set fifo operation */
+ val = opcode | (data_bytes & HSSPI_FIFO_OP_BYTES_MASK);
+ writew(cpu_to_be16(val),
+ 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(val, priv->regs + SPI_CMD_REG);
+
+ /* wait for completion */
+ ret = wait_for_bit_32(priv->regs + SPI_STAT_REG,
+ SPI_STAT_SRCBUSY_MASK, false,
+ 1000, false);
+ if (ret) {
+ bcm63xx_hsspi_deactivate_cs(priv);
+ printf("spi polling timeout\n");
+ return ret;
+ }
+
+ /* copy rx data */
+ if (din)
+ memcpy_fromio(din, priv->regs + HSSPI_FIFO_BASE,
+ data_bytes);
+ bcm63xx_hsspi_deactivate_cs(priv);
+ }
+
+ return 0;
+}
+
+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);
+ int ret;
+ u32 data_bytes = bitlen >> 3;
+
+ if (priv->xfer_mode == HSSPI_XFER_MODE_PREPEND) {
+ priv->xfer_mode =
+ bcm63xx_prepare_prepend_transfer(priv, data_bytes, dout, din, flags);
+ }
+
+ /* if not prependable, fall back to dummy cs mode with safe clock */
+ if (priv->xfer_mode == HSSPI_XFER_MODE_DUMMYCS) {
+ /* For pending prepend data from previous transfers, send it first */
+ if (priv->prepend_cnt) {
+ bcm63xx_hsspi_xfer_dummy_cs(dev, priv->prepend_cnt,
+ priv->prepend_buf, NULL,
+ (flags & ~SPI_XFER_END) | SPI_XFER_BEGIN);
+ priv->prepend_cnt = 0;
+ }
+ ret = bcm63xx_hsspi_xfer_dummy_cs(dev, data_bytes, dout, din, flags);
+ } else {
+ ret = bcm63xx_hsspi_xfer_prepend(dev, data_bytes, dout, din, flags);
+ }
+
+ if (flags & SPI_XFER_END)
+ priv->xfer_mode = HSSPI_XFER_MODE_PREPEND;
+
+ return ret;
+}
+
static const struct dm_spi_ops bcm63xx_hsspi_ops = {
.cs_info = bcm63xx_hsspi_cs_info,
.set_mode = bcm63xx_hsspi_set_mode,
@@ -310,6 +527,7 @@ static const struct dm_spi_ops bcm63xx_hsspi_ops = {
static const struct udevice_id bcm63xx_hsspi_ids[] = {
{ .compatible = "brcm,bcm6328-hsspi", },
+ { .compatible = "brcm,bcmbca-hsspi-v1.0", },
{ /* sentinel */ }
};
@@ -317,6 +535,7 @@ static int bcm63xx_hsspi_child_pre_probe(struct udevice *dev)
{
struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
+ struct spi_slave *slave = dev_get_parent_priv(dev);
/* check cs */
if (plat->cs >= priv->num_cs) {
@@ -330,6 +549,13 @@ static int bcm63xx_hsspi_child_pre_probe(struct udevice *dev)
else
priv->cs_pols &= ~BIT(plat->cs);
+ /*
+ * set the max read/write size to make sure each xfer are within the
+ * prepend limit
+ */
+ slave->max_read_size = HSSPI_MAX_DATA_SIZE;
+ slave->max_write_size = HSSPI_MAX_DATA_SIZE;
+
return 0;
}
@@ -391,6 +617,9 @@ static int bcm63xx_hsspi_probe(struct udevice *dev)
priv->cs_pols = readl(priv->regs + SPI_CTL_REG) &
SPI_CTL_CS_POL_MASK;
+ /* default in prepend mode */
+ priv->xfer_mode = HSSPI_XFER_MODE_PREPEND;
+
return 0;
}
diff --git a/drivers/spi/bcmbca_hsspi.c b/drivers/spi/bcmbca_hsspi.c
new file mode 100644
index 00000000000..fbe315a7d45
--- /dev/null
+++ b/drivers/spi/bcmbca_hsspi.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
+ *
+ * Derived from linux/drivers/spi/spi-bcm63xx-hsspi.c:
+ * Copyright (C) 2000-2010 Broadcom Corporation
+ * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org>
+ * Copyright (C) 2021 Broadcom Ltd
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <clk.h>
+#include <spi.h>
+#include <reset.h>
+#include <wait_bit.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+
+#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 BIT(SPI_CTL_CLK_GATE_SHIFT)
+#define SPI_CTL_CLK_POL_SHIFT 17
+#define SPI_CTL_CLK_POL_MASK BIT(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 BIT(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 BIT(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 BIT(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 BIT(SPI_PFL_SIG_LATCHRIS_SHIFT)
+#define SPI_PFL_SIG_LAUNCHRIS_SHIFT 13
+#define SPI_PFL_SIG_LAUNCHRIS_MASK BIT(SPI_PFL_SIG_LAUNCHRIS_SHIFT)
+#define SPI_PFL_SIG_ASYNCIN_SHIFT 16
+#define SPI_PFL_SIG_ASYNCIN_MASK BIT(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 BIT(SPI_PFL_MODE_MDRDSZ_SHIFT)
+#define SPI_PFL_MODE_MDWRSZ_SHIFT 18
+#define SPI_PFL_MODE_MDWRSZ_MASK BIT(SPI_PFL_MODE_MDWRSZ_SHIFT)
+#define SPI_PFL_MODE_3WIRE_SHIFT 20
+#define SPI_PFL_MODE_3WIRE_MASK BIT(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 BIT(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)
+
+#define HSSPI_MAX_DATA_SIZE (HSSPI_FIFO_SIZE - HSSPI_FIFO_OP_SIZE)
+
+#define SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT 0
+#define SPIM_CTRL_CS_OVERRIDE_SEL_MASK 0xff
+#define SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT 8
+#define SPIM_CTRL_CS_OVERRIDE_VAL_MASK 0xff
+
+struct bcmbca_hsspi_priv {
+ void __iomem *regs;
+ void __iomem *spim_ctrl;
+ u32 clk_rate;
+ u8 num_cs;
+ u8 cs_pols;
+ u32 speed;
+};
+
+static int bcmbca_hsspi_cs_info(struct udevice *bus, uint cs,
+ struct spi_cs_info *info)
+{
+ struct bcmbca_hsspi_priv *priv = dev_get_priv(bus);
+
+ if (cs >= priv->num_cs) {
+ dev_err(bus, "no cs %u\n", cs);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bcmbca_hsspi_set_mode(struct udevice *bus, uint mode)
+{
+ struct bcmbca_hsspi_priv *priv = dev_get_priv(bus);
+
+ /* clock polarity */
+ if (mode & SPI_CPOL)
+ setbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_POL_MASK);
+ else
+ clrbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_POL_MASK);
+
+ return 0;
+}
+
+static int bcmbca_hsspi_set_speed(struct udevice *bus, uint speed)
+{
+ struct bcmbca_hsspi_priv *priv = dev_get_priv(bus);
+
+ priv->speed = speed;
+
+ return 0;
+}
+
+static void bcmbca_hsspi_setup_clock(struct bcmbca_hsspi_priv *priv,
+ struct dm_spi_slave_plat *plat)
+{
+ u32 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(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_32(priv->regs + SPI_PFL_SIG_REG(plat->cs), clr, set);
+
+ /* global control */
+ set = 0;
+ clr = 0;
+
+ if (priv->cs_pols & BIT(plat->cs))
+ set |= BIT(plat->cs);
+ else
+ clr |= BIT(plat->cs);
+
+ clrsetbits_32(priv->regs + SPI_CTL_REG, clr, set);
+}
+
+static void bcmbca_hsspi_activate_cs(struct bcmbca_hsspi_priv *priv,
+ struct dm_spi_slave_plat *plat)
+{
+ u32 val;
+
+ /* set the override bit */
+ val = readl(priv->spim_ctrl);
+ val |= BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT);
+ writel(val, priv->spim_ctrl);
+}
+
+static void bcmbca_hsspi_deactivate_cs(struct bcmbca_hsspi_priv *priv,
+ struct dm_spi_slave_plat *plat)
+{
+ u32 val;
+
+ /* clear the cs override bit */
+ val = readl(priv->spim_ctrl);
+ val &= ~BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT);
+ writel(val, priv->spim_ctrl);
+}
+
+static int bcmbca_hsspi_xfer(struct udevice *dev, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ struct bcmbca_hsspi_priv *priv = dev_get_priv(dev->parent);
+ struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
+ size_t data_bytes = bitlen / 8;
+ size_t step_size = HSSPI_FIFO_SIZE;
+ u16 opcode = 0;
+ u32 val = SPI_PFL_MODE_FILL_MASK;
+ const u8 *tx = dout;
+ u8 *rx = din;
+ u32 cs_act = 0;
+
+ if (flags & SPI_XFER_BEGIN)
+ bcmbca_hsspi_setup_clock(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 */
+ if (plat->mode & SPI_RX_DUAL)
+ val |= SPI_PFL_MODE_MDRDSZ_MASK;
+ if (plat->mode & SPI_TX_DUAL)
+ val |= SPI_PFL_MODE_MDWRSZ_MASK;
+ }
+
+ if (plat->mode & SPI_3WIRE)
+ val |= SPI_PFL_MODE_3WIRE_MASK;
+ writel(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(cpu_to_be16(opcode | (curr_step & HSSPI_FIFO_OP_BYTES_MASK)),
+ priv->regs + HSSPI_FIFO_OP_REG);
+
+ /* make sure we keep cs active until spi transfer is done */
+ if (!cs_act) {
+ bcmbca_hsspi_activate_cs(priv, plat);
+ cs_act = 1;
+ }
+
+ /* 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(val, priv->regs + SPI_CMD_REG);
+
+ /* wait for completion */
+ ret = wait_for_bit_32(priv->regs + SPI_STAT_REG,
+ SPI_STAT_SRCBUSY_MASK, false,
+ 1000, false);
+ if (ret) {
+ bcmbca_hsspi_deactivate_cs(priv, plat);
+ dev_err(dev, "interrupt timeout\n");
+ return ret;
+ }
+
+ data_bytes -= curr_step;
+ if ((flags & SPI_XFER_END) && !data_bytes)
+ bcmbca_hsspi_deactivate_cs(priv, plat);
+
+ /* copy rx data */
+ if (rx) {
+ memcpy_fromio(rx, priv->regs + HSSPI_FIFO_BASE,
+ curr_step);
+ rx += curr_step;
+ }
+ }
+
+ return 0;
+}
+
+static const struct dm_spi_ops bcmbca_hsspi_ops = {
+ .cs_info = bcmbca_hsspi_cs_info,
+ .set_mode = bcmbca_hsspi_set_mode,
+ .set_speed = bcmbca_hsspi_set_speed,
+ .xfer = bcmbca_hsspi_xfer,
+};
+
+static const struct udevice_id bcmbca_hsspi_ids[] = {
+ { .compatible = "brcm,bcmbca-hsspi-v1.1", },
+ { /* sentinel */ }
+};
+
+static int bcmbca_hsspi_child_pre_probe(struct udevice *dev)
+{
+ struct bcmbca_hsspi_priv *priv = dev_get_priv(dev->parent);
+ struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
+ u32 val;
+
+ /* check cs */
+ if (plat->cs >= priv->num_cs) {
+ dev_err(dev, "no cs %u\n", plat->cs);
+ return -EINVAL;
+ }
+
+ /* cs polarity */
+ if (plat->mode & SPI_CS_HIGH)
+ priv->cs_pols |= BIT(plat->cs);
+ else
+ priv->cs_pols &= ~BIT(plat->cs);
+
+ /* set the polarity to spim cs register */
+ val = readl(priv->spim_ctrl);
+ val &= ~BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT);
+ if (priv->cs_pols & BIT(plat->cs))
+ val |= BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT);
+ writel(val, priv->spim_ctrl);
+
+ return 0;
+}
+
+static int bcmbca_hsspi_probe(struct udevice *dev)
+{
+ struct bcmbca_hsspi_priv *priv = dev_get_priv(dev);
+ struct clk clk;
+ int ret;
+
+ priv->regs = dev_remap_addr_name(dev, "hsspi");
+ if (!priv->regs)
+ return -EINVAL;
+
+ priv->spim_ctrl = dev_remap_addr_name(dev, "spim-ctrl");
+ if (!priv->spim_ctrl) {
+ dev_err(dev, "misc spim ctrl register not defined in dts!\n");
+ return -EINVAL;
+ }
+
+ priv->num_cs = dev_read_u32_default(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 && ret != -ENOSYS)
+ return ret;
+
+ clk_free(&clk);
+
+ /* get clock rate */
+ ret = clk_get_by_name(dev, "pll", &clk);
+ if (ret < 0 && ret != -ENOSYS)
+ return ret;
+
+ priv->clk_rate = clk_get_rate(&clk);
+
+ clk_free(&clk);
+
+ /* initialize hardware */
+ writel(0, priv->regs + SPI_IR_MASK_REG);
+
+ /* clear pending interrupts */
+ writel(SPI_IR_CLEAR_ALL, priv->regs + SPI_IR_STAT_REG);
+
+ /* enable clk gate */
+ setbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_GATE_MASK);
+
+ /* read default cs polarities */
+ priv->cs_pols = readl(priv->regs + SPI_CTL_REG) &
+ SPI_CTL_CS_POL_MASK;
+
+ dev_info(dev, "Broadcom BCMBCA HS SPI bus driver\n");
+ return 0;
+}
+
+U_BOOT_DRIVER(bcmbca_hsspi) = {
+ .name = "bcmbca_hsspi",
+ .id = UCLASS_SPI,
+ .of_match = bcmbca_hsspi_ids,
+ .ops = &bcmbca_hsspi_ops,
+ .priv_auto = sizeof(struct bcmbca_hsspi_priv),
+ .child_pre_probe = bcmbca_hsspi_child_pre_probe,
+ .probe = bcmbca_hsspi_probe,
+};
diff --git a/drivers/spi/npcm_pspi.c b/drivers/spi/npcm_pspi.c
index bd9ac654113..37bab709672 100644
--- a/drivers/spi/npcm_pspi.c
+++ b/drivers/spi/npcm_pspi.c
@@ -40,7 +40,7 @@ static inline void spi_cs_activate(struct udevice *dev)
struct udevice *bus = dev->parent;
struct npcm_pspi_priv *priv = dev_get_priv(bus);
- dm_gpio_set_value(&priv->cs_gpio, 0);
+ dm_gpio_set_value(&priv->cs_gpio, 1);
}
static inline void spi_cs_deactivate(struct udevice *dev)
@@ -48,7 +48,7 @@ static inline void spi_cs_deactivate(struct udevice *dev)
struct udevice *bus = dev->parent;
struct npcm_pspi_priv *priv = dev_get_priv(bus);
- dm_gpio_set_value(&priv->cs_gpio, 1);
+ dm_gpio_set_value(&priv->cs_gpio, 0);
}
static inline void npcm_pspi_enable(struct npcm_pspi_priv *priv)
@@ -122,6 +122,9 @@ static int npcm_pspi_xfer(struct udevice *dev, unsigned int bitlen,
if (flags & SPI_XFER_END)
spi_cs_deactivate(dev);
+ debug("npcm_pspi_xfer: slave %s:%s dout %08X din %08X bitlen %u\n",
+ dev->parent->name, dev->name, *(uint *)tx, *(uint *)rx, bitlen);
+
npcm_pspi_disable(priv);
return ret;
@@ -183,6 +186,7 @@ static int npcm_pspi_set_mode(struct udevice *bus, uint mode)
val |= pspi_mode;
writew(val, priv->base + PSPI_CTL1);
+ debug("%s: mode=%u\n", __func__, mode);
return 0;
}
@@ -197,9 +201,9 @@ static int npcm_pspi_probe(struct udevice *bus)
return ret;
priv->base = dev_read_addr_ptr(bus);
- priv->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 0);
+ priv->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 1000000);
gpio_request_by_name_nodev(offset_to_ofnode(node), "cs-gpios", 0,
- &priv->cs_gpio, GPIOD_IS_OUT);
+ &priv->cs_gpio, GPIOD_IS_OUT| GPIOD_ACTIVE_LOW);
return 0;
}
diff --git a/drivers/spi/pl022_spi.c b/drivers/spi/pl022_spi.c
index 828eab3d342..fc7388b379d 100644
--- a/drivers/spi/pl022_spi.c
+++ b/drivers/spi/pl022_spi.c
@@ -12,9 +12,11 @@
#include <clk.h>
#include <common.h>
#include <dm.h>
-#include <dm/platform_data/spi_pl022.h>
+#include <dm/device_compat.h>
+#include <fdtdec.h>
#include <linux/io.h>
#include <asm/global_data.h>
+#include <asm/gpio.h>
#include <spi.h>
#define SSP_CR0 0x000
@@ -66,6 +68,15 @@
#define SSP_SR_MASK_RFF (0x1 << 3) /* Receive FIFO full */
#define SSP_SR_MASK_BSY (0x1 << 4) /* Busy Flag */
+struct pl022_spi_pdata {
+ fdt_addr_t addr;
+ fdt_size_t size;
+ unsigned int freq;
+#if CONFIG_IS_ENABLED(DM_GPIO)
+ struct gpio_desc cs_gpio;
+#endif
+};
+
struct pl022_spi_slave {
void *base;
unsigned int freq;
@@ -107,7 +118,7 @@ static int pl022_spi_probe(struct udevice *bus)
return 0;
}
-static void flush(struct pl022_spi_slave *ps)
+static void pl022_spi_flush(struct pl022_spi_slave *ps)
{
do {
while (readw(ps->base + SSP_SR) & SSP_SR_MASK_RNE)
@@ -126,7 +137,7 @@ static int pl022_spi_claim_bus(struct udevice *dev)
reg |= SSP_CR1_MASK_SSE;
writew(reg, ps->base + SSP_CR1);
- flush(ps);
+ pl022_spi_flush(ps);
return 0;
}
@@ -137,7 +148,7 @@ static int pl022_spi_release_bus(struct udevice *dev)
struct pl022_spi_slave *ps = dev_get_priv(bus);
u16 reg;
- flush(ps);
+ pl022_spi_flush(ps);
/* Disable the SPI hardware */
reg = readw(ps->base + SSP_CR1);
@@ -147,6 +158,17 @@ static int pl022_spi_release_bus(struct udevice *dev)
return 0;
}
+static void pl022_spi_set_cs(struct udevice *dev, bool on)
+{
+#if CONFIG_IS_ENABLED(DM_GPIO)
+ struct udevice *bus = dev->parent;
+ struct pl022_spi_pdata *plat = dev_get_plat(bus);
+
+ if (dm_gpio_is_valid(&plat->cs_gpio))
+ dm_gpio_set_value(&plat->cs_gpio, on ? 1 : 0);
+#endif
+}
+
static int pl022_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
@@ -159,7 +181,7 @@ static int pl022_spi_xfer(struct udevice *dev, unsigned int bitlen,
if (bitlen == 0)
/* Finish any previously submitted transfers */
- return 0;
+ goto done;
/*
* TODO: The controller can do non-multiple-of-8 bit
@@ -172,9 +194,13 @@ static int pl022_spi_xfer(struct udevice *dev, unsigned int bitlen,
if (bitlen % 8) {
/* Errors always terminate an ongoing transfer */
flags |= SPI_XFER_END;
- return -1;
+ ret = -1;
+ goto done;
}
+ if (flags & SPI_XFER_BEGIN)
+ pl022_spi_set_cs(dev, true);
+
len = bitlen / 8;
while (len_tx < len) {
@@ -201,6 +227,10 @@ static int pl022_spi_xfer(struct udevice *dev, unsigned int bitlen,
}
}
+done:
+ if (flags & SPI_XFER_END)
+ pl022_spi_set_cs(dev, false);
+
return ret;
}
@@ -303,11 +333,18 @@ static int pl022_spi_of_to_plat(struct udevice *bus)
plat->freq = clk_get_rate(&clkdev);
+#if CONFIG_IS_ENABLED(DM_GPIO)
+ ret = gpio_request_by_name(bus, "cs-gpios", 0, &plat->cs_gpio,
+ GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+ if (ret < 0 && ret != -ENOENT)
+ return ret;
+#endif
+
return 0;
}
static const struct udevice_id pl022_spi_ids[] = {
- { .compatible = "arm,pl022-spi" },
+ { .compatible = "arm,pl022" },
{ }
};
#endif
diff --git a/drivers/spi/soft_spi.c b/drivers/spi/soft_spi.c
index f3602a25ba3..0fa14339bdc 100644
--- a/drivers/spi/soft_spi.c
+++ b/drivers/spi/soft_spi.c
@@ -248,20 +248,33 @@ static int soft_spi_probe(struct udevice *dev)
cs_flags = (slave && slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW;
clk_flags = (slave && slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0;
- if (gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs,
- GPIOD_IS_OUT | cs_flags) ||
- gpio_request_by_name(dev, "gpio-sck", 0, &plat->sclk,
- GPIOD_IS_OUT | clk_flags))
+ ret = gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs,
+ GPIOD_IS_OUT | cs_flags);
+ if (ret)
+ return -EINVAL;
+
+ ret = gpio_request_by_name(dev, "gpio-sck", 0, &plat->sclk,
+ GPIOD_IS_OUT | clk_flags);
+ if (ret)
+ ret = gpio_request_by_name(dev, "sck-gpios", 0, &plat->sclk,
+ GPIOD_IS_OUT | clk_flags);
+ if (ret)
return -EINVAL;
ret = gpio_request_by_name(dev, "gpio-mosi", 0, &plat->mosi,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
if (ret)
+ ret = gpio_request_by_name(dev, "mosi-gpios", 0, &plat->mosi,
+ GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+ if (ret)
plat->flags |= SPI_MASTER_NO_TX;
ret = gpio_request_by_name(dev, "gpio-miso", 0, &plat->miso,
GPIOD_IS_IN);
if (ret)
+ ret = gpio_request_by_name(dev, "gpio-miso", 0, &plat->miso,
+ GPIOD_IS_IN);
+ if (ret)
plat->flags |= SPI_MASTER_NO_RX;
if ((plat->flags & (SPI_MASTER_NO_RX | SPI_MASTER_NO_TX)) ==
diff --git a/drivers/spi/spi-synquacer.c b/drivers/spi/spi-synquacer.c
index 0f5d0a30c39..553f9687e3b 100644
--- a/drivers/spi/spi-synquacer.c
+++ b/drivers/spi/spi-synquacer.c
@@ -186,7 +186,7 @@ static void synquacer_spi_config(struct udevice *dev, void *rx, const void *tx)
struct udevice *bus = dev->parent;
struct synquacer_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
- u32 val, div, bus_width = 1;
+ u32 val, div, bus_width;
int rwflag;
rwflag = (rx ? 1 : 0) | (tx ? 2 : 0);
@@ -203,16 +203,14 @@ static void synquacer_spi_config(struct udevice *dev, void *rx, const void *tx)
priv->mode = slave_plat->mode;
priv->speed = slave_plat->max_hz;
- if (priv->mode & SPI_TX_BYTE)
- bus_width = 1;
- else if (priv->mode & SPI_TX_DUAL)
+ if (priv->mode & SPI_TX_DUAL)
bus_width = 2;
else if (priv->mode & SPI_TX_QUAD)
bus_width = 4;
else if (priv->mode & SPI_TX_OCTAL)
bus_width = 8;
else
- log_warning("SPI mode not configured, setting to byte mode\n");
+ bus_width = 1; /* default is single bit mode */
div = DIV_ROUND_UP(125000000, priv->speed);
diff --git a/include/dm/platform_data/spi_pl022.h b/include/dm/platform_data/spi_pl022.h
deleted file mode 100644
index 7f74b3cbc5c..00000000000
--- a/include/dm/platform_data/spi_pl022.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * (C) Copyright 2018
- * Quentin Schulz, Bootlin, quentin.schulz@bootlin.com
- *
- * Structure for use with U_BOOT_DRVINFO for pl022 SPI devices or to use
- * in of_to_plat.
- */
-
-#ifndef __spi_pl022_h
-#define __spi_pl022_h
-
-#include <fdtdec.h>
-
-struct pl022_spi_pdata {
- fdt_addr_t addr;
- fdt_size_t size;
- unsigned int freq;
-};
-
-#endif /* __spi_pl022_h */