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)
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 0000000..6554978
--- /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 dfb5066..bdf7e86 100644
--- a/doc/device-tree-bindings/spi/soft-spi.txt
+++ b/doc/device-tree-bindings/spi/soft-spi.txt
@@ -9,10 +9,10 @@
 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 @@
 	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 3f8b796..4587215 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -446,6 +446,11 @@
 			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 @@
 	{ 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 453a598..854b8b8 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -101,12 +101,21 @@
 
 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 95dba9a..c27b332 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -25,6 +25,7 @@
 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 4d714ad..a24bb43 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 @@
 				   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 @@
 		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 @@
 	set = 0;
 	clr = 0;
 
-	/* invert 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);
+		/* 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 @@
  * 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 @@
 		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 @@
 	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 udevice_id bcm63xx_hsspi_ids[] = {
 	{ .compatible = "brcm,bcm6328-hsspi", },
+	{ .compatible = "brcm,bcmbca-hsspi-v1.0", },
 	{ /* sentinel */ }
 };
 
@@ -317,6 +535,7 @@
 {
 	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 @@
 	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 @@
 	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 0000000..fbe315a
--- /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 bd9ac65..37bab70 100644
--- a/drivers/spi/npcm_pspi.c
+++ b/drivers/spi/npcm_pspi.c
@@ -40,7 +40,7 @@
 	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 @@
 	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 @@
 	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 @@
 	val |= pspi_mode;
 	writew(val, priv->base + PSPI_CTL1);
 
+	debug("%s: mode=%u\n", __func__, mode);
 	return 0;
 }
 
@@ -197,9 +201,9 @@
 		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 828eab3..fc7388b 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 @@
 	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 @@
 	reg |= SSP_CR1_MASK_SSE;
 	writew(reg, ps->base + SSP_CR1);
 
-	flush(ps);
+	pl022_spi_flush(ps);
 
 	return 0;
 }
@@ -137,7 +148,7 @@
 	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 @@
 	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 @@
 
 	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 @@
 	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 @@
 		}
 	}
 
+done:
+	if (flags & SPI_XFER_END)
+		pl022_spi_set_cs(dev, false);
+
 	return ret;
 }
 
@@ -303,11 +333,18 @@
 
 	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 f3602a2..0fa1433 100644
--- a/drivers/spi/soft_spi.c
+++ b/drivers/spi/soft_spi.c
@@ -248,20 +248,33 @@
 	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 0f5d0a3..553f968 100644
--- a/drivers/spi/spi-synquacer.c
+++ b/drivers/spi/spi-synquacer.c
@@ -186,7 +186,7 @@
 	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 @@
 	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 7f74b3c..0000000
--- 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 */