All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates
@ 2020-07-23 10:17 Stefan Roese
  2020-07-23 10:17 ` [PATCH v2 01/10] gpio: octeon_gpio: Add GPIO controller driver for Octeon Stefan Roese
                   ` (9 more replies)
  0 siblings, 10 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-23 10:17 UTC (permalink / raw)
  To: u-boot


This patchset includes the following parts:
- Octeon GPIO driver
- Octeon SPI driver
- New simple Octeon clk driver
- DT updates (GPIO, SPI, I2C & CLK nodes)
- Kconfig & defconfig updates

In v2 I've added the SPI driver as well, so combine all the drivers /
patches in one patchset, that have been sent to the list before. Also
GPIO and SPI have now been tested on ARM Octeon TX2 and have undergone
minor changes because of this.

Thanks,
Stefan

Changes in v2:
- Tested on MIPS Octeon and ARM Octeon TX2
- Removed "struct pci_device_id" definition and U_BOOT_PCI_DEVICE()
  as its not needed for the PCI based probing on Octeon TX2
- Added "depends on DM_PCI" to Kconfig
- Newly added to this series
- Removed inclusion of "common.h"
- Added "depends on DM_PCI" to Kconfig
- Tested on MIPS Octeon and ARM Octeon TX2
- Fixed issues with Octeon TX2 registration. Now only one driver is
  registered and the "ops" is overwritten in the Octeon TX2 case.
- Use dev_get_driver_data() to get the driver data struct
- Removed "struct pci_device_id" definition and U_BOOT_PCI_DEVICE()
  as its not needed for the PCI based probing on Octeon TX2
- Newly added to this series
- Newly added to this series
- Kconfig and defconfig changes separated into 2 patches
- Kconfig and defconfig changes separated into 2 patches

Changes in v1:
- Removed common.h and reduced headers as suggested by Daniel

Stefan Roese (8):
  mips: octeon: mrvl,cn73xx.dtsi: Add GPIO DT nodes
  mips: octeon: dts: Add I2C DT nodes
  clk: clk_octeon: Add simple MIPS Octeon clock driver
  mips: octeon: dts: Add Octeon clock driver DT nodes
  mips: octeon: mrvl,cn73xx.dtsi: Add SPI DT node
  mips: octeon: mrvl,octeon-ebb7304.dts: Add SPI flash DT node
  mips: octeon: Update Octeon Kconfig
  mips: octeon: Update EBB7304 defconfig

Suneel Garapati (2):
  gpio: octeon_gpio: Add GPIO controller driver for Octeon
  drivers: spi: Add SPI controller driver for Octeon

 arch/mips/Kconfig                        |   6 +-
 arch/mips/dts/mrvl,cn73xx.dtsi           |  66 +++
 arch/mips/dts/mrvl,octeon-ebb7304.dts    |  21 +-
 configs/octeon_ebb7304_defconfig         |  10 +
 drivers/clk/Kconfig                      |   7 +
 drivers/clk/Makefile                     |   1 +
 drivers/clk/clk_octeon.c                 |  72 +++
 drivers/gpio/Kconfig                     |  10 +
 drivers/gpio/Makefile                    |   1 +
 drivers/gpio/octeon_gpio.c               | 242 +++++++++
 drivers/spi/Kconfig                      |   8 +
 drivers/spi/Makefile                     |   1 +
 drivers/spi/octeon_spi.c                 | 647 +++++++++++++++++++++++
 include/dt-bindings/clock/octeon-clock.h |  12 +
 14 files changed, 1101 insertions(+), 3 deletions(-)
 create mode 100644 drivers/clk/clk_octeon.c
 create mode 100644 drivers/gpio/octeon_gpio.c
 create mode 100644 drivers/spi/octeon_spi.c
 create mode 100644 include/dt-bindings/clock/octeon-clock.h

-- 
2.27.0

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 01/10] gpio: octeon_gpio: Add GPIO controller driver for Octeon
  2020-07-23 10:17 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
@ 2020-07-23 10:17 ` Stefan Roese
  2020-07-23 10:17 ` [PATCH v2 02/10] mips: octeon: mrvl,cn73xx.dtsi: Add GPIO DT nodes Stefan Roese
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-23 10:17 UTC (permalink / raw)
  To: u-boot

From: Suneel Garapati <sgarapati@marvell.com>

Add support for GPIO controllers found on Octeon II/III and Octeon TX
TX2 SoC platforms.

Signed-off-by: Aaron Williams <awilliams@marvell.com>
Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
Signed-off-by: Stefan Roese <sr@denx.de>
Reviewed-by: Simon Glass <sjg@chromium.org>

---

Changes in v2:
- Tested on MIPS Octeon and ARM Octeon TX2
- Removed "struct pci_device_id" definition and U_BOOT_PCI_DEVICE()
  as its not needed for the PCI based probing on Octeon TX2
- Added "depends on DM_PCI" to Kconfig

Changes in v1:
- Removed common.h and reduced headers as suggested by Daniel

 drivers/gpio/Kconfig       |  10 ++
 drivers/gpio/Makefile      |   1 +
 drivers/gpio/octeon_gpio.c | 242 +++++++++++++++++++++++++++++++++++++
 3 files changed, 253 insertions(+)
 create mode 100644 drivers/gpio/octeon_gpio.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 0e8ad9530d..f8a48dc675 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -335,6 +335,16 @@ config PIC32_GPIO
 	help
 	  Say yes here to support Microchip PIC32 GPIOs.
 
+config OCTEON_GPIO
+	bool "Octeon II/III/TX/TX2 GPIO driver"
+	depends on DM_GPIO && DM_PCI &&	(ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2)
+	default y
+	help
+	  Add support for the Marvell Octeon GPIO driver. This is used with
+	  various Octeon parts such as Octeon II/III and OcteonTX/TX2.
+	  Octeon II/III has 32 GPIOs (count defined via DT) and OcteonTX/TX2
+	  has 64 GPIOs (count defined via internal register).
+
 config STM32_GPIO
 	bool "ST STM32 GPIO driver"
 	depends on DM_GPIO && (ARCH_STM32 || ARCH_STM32MP)
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 7638259007..eb6364adb4 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_HIKEY_GPIO)	+= hi6220_gpio.o
 obj-$(CONFIG_HSDK_CREG_GPIO)	+= hsdk-creg-gpio.o
 obj-$(CONFIG_IMX_RGPIO2P)	+= imx_rgpio2p.o
 obj-$(CONFIG_PIC32_GPIO)	+= pic32_gpio.o
+obj-$(CONFIG_OCTEON_GPIO)	+= octeon_gpio.o
 obj-$(CONFIG_MVEBU_GPIO)	+= mvebu_gpio.o
 obj-$(CONFIG_MSM_GPIO)		+= msm_gpio.o
 obj-$(CONFIG_$(SPL_)PCF8575_GPIO)	+= pcf8575_gpio.o
diff --git a/drivers/gpio/octeon_gpio.c b/drivers/gpio/octeon_gpio.c
new file mode 100644
index 0000000000..45acaadcdb
--- /dev/null
+++ b/drivers/gpio/octeon_gpio.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier:    GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ *
+ * (C) Copyright 2011
+ * eInfochips Ltd. <www.einfochips.com>
+ * Written-by: Ajay Bhargav <ajay.bhargav@einfochips.com>
+ *
+ * (C) Copyright 2010
+ * Marvell Semiconductor <www.marvell.com>
+ */
+
+#include <dm.h>
+#include <pci.h>
+#include <pci_ids.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <linux/bitfield.h>
+#include <linux/compat.h>
+#include <dt-bindings/gpio/gpio.h>
+
+/* Returns the bit value to write or read based on the offset */
+#define GPIO_BIT(x)		BIT_ULL((x) & 0x3f)
+
+#define GPIO_RX_DAT		0x00
+#define GPIO_TX_SET		0x08
+#define GPIO_TX_CLR		0x10
+#define GPIO_CONST		0x90	/* OcteonTX only */
+
+/* Offset to register-set for 2nd GPIOs (> 63), OcteonTX only */
+#define GPIO1_OFFSET		0x1400
+
+/* GPIO_CONST register bits */
+#define GPIO_CONST_GPIOS_MASK	GENMASK_ULL(7, 0)
+
+/* GPIO_BIT_CFG register bits */
+#define GPIO_BIT_CFG_TX_OE	BIT_ULL(0)
+#define GPIO_BIT_CFG_PIN_XOR	BIT_ULL(1)
+#define GPIO_BIT_CFG_INT_EN	BIT_ULL(2)
+#define GPIO_BIT_CFG_PIN_SEL_MASK GENMASK_ULL(26, 16)
+
+enum {
+	PROBE_PCI = 0,		/* PCI based probing */
+	PROBE_DT,		/* DT based probing */
+};
+
+struct octeon_gpio_data {
+	int probe;
+	u32 reg_offs;
+	u32 gpio_bit_cfg_offs;
+};
+
+struct octeon_gpio {
+	void __iomem *base;
+	const struct octeon_gpio_data *data;
+};
+
+/* Returns the offset to the output register based on the offset and value */
+static u32 gpio_tx_reg(int offset, int value)
+{
+	u32 ret;
+
+	ret = value ? GPIO_TX_SET : GPIO_TX_CLR;
+	if (offset > 63)
+		ret += GPIO1_OFFSET;
+
+	return ret;
+}
+
+/* Returns the offset to the input data register based on the offset */
+static u32 gpio_rx_dat_reg(int offset)
+{
+	u32 ret;
+
+	ret = GPIO_RX_DAT;
+	if (offset > 63)
+		ret += GPIO1_OFFSET;
+
+	return ret;
+}
+
+static int octeon_gpio_dir_input(struct udevice *dev, unsigned int offset)
+{
+	struct octeon_gpio *gpio = dev_get_priv(dev);
+
+	debug("%s(%s, %u)\n", __func__, dev->name, offset);
+	clrbits_64(gpio->base + gpio->data->gpio_bit_cfg_offs + 8 * offset,
+		   GPIO_BIT_CFG_TX_OE | GPIO_BIT_CFG_PIN_XOR |
+		   GPIO_BIT_CFG_INT_EN | GPIO_BIT_CFG_PIN_SEL_MASK);
+
+	return 0;
+}
+
+static int octeon_gpio_dir_output(struct udevice *dev, unsigned int offset,
+				  int value)
+{
+	struct octeon_gpio *gpio = dev_get_priv(dev);
+
+	debug("%s(%s, %u, %d)\n", __func__, dev->name, offset, value);
+	writeq(GPIO_BIT(offset), gpio->base + gpio->data->reg_offs +
+	       gpio_tx_reg(offset, value));
+
+	clrsetbits_64(gpio->base + gpio->data->gpio_bit_cfg_offs + 8 * offset,
+		      GPIO_BIT_CFG_PIN_SEL_MASK | GPIO_BIT_CFG_INT_EN,
+		      GPIO_BIT_CFG_TX_OE);
+
+	return 0;
+}
+
+static int octeon_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+	struct octeon_gpio *gpio = dev_get_priv(dev);
+	u64 reg = readq(gpio->base + gpio->data->reg_offs +
+			gpio_rx_dat_reg(offset));
+
+	debug("%s(%s, %u): value: %d\n", __func__, dev->name, offset,
+	      !!(reg & GPIO_BIT(offset)));
+
+	return !!(reg & GPIO_BIT(offset));
+}
+
+static int octeon_gpio_set_value(struct udevice *dev,
+				 unsigned int offset, int value)
+{
+	struct octeon_gpio *gpio = dev_get_priv(dev);
+
+	debug("%s(%s, %u, %d)\n", __func__, dev->name, offset, value);
+	writeq(GPIO_BIT(offset), gpio->base + gpio->data->reg_offs +
+	       gpio_tx_reg(offset, value));
+
+	return 0;
+}
+
+static int octeon_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+	struct octeon_gpio *gpio = dev_get_priv(dev);
+	u64 val = readq(gpio->base + gpio->data->gpio_bit_cfg_offs +
+			8 * offset);
+	int pin_sel;
+
+	debug("%s(%s, %u): GPIO_BIT_CFG: 0x%llx\n", __func__, dev->name,
+	      offset, val);
+	pin_sel = FIELD_GET(GPIO_BIT_CFG_PIN_SEL_MASK, val);
+	if (pin_sel)
+		return GPIOF_FUNC;
+	else if (val & GPIO_BIT_CFG_TX_OE)
+		return GPIOF_OUTPUT;
+	else
+		return GPIOF_INPUT;
+}
+
+static int octeon_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
+			     struct ofnode_phandle_args *args)
+{
+	if (args->args_count < 1)
+		return -EINVAL;
+
+	desc->offset = args->args[0];
+	desc->flags = 0;
+	if (args->args_count > 1) {
+		if (args->args[1] & GPIO_ACTIVE_LOW)
+			desc->flags |= GPIOD_ACTIVE_LOW;
+		/* In the future add tri-state flag support */
+	}
+	return 0;
+}
+
+static const struct dm_gpio_ops octeon_gpio_ops = {
+	.direction_input	= octeon_gpio_dir_input,
+	.direction_output	= octeon_gpio_dir_output,
+	.get_value		= octeon_gpio_get_value,
+	.set_value		= octeon_gpio_set_value,
+	.get_function		= octeon_gpio_get_function,
+	.xlate			= octeon_gpio_xlate,
+};
+
+static int octeon_gpio_probe(struct udevice *dev)
+{
+	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+	struct octeon_gpio *priv = dev_get_priv(dev);
+	char *end;
+
+	priv->data = (const struct octeon_gpio_data *)dev_get_driver_data(dev);
+
+	if (priv->data->probe == PROBE_PCI) {
+		priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
+					    PCI_REGION_MEM);
+		uc_priv->gpio_count = readq(priv->base +
+					    priv->data->reg_offs + GPIO_CONST) &
+			GPIO_CONST_GPIOS_MASK;
+	} else {
+		priv->base = dev_remap_addr(dev);
+		uc_priv->gpio_count = ofnode_read_u32_default(dev->node,
+							      "nr-gpios", 32);
+	}
+
+	if (!priv->base) {
+		debug("%s(%s): Could not get base address\n",
+		      __func__, dev->name);
+		return -ENODEV;
+	}
+
+	uc_priv->bank_name  = strdup(dev->name);
+	end = strchr(uc_priv->bank_name, '@');
+	end[0] = 'A' + dev->seq;
+	end[1] = '\0';
+
+	debug("%s(%s): base address: %p, pin count: %d\n",
+	      __func__, dev->name, priv->base, uc_priv->gpio_count);
+
+	return 0;
+}
+
+static const struct octeon_gpio_data gpio_octeon_data = {
+	.probe = PROBE_DT,
+	.reg_offs = 0x80,
+	.gpio_bit_cfg_offs = 0x100,
+};
+
+static const struct octeon_gpio_data gpio_octeontx_data = {
+	.probe = PROBE_PCI,
+	.reg_offs = 0x00,
+	.gpio_bit_cfg_offs = 0x400,
+};
+
+static const struct udevice_id octeon_gpio_ids[] = {
+	{ .compatible = "cavium,thunder-8890-gpio",
+	  .data = (ulong)&gpio_octeontx_data },
+	{ .compatible = "cavium,octeon-7890-gpio",
+	  .data = (ulong)&gpio_octeon_data },
+	{ }
+};
+
+U_BOOT_DRIVER(octeon_gpio) = {
+	.name	= "octeon_gpio",
+	.id	= UCLASS_GPIO,
+	.of_match = of_match_ptr(octeon_gpio_ids),
+	.probe = octeon_gpio_probe,
+	.priv_auto_alloc_size = sizeof(struct octeon_gpio),
+	.ops	= &octeon_gpio_ops,
+	.flags	= DM_FLAG_PRE_RELOC,
+};
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 02/10] mips: octeon: mrvl,cn73xx.dtsi: Add GPIO DT nodes
  2020-07-23 10:17 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
  2020-07-23 10:17 ` [PATCH v2 01/10] gpio: octeon_gpio: Add GPIO controller driver for Octeon Stefan Roese
@ 2020-07-23 10:17 ` Stefan Roese
  2020-07-23 10:17 ` [PATCH v2 03/10] mips: octeon: dts: Add I2C " Stefan Roese
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-23 10:17 UTC (permalink / raw)
  To: u-boot

Add the Octeon GPIO DT node to the dtsi file.

Signed-off-by: Stefan Roese <sr@denx.de>
---

(no changes since v1)

 arch/mips/dts/mrvl,cn73xx.dtsi | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/arch/mips/dts/mrvl,cn73xx.dtsi b/arch/mips/dts/mrvl,cn73xx.dtsi
index a7bd55f8ad..8d32a49795 100644
--- a/arch/mips/dts/mrvl,cn73xx.dtsi
+++ b/arch/mips/dts/mrvl,cn73xx.dtsi
@@ -38,6 +38,32 @@
 			#size-cells = <1>;
 		};
 
+		gpio: gpio-controller at 1070000000800 {
+			#gpio-cells = <2>;
+			compatible = "cavium,octeon-7890-gpio";
+			reg = <0x10700 0x00000800 0x0 0x100>;
+			gpio-controller;
+			nr-gpios = <32>;
+			/* Interrupts are specified by two parts:
+			 * 1) GPIO pin number (0..15)
+			 * 2) Triggering (1 - edge rising
+			 *		  2 - edge falling
+			 *		  4 - level active high
+			 *		  8 - level active low)
+			 */
+			interrupt-controller;
+			#interrupt-cells = <2>;
+			/* The GPIO pins connect to 16 consecutive CUI bits */
+			interrupts = <0x03000 4>, <0x03001 4>,
+				     <0x03002 4>, <0x03003 4>,
+				     <0x03004 4>, <0x03005 4>,
+				     <0x03006 4>, <0x03007 4>,
+				     <0x03008 4>, <0x03009 4>,
+				     <0x0300a 4>, <0x0300b 4>,
+				     <0x0300c 4>, <0x0300d 4>,
+				     <0x0300e 4>, <0x0300f 4>;
+		};
+
 		reset: reset at 1180006001600 {
 			compatible = "mrvl,cn7xxx-rst";
 			reg = <0x11800 0x06001600 0x0 0x200>;
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 03/10] mips: octeon: dts: Add I2C DT nodes
  2020-07-23 10:17 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
  2020-07-23 10:17 ` [PATCH v2 01/10] gpio: octeon_gpio: Add GPIO controller driver for Octeon Stefan Roese
  2020-07-23 10:17 ` [PATCH v2 02/10] mips: octeon: mrvl,cn73xx.dtsi: Add GPIO DT nodes Stefan Roese
@ 2020-07-23 10:17 ` Stefan Roese
  2020-07-23 10:17 ` [PATCH v2 04/10] clk: clk_octeon: Add simple MIPS Octeon clock driver Stefan Roese
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-23 10:17 UTC (permalink / raw)
  To: u-boot

Add I2C DT nodes to the Octeon dts / dtsi files.

Signed-off-by: Stefan Roese <sr@denx.de>
---

(no changes since v1)

 arch/mips/dts/mrvl,cn73xx.dtsi        | 20 ++++++++++++++++++++
 arch/mips/dts/mrvl,octeon-ebb7304.dts | 10 ++++++++++
 2 files changed, 30 insertions(+)

diff --git a/arch/mips/dts/mrvl,cn73xx.dtsi b/arch/mips/dts/mrvl,cn73xx.dtsi
index 8d32a49795..4c7b6e4160 100644
--- a/arch/mips/dts/mrvl,cn73xx.dtsi
+++ b/arch/mips/dts/mrvl,cn73xx.dtsi
@@ -86,5 +86,25 @@
 			reg-shift = <3>;
 			interrupts = <0x08040 4>;
 		};
+
+		i2c0: i2c at 1180000001000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "cavium,octeon-7890-twsi";
+			reg = <0x11800 0x00001000 0x0 0x200>;
+			/* INT_ST, INT_TS, INT_CORE */
+			interrupts = <0x0b000 1>, <0x0b001 1>, <0x0b002 1>;
+			clock-frequency = <100000>;
+		};
+
+		i2c1: i2c at 1180000001200 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "cavium,octeon-7890-twsi";
+			reg = <0x11800 0x00001200 0x0 0x200>;
+			/* INT_ST, INT_TS, INT_CORE */
+			interrupts = <0x0b100 1>, <0x0b101 1>, <0x0b102 1>;
+			clock-frequency = <100000>;
+		};
 	};
 };
diff --git a/arch/mips/dts/mrvl,octeon-ebb7304.dts b/arch/mips/dts/mrvl,octeon-ebb7304.dts
index 4e9c2de7d4..096e5c8f66 100644
--- a/arch/mips/dts/mrvl,octeon-ebb7304.dts
+++ b/arch/mips/dts/mrvl,octeon-ebb7304.dts
@@ -94,3 +94,13 @@
 &uart0 {
 	clock-frequency = <1200000000>;
 };
+
+&i2c0 {
+	u-boot,dm-pre-reloc;	/* Needed early for DDR SPD EEPROM */
+	clock-frequency = <100000>;
+};
+
+&i2c1 {
+	u-boot,dm-pre-reloc;	/* Needed early for DDR SPD EEPROM */
+	clock-frequency = <100000>;
+};
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 04/10] clk: clk_octeon: Add simple MIPS Octeon clock driver
  2020-07-23 10:17 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
                   ` (2 preceding siblings ...)
  2020-07-23 10:17 ` [PATCH v2 03/10] mips: octeon: dts: Add I2C " Stefan Roese
@ 2020-07-23 10:17 ` Stefan Roese
  2020-07-23 10:17 ` [PATCH v2 05/10] mips: octeon: dts: Add Octeon clock driver DT nodes Stefan Roese
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-23 10:17 UTC (permalink / raw)
  To: u-boot

This patch adds a simple clock driver for the Marvell Octeon MIPS SoC
family. Its for IO clock rate passing via DT in some of the Octeon
driver, like I2C. So that we don't need to use the non-mainline API
octeon_get_io_clock().

Signed-off-by: Stefan Roese <sr@denx.de>
Cc: Lukasz Majewski <lukma@denx.de>
---

(no changes since v1)

 drivers/clk/Kconfig                      |  7 +++
 drivers/clk/Makefile                     |  1 +
 drivers/clk/clk_octeon.c                 | 72 ++++++++++++++++++++++++
 include/dt-bindings/clock/octeon-clock.h | 12 ++++
 4 files changed, 92 insertions(+)
 create mode 100644 drivers/clk/clk_octeon.c
 create mode 100644 include/dt-bindings/clock/octeon-clock.h

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 82cb1874e1..6003e140b5 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -83,6 +83,13 @@ config CLK_INTEL
 	  set up by U-Boot itself but only statically. Thus the driver does not
 	  support changing clock rates, only querying them.
 
+config CLK_OCTEON
+	bool "Clock controller driver for Marvell MIPS Octeon"
+	depends on CLK && ARCH_OCTEON
+	default y
+	help
+	  Enable this to support the clocks on Octeon MIPS platforms.
+
 config CLK_STM32F
 	bool "Enable clock driver support for STM32F family"
 	depends on CLK && (STM32F7 || STM32F4)
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d911954581..cda4b4b605 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_$(SPL_TPL_)CLK_INTEL) += intel/
 obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o
 obj-$(CONFIG_CLK_K210) += kendryte/
 obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o
+obj-$(CONFIG_CLK_OCTEON) += clk_octeon.o
 obj-$(CONFIG_CLK_OWL) += owl/
 obj-$(CONFIG_CLK_RENESAS) += renesas/
 obj-$(CONFIG_CLK_SIFIVE) += sifive/
diff --git a/drivers/clk/clk_octeon.c b/drivers/clk/clk_octeon.c
new file mode 100644
index 0000000000..fd559e05fc
--- /dev/null
+++ b/drivers/clk/clk_octeon.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Stefan Roese <sr@denx.de>
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dt-bindings/clock/octeon-clock.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct octeon_clk_priv {
+	u64 core_clk;
+	u64 io_clk;
+};
+
+static int octeon_clk_enable(struct clk *clk)
+{
+	/* Nothing to do on Octeon */
+	return 0;
+}
+
+static ulong octeon_clk_get_rate(struct clk *clk)
+{
+	struct octeon_clk_priv *priv = dev_get_priv(clk->dev);
+
+	switch (clk->id) {
+	case OCTEON_CLK_CORE:
+		return priv->core_clk;
+
+	case OCTEON_CLK_IO:
+		return priv->io_clk;
+
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
+static struct clk_ops octeon_clk_ops = {
+	.enable = octeon_clk_enable,
+	.get_rate = octeon_clk_get_rate,
+};
+
+static const struct udevice_id octeon_clk_ids[] = {
+	{ .compatible = "mrvl,octeon-clk" },
+	{ /* sentinel */ }
+};
+
+static int octeon_clk_probe(struct udevice *dev)
+{
+	struct octeon_clk_priv *priv = dev_get_priv(dev);
+
+	/*
+	 * The clock values are already read into GD, lets just store them
+	 * in priv data
+	 */
+	priv->core_clk = gd->cpu_clk;
+	priv->io_clk = gd->bus_clk;
+
+	return 0;
+}
+
+U_BOOT_DRIVER(clk_octeon) = {
+	.name = "clk_octeon",
+	.id = UCLASS_CLK,
+	.of_match = octeon_clk_ids,
+	.ops = &octeon_clk_ops,
+	.probe = octeon_clk_probe,
+	.priv_auto_alloc_size = sizeof(struct octeon_clk_priv),
+};
diff --git a/include/dt-bindings/clock/octeon-clock.h b/include/dt-bindings/clock/octeon-clock.h
new file mode 100644
index 0000000000..34e6a3bf41
--- /dev/null
+++ b/include/dt-bindings/clock/octeon-clock.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2020 Stefan Roese <sr@denx.de>
+ */
+
+#ifndef __DT_BINDINGS_CLOCK_OCTEON_CLOCK_H
+#define __DT_BINDINGS_CLOCK_OCTEON_CLOCK_H
+
+#define OCTEON_CLK_CORE		0
+#define OCTEON_CLK_IO		1
+
+#endif /* __DT_BINDINGS_CLOCK_OCTEON_CLOCK_H */
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 05/10] mips: octeon: dts: Add Octeon clock driver DT nodes
  2020-07-23 10:17 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
                   ` (3 preceding siblings ...)
  2020-07-23 10:17 ` [PATCH v2 04/10] clk: clk_octeon: Add simple MIPS Octeon clock driver Stefan Roese
@ 2020-07-23 10:17 ` Stefan Roese
  2020-07-23 10:17 ` [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon Stefan Roese
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-23 10:17 UTC (permalink / raw)
  To: u-boot

This patch adds the DT nodes for the Octeon clock support via the
common clk_ API.

Signed-off-by: Stefan Roese <sr@denx.de>
---

(no changes since v1)

 arch/mips/dts/mrvl,cn73xx.dtsi        | 10 ++++++++++
 arch/mips/dts/mrvl,octeon-ebb7304.dts |  2 +-
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/arch/mips/dts/mrvl,cn73xx.dtsi b/arch/mips/dts/mrvl,cn73xx.dtsi
index 4c7b6e4160..7c0a8d73f0 100644
--- a/arch/mips/dts/mrvl,cn73xx.dtsi
+++ b/arch/mips/dts/mrvl,cn73xx.dtsi
@@ -5,6 +5,8 @@
 
 /dts-v1/;
 
+#include <dt-bindings/clock/octeon-clock.h>
+
 / {
 	#address-cells = <2>;
 	#size-cells = <2>;
@@ -38,6 +40,12 @@
 			#size-cells = <1>;
 		};
 
+		clk: clock {
+			compatible = "mrvl,octeon-clk";
+			#clock-cells = <1>;
+			u-boot,dm-pre-reloc;
+		};
+
 		gpio: gpio-controller at 1070000000800 {
 			#gpio-cells = <2>;
 			compatible = "cavium,octeon-7890-gpio";
@@ -95,6 +103,7 @@
 			/* INT_ST, INT_TS, INT_CORE */
 			interrupts = <0x0b000 1>, <0x0b001 1>, <0x0b002 1>;
 			clock-frequency = <100000>;
+			clocks = <&clk OCTEON_CLK_IO>;
 		};
 
 		i2c1: i2c at 1180000001200 {
@@ -105,6 +114,7 @@
 			/* INT_ST, INT_TS, INT_CORE */
 			interrupts = <0x0b100 1>, <0x0b101 1>, <0x0b102 1>;
 			clock-frequency = <100000>;
+			clocks = <&clk OCTEON_CLK_IO>;
 		};
 	};
 };
diff --git a/arch/mips/dts/mrvl,octeon-ebb7304.dts b/arch/mips/dts/mrvl,octeon-ebb7304.dts
index 096e5c8f66..c229aa5fc0 100644
--- a/arch/mips/dts/mrvl,octeon-ebb7304.dts
+++ b/arch/mips/dts/mrvl,octeon-ebb7304.dts
@@ -5,7 +5,7 @@
 
 /dts-v1/;
 
-/include/ "mrvl,cn73xx.dtsi"
+#include "mrvl,cn73xx.dtsi"
 
 / {
 	model = "cavium,ebb7304";
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon
  2020-07-23 10:17 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
                   ` (4 preceding siblings ...)
  2020-07-23 10:17 ` [PATCH v2 05/10] mips: octeon: dts: Add Octeon clock driver DT nodes Stefan Roese
@ 2020-07-23 10:17 ` Stefan Roese
  2020-07-24 13:56   ` Daniel Schwierzeck
  2020-07-30  7:44   ` Jagan Teki
  2020-07-23 10:17 ` [PATCH v2 07/10] mips: octeon: mrvl,cn73xx.dtsi: Add SPI DT node Stefan Roese
                   ` (3 subsequent siblings)
  9 siblings, 2 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-23 10:17 UTC (permalink / raw)
  To: u-boot

From: Suneel Garapati <sgarapati@marvell.com>

Adds support for SPI controllers found on Octeon II/III and Octeon TX
TX2 SoC platforms.

Signed-off-by: Aaron Williams <awilliams@marvell.com>
Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
Signed-off-by: Stefan Roese <sr@denx.de>
Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
Cc: Aaron Williams <awilliams@marvell.com>
Cc: Chandrakala Chavva <cchavva@marvell.com>
Cc: Jagan Teki <jagan@amarulasolutions.com>

---

Changes in v2:
- Newly added to this series
- Removed inclusion of "common.h"
- Added "depends on DM_PCI" to Kconfig
- Tested on MIPS Octeon and ARM Octeon TX2
- Fixed issues with Octeon TX2 registration. Now only one driver is
  registered and the "ops" is overwritten in the Octeon TX2 case.
- Use dev_get_driver_data() to get the driver data struct
- Removed "struct pci_device_id" definition and U_BOOT_PCI_DEVICE()
  as its not needed for the PCI based probing on Octeon TX2

 drivers/spi/Kconfig      |   8 +
 drivers/spi/Makefile     |   1 +
 drivers/spi/octeon_spi.c | 647 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 656 insertions(+)
 create mode 100644 drivers/spi/octeon_spi.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 30d808d7bb..3fc2d0674a 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -240,6 +240,14 @@ config NXP_FSPI
 	  Enable the NXP FlexSPI (FSPI) driver. This driver can be used to
 	  access the SPI NOR flash on platforms embedding this NXP IP core.
 
+config OCTEON_SPI
+	bool "Octeon SPI driver"
+	depends on DM_PCI && (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2)
+	help
+	  Enable the Octeon SPI driver. This driver can be used to
+	  access the SPI NOR flash on Octeon II/III and OcteonTX/TX2
+	  SoC platforms.
+
 config OMAP3_SPI
 	bool "McSPI driver for OMAP"
 	help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 4e7461771f..b5c9ff1af8 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_MXC_SPI) += mxc_spi.o
 obj-$(CONFIG_MXS_SPI) += mxs_spi.o
 obj-$(CONFIG_NXP_FSPI) += nxp_fspi.o
 obj-$(CONFIG_ATCSPI200_SPI) += atcspi200_spi.o
+obj-$(CONFIG_OCTEON_SPI) += octeon_spi.o
 obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
 obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
 obj-$(CONFIG_PL022_SPI) += pl022_spi.o
diff --git a/drivers/spi/octeon_spi.c b/drivers/spi/octeon_spi.c
new file mode 100644
index 0000000000..2fb39e444c
--- /dev/null
+++ b/drivers/spi/octeon_spi.c
@@ -0,0 +1,647 @@
+// SPDX-License-Identifier:    GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ *
+ * https://spdx.org/licenses
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <malloc.h>
+#include <spi.h>
+#include <spi-mem.h>
+#include <watchdog.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <linux/bitfield.h>
+#include <linux/compat.h>
+#include <linux/delay.h>
+
+#define OCTEON_SPI_MAX_BYTES	9
+#define OCTEON_SPI_MAX_CLOCK_HZ	50000000
+
+#define OCTEON_SPI_NUM_CS	4
+
+#define OCTEON_SPI_CS_VALID(cs)	((cs) < OCTEON_SPI_NUM_CS)
+
+#define MPI_CFG			0x0000
+#define MPI_STS			0x0008
+#define MPI_TX			0x0010
+#define MPI_XMIT		0x0018
+#define MPI_WIDE_DAT		0x0040
+#define MPI_IO_CTL		0x0048
+#define MPI_DAT(X)		(0x0080 + ((X) << 3))
+#define MPI_WIDE_BUF(X)		(0x0800 + ((X) << 3))
+#define MPI_CYA_CFG		0x1000
+#define MPI_CLKEN		0x1080
+
+#define MPI_CFG_ENABLE		BIT_ULL(0)
+#define MPI_CFG_IDLELO		BIT_ULL(1)
+#define MPI_CFG_CLK_CONT	BIT_ULL(2)
+#define MPI_CFG_WIREOR		BIT_ULL(3)
+#define MPI_CFG_LSBFIRST	BIT_ULL(4)
+#define MPI_CFG_CS_STICKY	BIT_ULL(5)
+#define MPI_CFG_CSHI		BIT_ULL(7)
+#define MPI_CFG_IDLECLKS	GENMASK_ULL(9, 8)
+#define MPI_CFG_TRITX		BIT_ULL(10)
+#define MPI_CFG_CSLATE		BIT_ULL(11)
+#define MPI_CFG_CSENA0		BIT_ULL(12)
+#define MPI_CFG_CSENA1		BIT_ULL(13)
+#define MPI_CFG_CSENA2		BIT_ULL(14)
+#define MPI_CFG_CSENA3		BIT_ULL(15)
+#define MPI_CFG_CLKDIV		GENMASK_ULL(28, 16)
+#define MPI_CFG_LEGACY_DIS	BIT_ULL(31)
+#define MPI_CFG_IOMODE		GENMASK_ULL(35, 34)
+#define MPI_CFG_TB100_EN	BIT_ULL(49)
+
+#define MPI_DAT_DATA		GENMASK_ULL(7, 0)
+
+#define MPI_STS_BUSY		BIT_ULL(0)
+#define MPI_STS_MPI_INTR	BIT_ULL(1)
+#define MPI_STS_RXNUM		GENMASK_ULL(12, 8)
+
+#define MPI_TX_TOTNUM		GENMASK_ULL(4, 0)
+#define MPI_TX_TXNUM		GENMASK_ULL(12, 8)
+#define MPI_TX_LEAVECS		BIT_ULL(16)
+#define MPI_TX_CSID		GENMASK_ULL(21, 20)
+
+#define MPI_XMIT_TOTNUM		GENMASK_ULL(10, 0)
+#define MPI_XMIT_TXNUM		GENMASK_ULL(30, 20)
+#define MPI_XMIT_BUF_SEL	BIT_ULL(59)
+#define MPI_XMIT_LEAVECS	BIT_ULL(60)
+#define MPI_XMIT_CSID		GENMASK_ULL(62, 61)
+
+enum {
+	PROBE_PCI = 0,		/* PCI based probing */
+	PROBE_DT,		/* DT based probing */
+};
+
+/* Used on Octeon TX2 */
+void board_acquire_flash_arb(bool acquire);
+
+struct octeon_spi_data {
+	int probe;
+	u32 reg_offs;
+};
+
+/* Local driver data structure */
+struct octeon_spi {
+	void __iomem *base;	/* Register base address */
+	struct clk clk;
+	u32 clkdiv;		/* Clock divisor for device speed */
+};
+
+static u64 octeon_spi_set_mpicfg(struct udevice *dev)
+{
+	struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
+	struct udevice *bus = dev_get_parent(dev);
+	struct octeon_spi *priv = dev_get_priv(bus);
+	u64 mpi_cfg;
+	uint max_speed = slave->max_hz;
+	bool cpha, cpol;
+
+	if (!max_speed)
+		max_speed = 12500000;
+	if (max_speed > OCTEON_SPI_MAX_CLOCK_HZ)
+		max_speed = OCTEON_SPI_MAX_CLOCK_HZ;
+
+	debug("\n slave params %d %d %d\n", slave->cs,
+	      slave->max_hz, slave->mode);
+	cpha = !!(slave->mode & SPI_CPHA);
+	cpol = !!(slave->mode & SPI_CPOL);
+
+	mpi_cfg = FIELD_PREP(MPI_CFG_CLKDIV, priv->clkdiv & 0x1fff) |
+		FIELD_PREP(MPI_CFG_CSHI, !!(slave->mode & SPI_CS_HIGH)) |
+		FIELD_PREP(MPI_CFG_LSBFIRST, !!(slave->mode & SPI_LSB_FIRST)) |
+		FIELD_PREP(MPI_CFG_WIREOR, !!(slave->mode & SPI_3WIRE)) |
+		FIELD_PREP(MPI_CFG_IDLELO, cpha != cpol) |
+		FIELD_PREP(MPI_CFG_CSLATE, cpha) |
+		MPI_CFG_CSENA0 | MPI_CFG_CSENA1 |
+		MPI_CFG_CSENA2 | MPI_CFG_CSENA1 |
+		MPI_CFG_ENABLE;
+
+	debug("\n mpi_cfg %llx\n", mpi_cfg);
+	return mpi_cfg;
+}
+
+/**
+ * Wait until the SPI bus is ready
+ *
+ * @param	dev	SPI device to wait for
+ */
+static void octeon_spi_wait_ready(struct udevice *dev)
+{
+	struct udevice *bus = dev_get_parent(dev);
+	struct octeon_spi *priv = dev_get_priv(bus);
+	void *base = priv->base;
+	u64 mpi_sts;
+
+	do {
+		mpi_sts = readq(base + MPI_STS);
+		WATCHDOG_RESET();
+	} while (mpi_sts & MPI_STS_BUSY);
+
+	debug("%s(%s)\n", __func__, dev->name);
+}
+
+/**
+ * Claim the bus for a slave device
+ *
+ * @param	dev	SPI bus
+ *
+ * @return	0 for success, -EINVAL if chip select is invalid
+ */
+static int octeon_spi_claim_bus(struct udevice *dev)
+{
+	struct udevice *bus = dev_get_parent(dev);
+	struct octeon_spi *priv = dev_get_priv(bus);
+	void *base = priv->base;
+	u64 mpi_cfg;
+
+	debug("\n\n%s(%s)\n", __func__, dev->name);
+	if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
+		return -EINVAL;
+
+	if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
+		board_acquire_flash_arb(true);
+
+	mpi_cfg = readq(base + MPI_CFG);
+	mpi_cfg &= ~MPI_CFG_TRITX;
+	mpi_cfg |= MPI_CFG_ENABLE;
+	writeq(mpi_cfg, base + MPI_CFG);
+	mpi_cfg = readq(base + MPI_CFG);
+	udelay(5);	/** Wait for bus to settle */
+
+	return 0;
+}
+
+/**
+ * Release the bus to a slave device
+ *
+ * @param	dev	SPI bus
+ *
+ * @return	0 for success, -EINVAL if chip select is invalid
+ */
+static int octeon_spi_release_bus(struct udevice *dev)
+{
+	struct udevice *bus = dev_get_parent(dev);
+	struct octeon_spi *priv = dev_get_priv(bus);
+	void *base = priv->base;
+	u64 mpi_cfg;
+
+	debug("%s(%s)\n\n", __func__, dev->name);
+	if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
+		return -EINVAL;
+
+	if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
+		board_acquire_flash_arb(false);
+
+	mpi_cfg = readq(base + MPI_CFG);
+	mpi_cfg &= ~MPI_CFG_ENABLE;
+	writeq(mpi_cfg, base + MPI_CFG);
+	mpi_cfg = readq(base + MPI_CFG);
+	udelay(1);
+
+	return 0;
+}
+
+static int octeon_spi_xfer(struct udevice *dev, unsigned int bitlen,
+			   const void *dout, void *din, unsigned long flags)
+{
+	struct udevice *bus = dev_get_parent(dev);
+	struct octeon_spi *priv = dev_get_priv(bus);
+	void *base = priv->base;
+	u64 mpi_tx;
+	u64 mpi_cfg;
+	u64 wide_dat = 0;
+	int len = bitlen / 8;
+	int i;
+	const u8 *tx_data = dout;
+	u8 *rx_data = din;
+	int cs = spi_chip_select(dev);
+
+	if (!OCTEON_SPI_CS_VALID(cs))
+		return -EINVAL;
+
+	debug("\n %s(%s, %u, %p, %p, 0x%lx), cs: %d\n",
+	      __func__, dev->name, bitlen, dout, din, flags, cs);
+
+	mpi_cfg = octeon_spi_set_mpicfg(dev);
+	if (mpi_cfg != readq(base + MPI_CFG)) {
+		writeq(mpi_cfg, base + MPI_CFG);
+		mpi_cfg = readq(base + MPI_CFG);
+		udelay(10);
+	}
+
+	debug("\n mpi_cfg upd %llx\n", mpi_cfg);
+
+	/*
+	 * Start by writing and reading 8 bytes at a time. While we can support
+	 * up to 10, it's easier to just use 8 with the MPI_WIDE_DAT register.
+	 */
+	while (len > 8) {
+		if (tx_data) {
+			wide_dat = get_unaligned((u64 *)tx_data);
+			debug("  tx: %016llx \t", (unsigned long long)wide_dat);
+			tx_data += 8;
+			writeq(wide_dat, base + MPI_WIDE_DAT);
+		}
+
+		mpi_tx = FIELD_PREP(MPI_TX_CSID, cs) |
+			FIELD_PREP(MPI_TX_LEAVECS, 1) |
+			FIELD_PREP(MPI_TX_TXNUM, tx_data ? 8 : 0) |
+			FIELD_PREP(MPI_TX_TOTNUM, 8);
+		writeq(mpi_tx, base + MPI_TX);
+
+		octeon_spi_wait_ready(dev);
+
+		debug("\n ");
+
+		if (rx_data) {
+			wide_dat = readq(base + MPI_WIDE_DAT);
+			debug("  rx: %016llx\t", (unsigned long long)wide_dat);
+			*(u64 *)rx_data = wide_dat;
+			rx_data += 8;
+		}
+		len -= 8;
+	}
+
+	debug("\n ");
+
+	/* Write and read the rest of the data */
+	if (tx_data) {
+		for (i = 0; i < len; i++) {
+			debug("  tx: %02x\n", *tx_data);
+			writeq(*tx_data++, base + MPI_DAT(i));
+		}
+	}
+
+	mpi_tx = FIELD_PREP(MPI_TX_CSID, cs) |
+		FIELD_PREP(MPI_TX_LEAVECS, !(flags & SPI_XFER_END)) |
+		FIELD_PREP(MPI_TX_TXNUM, tx_data ? len : 0) |
+		FIELD_PREP(MPI_TX_TOTNUM, len);
+	writeq(mpi_tx, base + MPI_TX);
+
+	octeon_spi_wait_ready(dev);
+
+	debug("\n ");
+
+	if (rx_data) {
+		for (i = 0; i < len; i++) {
+			*rx_data = readq(base + MPI_DAT(i)) & 0xff;
+			debug("  rx: %02x\n", *rx_data);
+			rx_data++;
+		}
+	}
+
+	return 0;
+}
+
+static int octeontx2_spi_xfer(struct udevice *dev, unsigned int bitlen,
+			      const void *dout, void *din, unsigned long flags)
+{
+	struct udevice *bus = dev_get_parent(dev);
+	struct octeon_spi *priv = dev_get_priv(bus);
+	void *base = priv->base;
+	u64 mpi_xmit;
+	u64 mpi_cfg;
+	u64 wide_dat = 0;
+	int len = bitlen / 8;
+	int rem;
+	int i;
+	const u8 *tx_data = dout;
+	u8 *rx_data = din;
+	int cs = spi_chip_select(dev);
+
+	if (!OCTEON_SPI_CS_VALID(cs))
+		return -EINVAL;
+
+	debug("\n %s(%s, %u, %p, %p, 0x%lx), cs: %d\n",
+	      __func__, dev->name, bitlen, dout, din, flags, cs);
+
+	mpi_cfg = octeon_spi_set_mpicfg(dev);
+
+	mpi_cfg |= MPI_CFG_TRITX | MPI_CFG_LEGACY_DIS | MPI_CFG_CS_STICKY |
+		MPI_CFG_TB100_EN;
+
+	mpi_cfg &= ~MPI_CFG_IOMODE;
+	if (flags & (SPI_TX_DUAL | SPI_RX_DUAL))
+		mpi_cfg |= FIELD_PREP(MPI_CFG_IOMODE, 2);
+	if (flags & (SPI_TX_QUAD | SPI_RX_QUAD))
+		mpi_cfg |= FIELD_PREP(MPI_CFG_IOMODE, 3);
+
+	if (mpi_cfg != readq(base + MPI_CFG)) {
+		writeq(mpi_cfg, base + MPI_CFG);
+		mpi_cfg = readq(base + MPI_CFG);
+		udelay(10);
+	}
+
+	debug("\n mpi_cfg upd %llx\n\n", mpi_cfg);
+
+	/* Start by writing or reading 1024 bytes at a time. */
+	while (len > 1024) {
+		if (tx_data) {
+			/* 8 bytes per iteration */
+			for (i = 0; i < 128; i++) {
+				wide_dat = get_unaligned((u64 *)tx_data);
+				debug("  tx: %016llx \t",
+				      (unsigned long long)wide_dat);
+				if ((i % 4) == 3)
+					debug("\n");
+				tx_data += 8;
+				writeq(wide_dat, base + MPI_WIDE_BUF(i));
+			}
+		}
+
+		mpi_xmit = FIELD_PREP(MPI_XMIT_CSID, cs) | MPI_XMIT_LEAVECS |
+			FIELD_PREP(MPI_XMIT_TXNUM, tx_data ? 1024 : 0) |
+			FIELD_PREP(MPI_XMIT_TOTNUM, 1024);
+		writeq(mpi_xmit, base + MPI_XMIT);
+
+		octeon_spi_wait_ready(dev);
+
+		debug("\n ");
+
+		if (rx_data) {
+			/* 8 bytes per iteration */
+			for (i = 0; i < 128; i++) {
+				wide_dat = readq(base + MPI_WIDE_BUF(i));
+				debug("  rx: %016llx\t",
+				      (unsigned long long)wide_dat);
+				if ((i % 4) == 3)
+					debug("\n");
+				*(u64 *)rx_data = wide_dat;
+				rx_data += 8;
+			}
+		}
+		len -= 1024;
+	}
+
+	if (tx_data) {
+		rem = len % 8;
+		/* 8 bytes per iteration */
+		for (i = 0; i < len / 8; i++) {
+			wide_dat = get_unaligned((u64 *)tx_data);
+			debug("  tx: %016llx \t",
+			      (unsigned long long)wide_dat);
+			if ((i % 4) == 3)
+				debug("\n");
+			tx_data += 8;
+			writeq(wide_dat, base + MPI_WIDE_BUF(i));
+		}
+		if (rem) {
+			memcpy(&wide_dat, tx_data, rem);
+			debug("  rtx: %016llx\t", wide_dat);
+			writeq(wide_dat, base + MPI_WIDE_BUF(i));
+		}
+	}
+
+	mpi_xmit = FIELD_PREP(MPI_XMIT_CSID, cs) |
+		FIELD_PREP(MPI_XMIT_LEAVECS, !(flags & SPI_XFER_END)) |
+		FIELD_PREP(MPI_XMIT_TXNUM, tx_data ? len : 0) |
+		FIELD_PREP(MPI_XMIT_TOTNUM, len);
+	writeq(mpi_xmit, base + MPI_XMIT);
+
+	octeon_spi_wait_ready(dev);
+
+	debug("\n ");
+
+	if (rx_data) {
+		rem = len % 8;
+		/* 8 bytes per iteration */
+		for (i = 0; i < len / 8; i++) {
+			wide_dat = readq(base + MPI_WIDE_BUF(i));
+			debug("  rx: %016llx\t",
+			      (unsigned long long)wide_dat);
+			if ((i % 4) == 3)
+				debug("\n");
+			*(u64 *)rx_data = wide_dat;
+			rx_data += 8;
+		}
+		if (rem) {
+			wide_dat = readq(base + MPI_WIDE_BUF(i));
+			debug("  rrx: %016llx\t",
+			      (unsigned long long)wide_dat);
+			memcpy(rx_data, &wide_dat, rem);
+			rx_data += rem;
+		}
+	}
+
+	return 0;
+}
+
+static bool octeon_spi_supports_op(struct spi_slave *slave,
+				   const struct spi_mem_op *op)
+{
+	/* For now, support only below combinations
+	 * 1-1-1
+	 * 1-1-2 1-2-2
+	 * 1-1-4 1-4-4
+	 */
+	if (op->cmd.buswidth != 1)
+		return false;
+	return true;
+}
+
+static int octeon_spi_exec_op(struct spi_slave *slave,
+			      const struct spi_mem_op *op)
+{
+	unsigned long flags = SPI_XFER_BEGIN;
+	const void *tx;
+	void *rx;
+	u8 opcode, *buf;
+	u8 *addr;
+	int i, temp, ret;
+
+	if (op->cmd.buswidth != 1)
+		return -ENOTSUPP;
+
+	/* Send CMD */
+	i = 0;
+	opcode = op->cmd.opcode;
+
+	if (!op->data.nbytes && !op->addr.nbytes && !op->dummy.nbytes)
+		flags |= SPI_XFER_END;
+
+	ret = octeontx2_spi_xfer(slave->dev, 8, (void *)&opcode, NULL, flags);
+	if (ret < 0)
+		return ret;
+
+	/* Send Address and dummy */
+	if (op->addr.nbytes) {
+		/* Alloc buffer for address+dummy */
+		buf = (u8 *)calloc(1, op->addr.nbytes + op->dummy.nbytes);
+		if (!buf) {
+			printf("%s Out of memory\n", __func__);
+			return -ENOMEM;
+		}
+		addr = (u8 *)&op->addr.val;
+		for (temp = 0; temp < op->addr.nbytes; temp++)
+			buf[i++] = *(u8 *)(addr + op->addr.nbytes - 1 - temp);
+		for (temp = 0; temp < op->dummy.nbytes; temp++)
+			buf[i++] = 0xff;
+		if (op->addr.buswidth == 2)
+			flags |= SPI_RX_DUAL;
+		if (op->addr.buswidth == 4)
+			flags |= SPI_RX_QUAD;
+
+		if (!op->data.nbytes)
+			flags |= SPI_XFER_END;
+		ret = octeontx2_spi_xfer(slave->dev, i * 8, (void *)buf, NULL,
+					 flags);
+		free(buf);
+		if (ret < 0)
+			return ret;
+	}
+	if (!op->data.nbytes)
+		return 0;
+
+	/* Send/Receive Data */
+	flags |= SPI_XFER_END;
+	if (op->data.buswidth == 2)
+		flags |= SPI_RX_DUAL;
+	if (op->data.buswidth == 4)
+		flags |= SPI_RX_QUAD;
+
+	rx = (op->data.dir == SPI_MEM_DATA_IN) ? op->data.buf.in : NULL;
+	tx = (op->data.dir == SPI_MEM_DATA_OUT) ? op->data.buf.out : NULL;
+
+	ret = octeontx2_spi_xfer(slave->dev, (op->data.nbytes * 8), tx, rx,
+				 flags);
+	return ret;
+}
+
+static const struct spi_controller_mem_ops octeontx2_spi_mem_ops = {
+	.supports_op = octeon_spi_supports_op,
+	.exec_op = octeon_spi_exec_op,
+};
+
+/**
+ * Set the speed of the SPI bus
+ *
+ * @param	bus	bus to set
+ * @param	max_hz	maximum speed supported
+ */
+static int octeon_spi_set_speed(struct udevice *bus, uint max_hz)
+{
+	struct octeon_spi *priv = dev_get_priv(bus);
+	ulong clk_rate;
+	u32 calc_hz;
+
+	if (max_hz > OCTEON_SPI_MAX_CLOCK_HZ)
+		max_hz = OCTEON_SPI_MAX_CLOCK_HZ;
+
+	clk_rate = clk_get_rate(&priv->clk);
+	if (IS_ERR_VALUE(clk_rate))
+		return -EINVAL;
+
+	debug("%s(%s, %u, %lu)\n", __func__, bus->name, max_hz, clk_rate);
+
+	priv->clkdiv = clk_rate / (2 * max_hz);
+	while (1) {
+		calc_hz = clk_rate / (2 * priv->clkdiv);
+		if (calc_hz <= max_hz)
+			break;
+		priv->clkdiv += 1;
+	}
+
+	if (priv->clkdiv > 8191)
+		return -EINVAL;
+
+	debug("%s: clkdiv=%d\n", __func__, priv->clkdiv);
+
+	return 0;
+}
+
+static int octeon_spi_set_mode(struct udevice *bus, uint mode)
+{
+	/* We don't set it here */
+	return 0;
+}
+
+static const struct dm_spi_ops octeon_spi_ops = {
+	.claim_bus	= octeon_spi_claim_bus,
+	.release_bus	= octeon_spi_release_bus,
+	.set_speed	= octeon_spi_set_speed,
+	.set_mode	= octeon_spi_set_mode,
+	.xfer		= octeon_spi_xfer,
+};
+
+static const struct dm_spi_ops octeontx2_spi_ops = {
+	.claim_bus	= octeon_spi_claim_bus,
+	.release_bus	= octeon_spi_release_bus,
+	.set_speed	= octeon_spi_set_speed,
+	.set_mode	= octeon_spi_set_mode,
+	.xfer		= octeontx2_spi_xfer,
+	.mem_ops	= &octeontx2_spi_mem_ops,
+};
+
+static int octeon_spi_probe(struct udevice *dev)
+{
+	struct octeon_spi *priv = dev_get_priv(dev);
+	const struct octeon_spi_data *data;
+	int ret;
+
+	data = (const struct octeon_spi_data *)dev_get_driver_data(dev);
+	if (data->probe == PROBE_PCI) {
+		pci_dev_t bdf = dm_pci_get_bdf(dev);
+
+		debug("SPI PCI device: %x\n", bdf);
+		priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
+					    PCI_REGION_MEM);
+	} else {
+		priv->base = dev_remap_addr(dev);
+	}
+
+	priv->base += data->reg_offs;
+
+	/* Octeon TX2 needs a different ops struct */
+	if (device_is_compatible(dev, "cavium,thunderx-spi")) {
+		/*
+		 * "ops" is const and can't be written directly. So we need
+		 * to write the Octeon TX2 ops value using this trick
+		 */
+		writeq((u64)&octeontx2_spi_ops, (void *)&dev->driver->ops);
+	}
+
+	ret = clk_get_by_index(dev, 0, &priv->clk);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_enable(&priv->clk);
+	if (ret)
+		return ret;
+
+	debug("SPI bus %s %d at %p\n", dev->name, dev->seq, priv->base);
+
+	return 0;
+}
+
+static const struct octeon_spi_data spi_octeon_data = {
+	.probe = PROBE_DT,
+	.reg_offs = 0x0000,
+};
+
+static const struct octeon_spi_data spi_octeontx_data = {
+	.probe = PROBE_PCI,
+	.reg_offs = 0x1000,
+};
+
+static const struct udevice_id octeon_spi_ids[] = {
+	/* MIPS Octeon */
+	{ .compatible = "cavium,octeon-3010-spi",
+	  .data = (ulong)&spi_octeon_data },
+	/* ARM Octeon TX / TX2 */
+	{ .compatible = "cavium,thunder-8190-spi",
+	  .data = (ulong)&spi_octeontx_data },
+	{ }
+};
+
+U_BOOT_DRIVER(octeon_spi) = {
+	.name			= "spi_octeon",
+	.id			= UCLASS_SPI,
+	.of_match		= octeon_spi_ids,
+	.probe			= octeon_spi_probe,
+	.priv_auto_alloc_size	= sizeof(struct octeon_spi),
+	.ops			= &octeon_spi_ops,
+};
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 07/10] mips: octeon: mrvl,cn73xx.dtsi: Add SPI DT node
  2020-07-23 10:17 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
                   ` (5 preceding siblings ...)
  2020-07-23 10:17 ` [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon Stefan Roese
@ 2020-07-23 10:17 ` Stefan Roese
  2020-07-23 10:17 ` [PATCH v2 08/10] mips: octeon: mrvl, octeon-ebb7304.dts: Add SPI flash " Stefan Roese
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-23 10:17 UTC (permalink / raw)
  To: u-boot

Add the Octeon SPI DT node to the dtsi file.

Signed-off-by: Stefan Roese <sr@denx.de>

---

Changes in v2:
- Newly added to this series

 arch/mips/dts/mrvl,cn73xx.dtsi | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/mips/dts/mrvl,cn73xx.dtsi b/arch/mips/dts/mrvl,cn73xx.dtsi
index 7c0a8d73f0..f5ad4a6213 100644
--- a/arch/mips/dts/mrvl,cn73xx.dtsi
+++ b/arch/mips/dts/mrvl,cn73xx.dtsi
@@ -116,5 +116,15 @@
 			clock-frequency = <100000>;
 			clocks = <&clk OCTEON_CLK_IO>;
 		};
+
+		spi: spi at 1070000001000 {
+			compatible = "cavium,octeon-3010-spi";
+			reg = <0x10700 0x00001000 0x0 0x100>;
+			interrupts = <0x05001 1>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			spi-max-frequency = <25000000>;
+			clocks = <&clk OCTEON_CLK_IO>;
+		};
 	};
 };
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 08/10] mips: octeon: mrvl, octeon-ebb7304.dts: Add SPI flash DT node
  2020-07-23 10:17 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
                   ` (6 preceding siblings ...)
  2020-07-23 10:17 ` [PATCH v2 07/10] mips: octeon: mrvl,cn73xx.dtsi: Add SPI DT node Stefan Roese
@ 2020-07-23 10:17 ` Stefan Roese
  2020-07-23 10:17 ` [PATCH v2 09/10] mips: octeon: Update Octeon Kconfig Stefan Roese
  2020-07-23 10:17 ` [PATCH v2 10/10] mips: octeon: Update EBB7304 defconfig Stefan Roese
  9 siblings, 0 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-23 10:17 UTC (permalink / raw)
  To: u-boot

Add the SPI flash DT node for the EBB7304.

Signed-off-by: Stefan Roese <sr@denx.de>

---

Changes in v2:
- Newly added to this series

 arch/mips/dts/mrvl,octeon-ebb7304.dts | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/mips/dts/mrvl,octeon-ebb7304.dts b/arch/mips/dts/mrvl,octeon-ebb7304.dts
index c229aa5fc0..6b2e5e84bc 100644
--- a/arch/mips/dts/mrvl,octeon-ebb7304.dts
+++ b/arch/mips/dts/mrvl,octeon-ebb7304.dts
@@ -13,6 +13,7 @@
 
 	aliases {
 		serial0 = &uart0;
+		spi0 = &spi;
 	};
 
 	chosen {
@@ -104,3 +105,11 @@
 	u-boot,dm-pre-reloc;	/* Needed early for DDR SPD EEPROM */
 	clock-frequency = <100000>;
 };
+
+&spi {
+	flash at 0 {
+		compatible = "micron,n25q128a11", "jedec,spi-nor";
+		spi-max-frequency = <2000000>;
+		reg = <0>;
+	};
+};
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 09/10] mips: octeon: Update Octeon Kconfig
  2020-07-23 10:17 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
                   ` (7 preceding siblings ...)
  2020-07-23 10:17 ` [PATCH v2 08/10] mips: octeon: mrvl, octeon-ebb7304.dts: Add SPI flash " Stefan Roese
@ 2020-07-23 10:17 ` Stefan Roese
  2020-07-23 10:17 ` [PATCH v2 10/10] mips: octeon: Update EBB7304 defconfig Stefan Roese
  9 siblings, 0 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-23 10:17 UTC (permalink / raw)
  To: u-boot

This patch selects DM_SPI & DM_I2C for MIPS Octeon. DM_GPIO, DM_SERIAL
and DM_ETH are already selected.

Additionally the selections are now alphabetically sorted.

Signed-off-by: Stefan Roese <sr@denx.de>

---

Changes in v2:
- Kconfig and defconfig changes separated into 2 patches

 arch/mips/Kconfig | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 997e145450..e0f6b6c4b3 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -112,9 +112,11 @@ config ARCH_OCTEON
 	select DISPLAY_CPUINFO
 	select DMA_ADDR_T_64BIT
 	select DM
-	select DM_SERIAL
-	select DM_GPIO
 	select DM_ETH
+	select DM_GPIO
+	select DM_I2C
+	select DM_SERIAL
+	select DM_SPI
 	select MIPS_L2_CACHE
 	select MIPS_MACH_EARLY_INIT
 	select MIPS_TUNE_OCTEON3
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 10/10] mips: octeon: Update EBB7304 defconfig
  2020-07-23 10:17 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
                   ` (8 preceding siblings ...)
  2020-07-23 10:17 ` [PATCH v2 09/10] mips: octeon: Update Octeon Kconfig Stefan Roese
@ 2020-07-23 10:17 ` Stefan Roese
  9 siblings, 0 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-23 10:17 UTC (permalink / raw)
  To: u-boot

This patch enables the following options for the Octeon EBB7304 EVB:

- PCI & DM_PCI
- DM_SPI_FLASH & SPI flash device support
- SPI & Octeon SPI driver
- GPIO cmd support
- I2C cmd support

Signed-off-by: Stefan Roese <sr@denx.de>

---

Changes in v2:
- Kconfig and defconfig changes separated into 2 patches

 configs/octeon_ebb7304_defconfig | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/configs/octeon_ebb7304_defconfig b/configs/octeon_ebb7304_defconfig
index d810b1e45f..17fdc68c03 100644
--- a/configs/octeon_ebb7304_defconfig
+++ b/configs/octeon_ebb7304_defconfig
@@ -12,6 +12,8 @@ CONFIG_ARCH_OCTEON=y
 CONFIG_DEBUG_UART=y
 CONFIG_SYS_CONSOLE_INFO_QUIET=y
 CONFIG_HUSH_PARSER=y
+CONFIG_CMD_GPIO=y
+CONFIG_CMD_I2C=y
 CONFIG_CMD_MTD=y
 CONFIG_CMD_PCI=y
 CONFIG_CMD_DHCP=y
@@ -29,10 +31,18 @@ CONFIG_CFI_FLASH=y
 CONFIG_SYS_FLASH_USE_BUFFER_WRITE=y
 CONFIG_FLASH_CFI_MTD=y
 CONFIG_SYS_FLASH_CFI=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH_ATMEL=y
+CONFIG_SPI_FLASH_SPANSION=y
+CONFIG_SPI_FLASH_STMICRO=y
 # CONFIG_NETDEVICES is not set
+CONFIG_PCI=y
+CONFIG_DM_PCI=y
 CONFIG_DEBUG_UART_SHIFT=3
 CONFIG_DEBUG_UART_ANNOUNCE=y
 CONFIG_SYS_NS16550=y
+CONFIG_SPI=y
+CONFIG_OCTEON_SPI=y
 CONFIG_SYSRESET=y
 CONFIG_SYSRESET_OCTEON=y
 CONFIG_HEXDUMP=y
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon
  2020-07-23 10:17 ` [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon Stefan Roese
@ 2020-07-24 13:56   ` Daniel Schwierzeck
  2020-07-24 14:27     ` Stefan Roese
                       ` (2 more replies)
  2020-07-30  7:44   ` Jagan Teki
  1 sibling, 3 replies; 22+ messages in thread
From: Daniel Schwierzeck @ 2020-07-24 13:56 UTC (permalink / raw)
  To: u-boot

Am Donnerstag, den 23.07.2020, 12:17 +0200 schrieb Stefan Roese:
> From: Suneel Garapati <sgarapati@marvell.com>
> 
> Adds support for SPI controllers found on Octeon II/III and Octeon TX
> TX2 SoC platforms.
> 
> Signed-off-by: Aaron Williams <awilliams@marvell.com>
> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
> Signed-off-by: Stefan Roese <sr@denx.de>
> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
> Cc: Aaron Williams <awilliams@marvell.com>
> Cc: Chandrakala Chavva <cchavva@marvell.com>
> Cc: Jagan Teki <jagan@amarulasolutions.com>
> 
> ---
> 
> Changes in v2:
> - Newly added to this series
> - Removed inclusion of "common.h"
> - Added "depends on DM_PCI" to Kconfig
> - Tested on MIPS Octeon and ARM Octeon TX2
> - Fixed issues with Octeon TX2 registration. Now only one driver is
>   registered and the "ops" is overwritten in the Octeon TX2 case.
> - Use dev_get_driver_data() to get the driver data struct
> - Removed "struct pci_device_id" definition and U_BOOT_PCI_DEVICE()
>   as its not needed for the PCI based probing on Octeon TX2
> 
>  drivers/spi/Kconfig      |   8 +
>  drivers/spi/Makefile     |   1 +
>  drivers/spi/octeon_spi.c | 647 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 656 insertions(+)
>  create mode 100644 drivers/spi/octeon_spi.c
> 
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index 30d808d7bb..3fc2d0674a 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -240,6 +240,14 @@ config NXP_FSPI
>  	  Enable the NXP FlexSPI (FSPI) driver. This driver can be used to
>  	  access the SPI NOR flash on platforms embedding this NXP IP core.
>  
> +config OCTEON_SPI
> +	bool "Octeon SPI driver"
> +	depends on DM_PCI && (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2)
> +	help
> +	  Enable the Octeon SPI driver. This driver can be used to
> +	  access the SPI NOR flash on Octeon II/III and OcteonTX/TX2
> +	  SoC platforms.
> +
>  config OMAP3_SPI
>  	bool "McSPI driver for OMAP"
>  	help
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index 4e7461771f..b5c9ff1af8 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_MXC_SPI) += mxc_spi.o
>  obj-$(CONFIG_MXS_SPI) += mxs_spi.o
>  obj-$(CONFIG_NXP_FSPI) += nxp_fspi.o
>  obj-$(CONFIG_ATCSPI200_SPI) += atcspi200_spi.o
> +obj-$(CONFIG_OCTEON_SPI) += octeon_spi.o
>  obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
>  obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
>  obj-$(CONFIG_PL022_SPI) += pl022_spi.o
> diff --git a/drivers/spi/octeon_spi.c b/drivers/spi/octeon_spi.c
> new file mode 100644
> index 0000000000..2fb39e444c
> --- /dev/null
> +++ b/drivers/spi/octeon_spi.c
> @@ -0,0 +1,647 @@
> +// SPDX-License-Identifier:    GPL-2.0
> +/*
> + * Copyright (C) 2018 Marvell International Ltd.
> + *
> + * https://spdx.org/licenses
> + */
> +
> +#include <clk.h>
> +#include <dm.h>
> +#include <malloc.h>
> +#include <spi.h>
> +#include <spi-mem.h>
> +#include <watchdog.h>
> +#include <asm/io.h>
> +#include <asm/unaligned.h>
> +#include <linux/bitfield.h>
> +#include <linux/compat.h>
> +#include <linux/delay.h>
> +
> +#define OCTEON_SPI_MAX_BYTES	9
> +#define OCTEON_SPI_MAX_CLOCK_HZ	50000000
> +
> +#define OCTEON_SPI_NUM_CS	4
> +
> +#define OCTEON_SPI_CS_VALID(cs)	((cs) < OCTEON_SPI_NUM_CS)
> +
> +#define MPI_CFG			0x0000
> +#define MPI_STS			0x0008
> +#define MPI_TX			0x0010
> +#define MPI_XMIT		0x0018
> +#define MPI_WIDE_DAT		0x0040
> +#define MPI_IO_CTL		0x0048
> +#define MPI_DAT(X)		(0x0080 + ((X) << 3))
> +#define MPI_WIDE_BUF(X)		(0x0800 + ((X) << 3))
> +#define MPI_CYA_CFG		0x1000
> +#define MPI_CLKEN		0x1080
> +
> +#define MPI_CFG_ENABLE		BIT_ULL(0)
> +#define MPI_CFG_IDLELO		BIT_ULL(1)
> +#define MPI_CFG_CLK_CONT	BIT_ULL(2)
> +#define MPI_CFG_WIREOR		BIT_ULL(3)
> +#define MPI_CFG_LSBFIRST	BIT_ULL(4)
> +#define MPI_CFG_CS_STICKY	BIT_ULL(5)
> +#define MPI_CFG_CSHI		BIT_ULL(7)
> +#define MPI_CFG_IDLECLKS	GENMASK_ULL(9, 8)
> +#define MPI_CFG_TRITX		BIT_ULL(10)
> +#define MPI_CFG_CSLATE		BIT_ULL(11)
> +#define MPI_CFG_CSENA0		BIT_ULL(12)
> +#define MPI_CFG_CSENA1		BIT_ULL(13)
> +#define MPI_CFG_CSENA2		BIT_ULL(14)
> +#define MPI_CFG_CSENA3		BIT_ULL(15)
> +#define MPI_CFG_CLKDIV		GENMASK_ULL(28, 16)
> +#define MPI_CFG_LEGACY_DIS	BIT_ULL(31)
> +#define MPI_CFG_IOMODE		GENMASK_ULL(35, 34)
> +#define MPI_CFG_TB100_EN	BIT_ULL(49)
> +
> +#define MPI_DAT_DATA		GENMASK_ULL(7, 0)
> +
> +#define MPI_STS_BUSY		BIT_ULL(0)
> +#define MPI_STS_MPI_INTR	BIT_ULL(1)
> +#define MPI_STS_RXNUM		GENMASK_ULL(12, 8)
> +
> +#define MPI_TX_TOTNUM		GENMASK_ULL(4, 0)
> +#define MPI_TX_TXNUM		GENMASK_ULL(12, 8)
> +#define MPI_TX_LEAVECS		BIT_ULL(16)
> +#define MPI_TX_CSID		GENMASK_ULL(21, 20)
> +
> +#define MPI_XMIT_TOTNUM		GENMASK_ULL(10, 0)
> +#define MPI_XMIT_TXNUM		GENMASK_ULL(30, 20)
> +#define MPI_XMIT_BUF_SEL	BIT_ULL(59)
> +#define MPI_XMIT_LEAVECS	BIT_ULL(60)
> +#define MPI_XMIT_CSID		GENMASK_ULL(62, 61)
> +
> +enum {
> +	PROBE_PCI = 0,		/* PCI based probing */
> +	PROBE_DT,		/* DT based probing */
> +};
> +
> +/* Used on Octeon TX2 */
> +void board_acquire_flash_arb(bool acquire);
> +
> +struct octeon_spi_data {
> +	int probe;
> +	u32 reg_offs;
> +};
> +
> +/* Local driver data structure */
> +struct octeon_spi {
> +	void __iomem *base;	/* Register base address */
> +	struct clk clk;
> +	u32 clkdiv;		/* Clock divisor for device speed */
> +};
> +
> +static u64 octeon_spi_set_mpicfg(struct udevice *dev)
> +{
> +	struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
> +	struct udevice *bus = dev_get_parent(dev);
> +	struct octeon_spi *priv = dev_get_priv(bus);
> +	u64 mpi_cfg;
> +	uint max_speed = slave->max_hz;
> +	bool cpha, cpol;
> +
> +	if (!max_speed)
> +		max_speed = 12500000;
> +	if (max_speed > OCTEON_SPI_MAX_CLOCK_HZ)
> +		max_speed = OCTEON_SPI_MAX_CLOCK_HZ;
> +
> +	debug("\n slave params %d %d %d\n", slave->cs,
> +	      slave->max_hz, slave->mode);
> +	cpha = !!(slave->mode & SPI_CPHA);
> +	cpol = !!(slave->mode & SPI_CPOL);
> +
> +	mpi_cfg = FIELD_PREP(MPI_CFG_CLKDIV, priv->clkdiv & 0x1fff) |
> +		FIELD_PREP(MPI_CFG_CSHI, !!(slave->mode & SPI_CS_HIGH)) |
> +		FIELD_PREP(MPI_CFG_LSBFIRST, !!(slave->mode & SPI_LSB_FIRST)) |
> +		FIELD_PREP(MPI_CFG_WIREOR, !!(slave->mode & SPI_3WIRE)) |
> +		FIELD_PREP(MPI_CFG_IDLELO, cpha != cpol) |
> +		FIELD_PREP(MPI_CFG_CSLATE, cpha) |
> +		MPI_CFG_CSENA0 | MPI_CFG_CSENA1 |
> +		MPI_CFG_CSENA2 | MPI_CFG_CSENA1 |
> +		MPI_CFG_ENABLE;
> +
> +	debug("\n mpi_cfg %llx\n", mpi_cfg);
> +	return mpi_cfg;
> +}
> +
> +/**
> + * Wait until the SPI bus is ready
> + *
> + * @param	dev	SPI device to wait for
> + */
> +static void octeon_spi_wait_ready(struct udevice *dev)
> +{
> +	struct udevice *bus = dev_get_parent(dev);
> +	struct octeon_spi *priv = dev_get_priv(bus);
> +	void *base = priv->base;
> +	u64 mpi_sts;
> +
> +	do {
> +		mpi_sts = readq(base + MPI_STS);
> +		WATCHDOG_RESET();
> +	} while (mpi_sts & MPI_STS_BUSY);
> +
> +	debug("%s(%s)\n", __func__, dev->name);
> +}
> +
> +/**
> + * Claim the bus for a slave device
> + *
> + * @param	dev	SPI bus
> + *
> + * @return	0 for success, -EINVAL if chip select is invalid
> + */
> +static int octeon_spi_claim_bus(struct udevice *dev)
> +{
> +	struct udevice *bus = dev_get_parent(dev);
> +	struct octeon_spi *priv = dev_get_priv(bus);
> +	void *base = priv->base;
> +	u64 mpi_cfg;
> +
> +	debug("\n\n%s(%s)\n", __func__, dev->name);
> +	if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
> +		return -EINVAL;
> +
> +	if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
> +		board_acquire_flash_arb(true);
> +
> +	mpi_cfg = readq(base + MPI_CFG);
> +	mpi_cfg &= ~MPI_CFG_TRITX;
> +	mpi_cfg |= MPI_CFG_ENABLE;
> +	writeq(mpi_cfg, base + MPI_CFG);
> +	mpi_cfg = readq(base + MPI_CFG);
> +	udelay(5);	/** Wait for bus to settle */
> +
> +	return 0;
> +}
> +
> +/**
> + * Release the bus to a slave device
> + *
> + * @param	dev	SPI bus
> + *
> + * @return	0 for success, -EINVAL if chip select is invalid
> + */
> +static int octeon_spi_release_bus(struct udevice *dev)
> +{
> +	struct udevice *bus = dev_get_parent(dev);
> +	struct octeon_spi *priv = dev_get_priv(bus);
> +	void *base = priv->base;
> +	u64 mpi_cfg;
> +
> +	debug("%s(%s)\n\n", __func__, dev->name);
> +	if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
> +		return -EINVAL;
> +
> +	if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
> +		board_acquire_flash_arb(false);
> +
> +	mpi_cfg = readq(base + MPI_CFG);
> +	mpi_cfg &= ~MPI_CFG_ENABLE;
> +	writeq(mpi_cfg, base + MPI_CFG);
> +	mpi_cfg = readq(base + MPI_CFG);
> +	udelay(1);
> +
> +	return 0;
> +}
> +
> +static int octeon_spi_xfer(struct udevice *dev, unsigned int bitlen,
> +			   const void *dout, void *din, unsigned long flags)
> +{
> +	struct udevice *bus = dev_get_parent(dev);
> +	struct octeon_spi *priv = dev_get_priv(bus);
> +	void *base = priv->base;
> +	u64 mpi_tx;
> +	u64 mpi_cfg;
> +	u64 wide_dat = 0;
> +	int len = bitlen / 8;
> +	int i;
> +	const u8 *tx_data = dout;
> +	u8 *rx_data = din;
> +	int cs = spi_chip_select(dev);
> +
> +	if (!OCTEON_SPI_CS_VALID(cs))
> +		return -EINVAL;
> +
> +	debug("\n %s(%s, %u, %p, %p, 0x%lx), cs: %d\n",
> +	      __func__, dev->name, bitlen, dout, din, flags, cs);
> +
> +	mpi_cfg = octeon_spi_set_mpicfg(dev);
> +	if (mpi_cfg != readq(base + MPI_CFG)) {
> +		writeq(mpi_cfg, base + MPI_CFG);
> +		mpi_cfg = readq(base + MPI_CFG);
> +		udelay(10);
> +	}
> +
> +	debug("\n mpi_cfg upd %llx\n", mpi_cfg);
> +
> +	/*
> +	 * Start by writing and reading 8 bytes at a time. While we can support
> +	 * up to 10, it's easier to just use 8 with the MPI_WIDE_DAT register.
> +	 */
> +	while (len > 8) {
> +		if (tx_data) {
> +			wide_dat = get_unaligned((u64 *)tx_data);
> +			debug("  tx: %016llx \t", (unsigned long long)wide_dat);
> +			tx_data += 8;
> +			writeq(wide_dat, base + MPI_WIDE_DAT);
> +		}
> +
> +		mpi_tx = FIELD_PREP(MPI_TX_CSID, cs) |
> +			FIELD_PREP(MPI_TX_LEAVECS, 1) |
> +			FIELD_PREP(MPI_TX_TXNUM, tx_data ? 8 : 0) |
> +			FIELD_PREP(MPI_TX_TOTNUM, 8);
> +		writeq(mpi_tx, base + MPI_TX);
> +
> +		octeon_spi_wait_ready(dev);
> +
> +		debug("\n ");
> +
> +		if (rx_data) {
> +			wide_dat = readq(base + MPI_WIDE_DAT);
> +			debug("  rx: %016llx\t", (unsigned long long)wide_dat);
> +			*(u64 *)rx_data = wide_dat;
> +			rx_data += 8;
> +		}
> +		len -= 8;
> +	}
> +
> +	debug("\n ");
> +
> +	/* Write and read the rest of the data */
> +	if (tx_data) {
> +		for (i = 0; i < len; i++) {
> +			debug("  tx: %02x\n", *tx_data);
> +			writeq(*tx_data++, base + MPI_DAT(i));
> +		}
> +	}
> +
> +	mpi_tx = FIELD_PREP(MPI_TX_CSID, cs) |
> +		FIELD_PREP(MPI_TX_LEAVECS, !(flags & SPI_XFER_END)) |
> +		FIELD_PREP(MPI_TX_TXNUM, tx_data ? len : 0) |
> +		FIELD_PREP(MPI_TX_TOTNUM, len);
> +	writeq(mpi_tx, base + MPI_TX);
> +
> +	octeon_spi_wait_ready(dev);
> +
> +	debug("\n ");
> +
> +	if (rx_data) {
> +		for (i = 0; i < len; i++) {
> +			*rx_data = readq(base + MPI_DAT(i)) & 0xff;
> +			debug("  rx: %02x\n", *rx_data);
> +			rx_data++;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int octeontx2_spi_xfer(struct udevice *dev, unsigned int bitlen,
> +			      const void *dout, void *din, unsigned long flags)
> +{
> +	struct udevice *bus = dev_get_parent(dev);
> +	struct octeon_spi *priv = dev_get_priv(bus);
> +	void *base = priv->base;
> +	u64 mpi_xmit;
> +	u64 mpi_cfg;
> +	u64 wide_dat = 0;
> +	int len = bitlen / 8;
> +	int rem;
> +	int i;
> +	const u8 *tx_data = dout;
> +	u8 *rx_data = din;
> +	int cs = spi_chip_select(dev);
> +
> +	if (!OCTEON_SPI_CS_VALID(cs))
> +		return -EINVAL;
> +
> +	debug("\n %s(%s, %u, %p, %p, 0x%lx), cs: %d\n",
> +	      __func__, dev->name, bitlen, dout, din, flags, cs);
> +
> +	mpi_cfg = octeon_spi_set_mpicfg(dev);
> +
> +	mpi_cfg |= MPI_CFG_TRITX | MPI_CFG_LEGACY_DIS | MPI_CFG_CS_STICKY |
> +		MPI_CFG_TB100_EN;
> +
> +	mpi_cfg &= ~MPI_CFG_IOMODE;
> +	if (flags & (SPI_TX_DUAL | SPI_RX_DUAL))
> +		mpi_cfg |= FIELD_PREP(MPI_CFG_IOMODE, 2);
> +	if (flags & (SPI_TX_QUAD | SPI_RX_QUAD))
> +		mpi_cfg |= FIELD_PREP(MPI_CFG_IOMODE, 3);
> +
> +	if (mpi_cfg != readq(base + MPI_CFG)) {
> +		writeq(mpi_cfg, base + MPI_CFG);
> +		mpi_cfg = readq(base + MPI_CFG);
> +		udelay(10);
> +	}
> +
> +	debug("\n mpi_cfg upd %llx\n\n", mpi_cfg);
> +
> +	/* Start by writing or reading 1024 bytes at a time. */
> +	while (len > 1024) {
> +		if (tx_data) {
> +			/* 8 bytes per iteration */
> +			for (i = 0; i < 128; i++) {
> +				wide_dat = get_unaligned((u64 *)tx_data);
> +				debug("  tx: %016llx \t",
> +				      (unsigned long long)wide_dat);
> +				if ((i % 4) == 3)
> +					debug("\n");
> +				tx_data += 8;
> +				writeq(wide_dat, base + MPI_WIDE_BUF(i));
> +			}
> +		}
> +
> +		mpi_xmit = FIELD_PREP(MPI_XMIT_CSID, cs) | MPI_XMIT_LEAVECS |
> +			FIELD_PREP(MPI_XMIT_TXNUM, tx_data ? 1024 : 0) |
> +			FIELD_PREP(MPI_XMIT_TOTNUM, 1024);
> +		writeq(mpi_xmit, base + MPI_XMIT);
> +
> +		octeon_spi_wait_ready(dev);
> +
> +		debug("\n ");
> +
> +		if (rx_data) {
> +			/* 8 bytes per iteration */
> +			for (i = 0; i < 128; i++) {
> +				wide_dat = readq(base + MPI_WIDE_BUF(i));
> +				debug("  rx: %016llx\t",
> +				      (unsigned long long)wide_dat);
> +				if ((i % 4) == 3)
> +					debug("\n");
> +				*(u64 *)rx_data = wide_dat;
> +				rx_data += 8;
> +			}
> +		}
> +		len -= 1024;
> +	}
> +
> +	if (tx_data) {
> +		rem = len % 8;
> +		/* 8 bytes per iteration */
> +		for (i = 0; i < len / 8; i++) {
> +			wide_dat = get_unaligned((u64 *)tx_data);
> +			debug("  tx: %016llx \t",
> +			      (unsigned long long)wide_dat);
> +			if ((i % 4) == 3)
> +				debug("\n");
> +			tx_data += 8;
> +			writeq(wide_dat, base + MPI_WIDE_BUF(i));
> +		}
> +		if (rem) {
> +			memcpy(&wide_dat, tx_data, rem);
> +			debug("  rtx: %016llx\t", wide_dat);
> +			writeq(wide_dat, base + MPI_WIDE_BUF(i));
> +		}
> +	}
> +
> +	mpi_xmit = FIELD_PREP(MPI_XMIT_CSID, cs) |
> +		FIELD_PREP(MPI_XMIT_LEAVECS, !(flags & SPI_XFER_END)) |
> +		FIELD_PREP(MPI_XMIT_TXNUM, tx_data ? len : 0) |
> +		FIELD_PREP(MPI_XMIT_TOTNUM, len);
> +	writeq(mpi_xmit, base + MPI_XMIT);
> +
> +	octeon_spi_wait_ready(dev);
> +
> +	debug("\n ");
> +
> +	if (rx_data) {
> +		rem = len % 8;
> +		/* 8 bytes per iteration */
> +		for (i = 0; i < len / 8; i++) {
> +			wide_dat = readq(base + MPI_WIDE_BUF(i));
> +			debug("  rx: %016llx\t",
> +			      (unsigned long long)wide_dat);
> +			if ((i % 4) == 3)
> +				debug("\n");
> +			*(u64 *)rx_data = wide_dat;
> +			rx_data += 8;
> +		}
> +		if (rem) {
> +			wide_dat = readq(base + MPI_WIDE_BUF(i));
> +			debug("  rrx: %016llx\t",
> +			      (unsigned long long)wide_dat);
> +			memcpy(rx_data, &wide_dat, rem);
> +			rx_data += rem;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static bool octeon_spi_supports_op(struct spi_slave *slave,
> +				   const struct spi_mem_op *op)
> +{
> +	/* For now, support only below combinations
> +	 * 1-1-1
> +	 * 1-1-2 1-2-2
> +	 * 1-1-4 1-4-4
> +	 */
> +	if (op->cmd.buswidth != 1)
> +		return false;
> +	return true;
> +}
> +
> +static int octeon_spi_exec_op(struct spi_slave *slave,
> +			      const struct spi_mem_op *op)
> +{
> +	unsigned long flags = SPI_XFER_BEGIN;
> +	const void *tx;
> +	void *rx;
> +	u8 opcode, *buf;
> +	u8 *addr;
> +	int i, temp, ret;
> +
> +	if (op->cmd.buswidth != 1)
> +		return -ENOTSUPP;
> +
> +	/* Send CMD */
> +	i = 0;
> +	opcode = op->cmd.opcode;
> +
> +	if (!op->data.nbytes && !op->addr.nbytes && !op->dummy.nbytes)
> +		flags |= SPI_XFER_END;
> +
> +	ret = octeontx2_spi_xfer(slave->dev, 8, (void *)&opcode, NULL, flags);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Send Address and dummy */
> +	if (op->addr.nbytes) {
> +		/* Alloc buffer for address+dummy */
> +		buf = (u8 *)calloc(1, op->addr.nbytes + op->dummy.nbytes);
> +		if (!buf) {
> +			printf("%s Out of memory\n", __func__);
> +			return -ENOMEM;
> +		}
> +		addr = (u8 *)&op->addr.val;
> +		for (temp = 0; temp < op->addr.nbytes; temp++)
> +			buf[i++] = *(u8 *)(addr + op->addr.nbytes - 1 - temp);
> +		for (temp = 0; temp < op->dummy.nbytes; temp++)
> +			buf[i++] = 0xff;
> +		if (op->addr.buswidth == 2)
> +			flags |= SPI_RX_DUAL;
> +		if (op->addr.buswidth == 4)
> +			flags |= SPI_RX_QUAD;
> +
> +		if (!op->data.nbytes)
> +			flags |= SPI_XFER_END;
> +		ret = octeontx2_spi_xfer(slave->dev, i * 8, (void *)buf, NULL,
> +					 flags);
> +		free(buf);
> +		if (ret < 0)
> +			return ret;
> +	}
> +	if (!op->data.nbytes)
> +		return 0;
> +
> +	/* Send/Receive Data */
> +	flags |= SPI_XFER_END;
> +	if (op->data.buswidth == 2)
> +		flags |= SPI_RX_DUAL;
> +	if (op->data.buswidth == 4)
> +		flags |= SPI_RX_QUAD;
> +
> +	rx = (op->data.dir == SPI_MEM_DATA_IN) ? op->data.buf.in : NULL;
> +	tx = (op->data.dir == SPI_MEM_DATA_OUT) ? op->data.buf.out : NULL;
> +
> +	ret = octeontx2_spi_xfer(slave->dev, (op->data.nbytes * 8), tx, rx,
> +				 flags);
> +	return ret;
> +}
> +
> +static const struct spi_controller_mem_ops octeontx2_spi_mem_ops = {
> +	.supports_op = octeon_spi_supports_op,
> +	.exec_op = octeon_spi_exec_op,
> +};
> +
> +/**
> + * Set the speed of the SPI bus
> + *
> + * @param	bus	bus to set
> + * @param	max_hz	maximum speed supported
> + */
> +static int octeon_spi_set_speed(struct udevice *bus, uint max_hz)
> +{
> +	struct octeon_spi *priv = dev_get_priv(bus);
> +	ulong clk_rate;
> +	u32 calc_hz;
> +
> +	if (max_hz > OCTEON_SPI_MAX_CLOCK_HZ)
> +		max_hz = OCTEON_SPI_MAX_CLOCK_HZ;
> +
> +	clk_rate = clk_get_rate(&priv->clk);
> +	if (IS_ERR_VALUE(clk_rate))
> +		return -EINVAL;
> +
> +	debug("%s(%s, %u, %lu)\n", __func__, bus->name, max_hz, clk_rate);
> +
> +	priv->clkdiv = clk_rate / (2 * max_hz);
> +	while (1) {
> +		calc_hz = clk_rate / (2 * priv->clkdiv);
> +		if (calc_hz <= max_hz)
> +			break;
> +		priv->clkdiv += 1;
> +	}
> +
> +	if (priv->clkdiv > 8191)
> +		return -EINVAL;
> +
> +	debug("%s: clkdiv=%d\n", __func__, priv->clkdiv);
> +
> +	return 0;
> +}
> +
> +static int octeon_spi_set_mode(struct udevice *bus, uint mode)
> +{
> +	/* We don't set it here */
> +	return 0;
> +}
> +
> +static const struct dm_spi_ops octeon_spi_ops = {
> +	.claim_bus	= octeon_spi_claim_bus,
> +	.release_bus	= octeon_spi_release_bus,
> +	.set_speed	= octeon_spi_set_speed,
> +	.set_mode	= octeon_spi_set_mode,
> +	.xfer		= octeon_spi_xfer,
> +};
> +
> +static const struct dm_spi_ops octeontx2_spi_ops = {
> +	.claim_bus	= octeon_spi_claim_bus,
> +	.release_bus	= octeon_spi_release_bus,
> +	.set_speed	= octeon_spi_set_speed,
> +	.set_mode	= octeon_spi_set_mode,
> +	.xfer		= octeontx2_spi_xfer,
> +	.mem_ops	= &octeontx2_spi_mem_ops,
> +};
> +
> +static int octeon_spi_probe(struct udevice *dev)
> +{
> +	struct octeon_spi *priv = dev_get_priv(dev);
> +	const struct octeon_spi_data *data;
> +	int ret;
> +
> +	data = (const struct octeon_spi_data *)dev_get_driver_data(dev);
> +	if (data->probe == PROBE_PCI) {
> +		pci_dev_t bdf = dm_pci_get_bdf(dev);
> +
> +		debug("SPI PCI device: %x\n", bdf);
> +		priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
> +					    PCI_REGION_MEM);
> +	} else {
> +		priv->base = dev_remap_addr(dev);
> +	}
> +
> +	priv->base += data->reg_offs;
> +
> +	/* Octeon TX2 needs a different ops struct */
> +	if (device_is_compatible(dev, "cavium,thunderx-spi")) {
> +		/*
> +		 * "ops" is const and can't be written directly. So we need
> +		 * to write the Octeon TX2 ops value using this trick
> +		 */
> +		writeq((u64)&octeontx2_spi_ops, (void *)&dev->driver->ops);
> +	}

can't you simply add a xfer() function pointer to "struct
octeon_spi_data" and assign the according xfer function to it? Then
in octeon_spi_xfer() you can simply call that function pointer. With
this you only need one instance of "struct dm_spi_ops" and don't need
this ugly hack ;) Maybe you can add some common code to
octeon_spi_xfer() itself and reduce the size of the SoC specific xfer
functions.

> +
> +	ret = clk_get_by_index(dev, 0, &priv->clk);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = clk_enable(&priv->clk);
> +	if (ret)
> +		return ret;
> +
> +	debug("SPI bus %s %d at %p\n", dev->name, dev->seq, priv->base);
> +
> +	return 0;
> +}
> +
> +static const struct octeon_spi_data spi_octeon_data = {
> +	.probe = PROBE_DT,
> +	.reg_offs = 0x0000,
> +};
> +
> +static const struct octeon_spi_data spi_octeontx_data = {
> +	.probe = PROBE_PCI,
> +	.reg_offs = 0x1000,
> +};
> +
> +static const struct udevice_id octeon_spi_ids[] = {
> +	/* MIPS Octeon */
> +	{ .compatible = "cavium,octeon-3010-spi",
> +	  .data = (ulong)&spi_octeon_data },
> +	/* ARM Octeon TX / TX2 */
> +	{ .compatible = "cavium,thunder-8190-spi",
> +	  .data = (ulong)&spi_octeontx_data },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(octeon_spi) = {
> +	.name			= "spi_octeon",
> +	.id			= UCLASS_SPI,
> +	.of_match		= octeon_spi_ids,
> +	.probe			= octeon_spi_probe,
> +	.priv_auto_alloc_size	= sizeof(struct octeon_spi),
> +	.ops			= &octeon_spi_ops,
> +};
-- 
- Daniel

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon
  2020-07-24 13:56   ` Daniel Schwierzeck
@ 2020-07-24 14:27     ` Stefan Roese
  2020-07-30  5:49     ` Stefan Roese
  2020-07-30  7:50     ` Jagan Teki
  2 siblings, 0 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-24 14:27 UTC (permalink / raw)
  To: u-boot

On 24.07.20 15:56, Daniel Schwierzeck wrote:
> Am Donnerstag, den 23.07.2020, 12:17 +0200 schrieb Stefan Roese:
>> From: Suneel Garapati <sgarapati@marvell.com>
>>
>> Adds support for SPI controllers found on Octeon II/III and Octeon TX
>> TX2 SoC platforms.
>>
>> Signed-off-by: Aaron Williams <awilliams@marvell.com>
>> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
>> Signed-off-by: Stefan Roese <sr@denx.de>
>> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
>> Cc: Aaron Williams <awilliams@marvell.com>
>> Cc: Chandrakala Chavva <cchavva@marvell.com>
>> Cc: Jagan Teki <jagan@amarulasolutions.com>
>>
>> ---
>>
>> Changes in v2:
>> - Newly added to this series
>> - Removed inclusion of "common.h"
>> - Added "depends on DM_PCI" to Kconfig
>> - Tested on MIPS Octeon and ARM Octeon TX2
>> - Fixed issues with Octeon TX2 registration. Now only one driver is
>>    registered and the "ops" is overwritten in the Octeon TX2 case.
>> - Use dev_get_driver_data() to get the driver data struct
>> - Removed "struct pci_device_id" definition and U_BOOT_PCI_DEVICE()
>>    as its not needed for the PCI based probing on Octeon TX2
>>
>>   drivers/spi/Kconfig      |   8 +
>>   drivers/spi/Makefile     |   1 +
>>   drivers/spi/octeon_spi.c | 647 +++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 656 insertions(+)
>>   create mode 100644 drivers/spi/octeon_spi.c
>>
>> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
>> index 30d808d7bb..3fc2d0674a 100644
>> --- a/drivers/spi/Kconfig
>> +++ b/drivers/spi/Kconfig
>> @@ -240,6 +240,14 @@ config NXP_FSPI
>>   	  Enable the NXP FlexSPI (FSPI) driver. This driver can be used to
>>   	  access the SPI NOR flash on platforms embedding this NXP IP core.
>>   
>> +config OCTEON_SPI
>> +	bool "Octeon SPI driver"
>> +	depends on DM_PCI && (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2)
>> +	help
>> +	  Enable the Octeon SPI driver. This driver can be used to
>> +	  access the SPI NOR flash on Octeon II/III and OcteonTX/TX2
>> +	  SoC platforms.
>> +
>>   config OMAP3_SPI
>>   	bool "McSPI driver for OMAP"
>>   	help
>> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
>> index 4e7461771f..b5c9ff1af8 100644
>> --- a/drivers/spi/Makefile
>> +++ b/drivers/spi/Makefile
>> @@ -43,6 +43,7 @@ obj-$(CONFIG_MXC_SPI) += mxc_spi.o
>>   obj-$(CONFIG_MXS_SPI) += mxs_spi.o
>>   obj-$(CONFIG_NXP_FSPI) += nxp_fspi.o
>>   obj-$(CONFIG_ATCSPI200_SPI) += atcspi200_spi.o
>> +obj-$(CONFIG_OCTEON_SPI) += octeon_spi.o
>>   obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
>>   obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
>>   obj-$(CONFIG_PL022_SPI) += pl022_spi.o
>> diff --git a/drivers/spi/octeon_spi.c b/drivers/spi/octeon_spi.c
>> new file mode 100644
>> index 0000000000..2fb39e444c
>> --- /dev/null
>> +++ b/drivers/spi/octeon_spi.c
>> @@ -0,0 +1,647 @@
>> +// SPDX-License-Identifier:    GPL-2.0
>> +/*
>> + * Copyright (C) 2018 Marvell International Ltd.
>> + *
>> + * https://spdx.org/licenses
>> + */
>> +
>> +#include <clk.h>
>> +#include <dm.h>
>> +#include <malloc.h>
>> +#include <spi.h>
>> +#include <spi-mem.h>
>> +#include <watchdog.h>
>> +#include <asm/io.h>
>> +#include <asm/unaligned.h>
>> +#include <linux/bitfield.h>
>> +#include <linux/compat.h>
>> +#include <linux/delay.h>
>> +
>> +#define OCTEON_SPI_MAX_BYTES	9
>> +#define OCTEON_SPI_MAX_CLOCK_HZ	50000000
>> +
>> +#define OCTEON_SPI_NUM_CS	4
>> +
>> +#define OCTEON_SPI_CS_VALID(cs)	((cs) < OCTEON_SPI_NUM_CS)
>> +
>> +#define MPI_CFG			0x0000
>> +#define MPI_STS			0x0008
>> +#define MPI_TX			0x0010
>> +#define MPI_XMIT		0x0018
>> +#define MPI_WIDE_DAT		0x0040
>> +#define MPI_IO_CTL		0x0048
>> +#define MPI_DAT(X)		(0x0080 + ((X) << 3))
>> +#define MPI_WIDE_BUF(X)		(0x0800 + ((X) << 3))
>> +#define MPI_CYA_CFG		0x1000
>> +#define MPI_CLKEN		0x1080
>> +
>> +#define MPI_CFG_ENABLE		BIT_ULL(0)
>> +#define MPI_CFG_IDLELO		BIT_ULL(1)
>> +#define MPI_CFG_CLK_CONT	BIT_ULL(2)
>> +#define MPI_CFG_WIREOR		BIT_ULL(3)
>> +#define MPI_CFG_LSBFIRST	BIT_ULL(4)
>> +#define MPI_CFG_CS_STICKY	BIT_ULL(5)
>> +#define MPI_CFG_CSHI		BIT_ULL(7)
>> +#define MPI_CFG_IDLECLKS	GENMASK_ULL(9, 8)
>> +#define MPI_CFG_TRITX		BIT_ULL(10)
>> +#define MPI_CFG_CSLATE		BIT_ULL(11)
>> +#define MPI_CFG_CSENA0		BIT_ULL(12)
>> +#define MPI_CFG_CSENA1		BIT_ULL(13)
>> +#define MPI_CFG_CSENA2		BIT_ULL(14)
>> +#define MPI_CFG_CSENA3		BIT_ULL(15)
>> +#define MPI_CFG_CLKDIV		GENMASK_ULL(28, 16)
>> +#define MPI_CFG_LEGACY_DIS	BIT_ULL(31)
>> +#define MPI_CFG_IOMODE		GENMASK_ULL(35, 34)
>> +#define MPI_CFG_TB100_EN	BIT_ULL(49)
>> +
>> +#define MPI_DAT_DATA		GENMASK_ULL(7, 0)
>> +
>> +#define MPI_STS_BUSY		BIT_ULL(0)
>> +#define MPI_STS_MPI_INTR	BIT_ULL(1)
>> +#define MPI_STS_RXNUM		GENMASK_ULL(12, 8)
>> +
>> +#define MPI_TX_TOTNUM		GENMASK_ULL(4, 0)
>> +#define MPI_TX_TXNUM		GENMASK_ULL(12, 8)
>> +#define MPI_TX_LEAVECS		BIT_ULL(16)
>> +#define MPI_TX_CSID		GENMASK_ULL(21, 20)
>> +
>> +#define MPI_XMIT_TOTNUM		GENMASK_ULL(10, 0)
>> +#define MPI_XMIT_TXNUM		GENMASK_ULL(30, 20)
>> +#define MPI_XMIT_BUF_SEL	BIT_ULL(59)
>> +#define MPI_XMIT_LEAVECS	BIT_ULL(60)
>> +#define MPI_XMIT_CSID		GENMASK_ULL(62, 61)
>> +
>> +enum {
>> +	PROBE_PCI = 0,		/* PCI based probing */
>> +	PROBE_DT,		/* DT based probing */
>> +};
>> +
>> +/* Used on Octeon TX2 */
>> +void board_acquire_flash_arb(bool acquire);
>> +
>> +struct octeon_spi_data {
>> +	int probe;
>> +	u32 reg_offs;
>> +};
>> +
>> +/* Local driver data structure */
>> +struct octeon_spi {
>> +	void __iomem *base;	/* Register base address */
>> +	struct clk clk;
>> +	u32 clkdiv;		/* Clock divisor for device speed */
>> +};
>> +
>> +static u64 octeon_spi_set_mpicfg(struct udevice *dev)
>> +{
>> +	struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
>> +	struct udevice *bus = dev_get_parent(dev);
>> +	struct octeon_spi *priv = dev_get_priv(bus);
>> +	u64 mpi_cfg;
>> +	uint max_speed = slave->max_hz;
>> +	bool cpha, cpol;
>> +
>> +	if (!max_speed)
>> +		max_speed = 12500000;
>> +	if (max_speed > OCTEON_SPI_MAX_CLOCK_HZ)
>> +		max_speed = OCTEON_SPI_MAX_CLOCK_HZ;
>> +
>> +	debug("\n slave params %d %d %d\n", slave->cs,
>> +	      slave->max_hz, slave->mode);
>> +	cpha = !!(slave->mode & SPI_CPHA);
>> +	cpol = !!(slave->mode & SPI_CPOL);
>> +
>> +	mpi_cfg = FIELD_PREP(MPI_CFG_CLKDIV, priv->clkdiv & 0x1fff) |
>> +		FIELD_PREP(MPI_CFG_CSHI, !!(slave->mode & SPI_CS_HIGH)) |
>> +		FIELD_PREP(MPI_CFG_LSBFIRST, !!(slave->mode & SPI_LSB_FIRST)) |
>> +		FIELD_PREP(MPI_CFG_WIREOR, !!(slave->mode & SPI_3WIRE)) |
>> +		FIELD_PREP(MPI_CFG_IDLELO, cpha != cpol) |
>> +		FIELD_PREP(MPI_CFG_CSLATE, cpha) |
>> +		MPI_CFG_CSENA0 | MPI_CFG_CSENA1 |
>> +		MPI_CFG_CSENA2 | MPI_CFG_CSENA1 |
>> +		MPI_CFG_ENABLE;
>> +
>> +	debug("\n mpi_cfg %llx\n", mpi_cfg);
>> +	return mpi_cfg;
>> +}
>> +
>> +/**
>> + * Wait until the SPI bus is ready
>> + *
>> + * @param	dev	SPI device to wait for
>> + */
>> +static void octeon_spi_wait_ready(struct udevice *dev)
>> +{
>> +	struct udevice *bus = dev_get_parent(dev);
>> +	struct octeon_spi *priv = dev_get_priv(bus);
>> +	void *base = priv->base;
>> +	u64 mpi_sts;
>> +
>> +	do {
>> +		mpi_sts = readq(base + MPI_STS);
>> +		WATCHDOG_RESET();
>> +	} while (mpi_sts & MPI_STS_BUSY);
>> +
>> +	debug("%s(%s)\n", __func__, dev->name);
>> +}
>> +
>> +/**
>> + * Claim the bus for a slave device
>> + *
>> + * @param	dev	SPI bus
>> + *
>> + * @return	0 for success, -EINVAL if chip select is invalid
>> + */
>> +static int octeon_spi_claim_bus(struct udevice *dev)
>> +{
>> +	struct udevice *bus = dev_get_parent(dev);
>> +	struct octeon_spi *priv = dev_get_priv(bus);
>> +	void *base = priv->base;
>> +	u64 mpi_cfg;
>> +
>> +	debug("\n\n%s(%s)\n", __func__, dev->name);
>> +	if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
>> +		return -EINVAL;
>> +
>> +	if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
>> +		board_acquire_flash_arb(true);
>> +
>> +	mpi_cfg = readq(base + MPI_CFG);
>> +	mpi_cfg &= ~MPI_CFG_TRITX;
>> +	mpi_cfg |= MPI_CFG_ENABLE;
>> +	writeq(mpi_cfg, base + MPI_CFG);
>> +	mpi_cfg = readq(base + MPI_CFG);
>> +	udelay(5);	/** Wait for bus to settle */
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * Release the bus to a slave device
>> + *
>> + * @param	dev	SPI bus
>> + *
>> + * @return	0 for success, -EINVAL if chip select is invalid
>> + */
>> +static int octeon_spi_release_bus(struct udevice *dev)
>> +{
>> +	struct udevice *bus = dev_get_parent(dev);
>> +	struct octeon_spi *priv = dev_get_priv(bus);
>> +	void *base = priv->base;
>> +	u64 mpi_cfg;
>> +
>> +	debug("%s(%s)\n\n", __func__, dev->name);
>> +	if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
>> +		return -EINVAL;
>> +
>> +	if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
>> +		board_acquire_flash_arb(false);
>> +
>> +	mpi_cfg = readq(base + MPI_CFG);
>> +	mpi_cfg &= ~MPI_CFG_ENABLE;
>> +	writeq(mpi_cfg, base + MPI_CFG);
>> +	mpi_cfg = readq(base + MPI_CFG);
>> +	udelay(1);
>> +
>> +	return 0;
>> +}
>> +
>> +static int octeon_spi_xfer(struct udevice *dev, unsigned int bitlen,
>> +			   const void *dout, void *din, unsigned long flags)
>> +{
>> +	struct udevice *bus = dev_get_parent(dev);
>> +	struct octeon_spi *priv = dev_get_priv(bus);
>> +	void *base = priv->base;
>> +	u64 mpi_tx;
>> +	u64 mpi_cfg;
>> +	u64 wide_dat = 0;
>> +	int len = bitlen / 8;
>> +	int i;
>> +	const u8 *tx_data = dout;
>> +	u8 *rx_data = din;
>> +	int cs = spi_chip_select(dev);
>> +
>> +	if (!OCTEON_SPI_CS_VALID(cs))
>> +		return -EINVAL;
>> +
>> +	debug("\n %s(%s, %u, %p, %p, 0x%lx), cs: %d\n",
>> +	      __func__, dev->name, bitlen, dout, din, flags, cs);
>> +
>> +	mpi_cfg = octeon_spi_set_mpicfg(dev);
>> +	if (mpi_cfg != readq(base + MPI_CFG)) {
>> +		writeq(mpi_cfg, base + MPI_CFG);
>> +		mpi_cfg = readq(base + MPI_CFG);
>> +		udelay(10);
>> +	}
>> +
>> +	debug("\n mpi_cfg upd %llx\n", mpi_cfg);
>> +
>> +	/*
>> +	 * Start by writing and reading 8 bytes at a time. While we can support
>> +	 * up to 10, it's easier to just use 8 with the MPI_WIDE_DAT register.
>> +	 */
>> +	while (len > 8) {
>> +		if (tx_data) {
>> +			wide_dat = get_unaligned((u64 *)tx_data);
>> +			debug("  tx: %016llx \t", (unsigned long long)wide_dat);
>> +			tx_data += 8;
>> +			writeq(wide_dat, base + MPI_WIDE_DAT);
>> +		}
>> +
>> +		mpi_tx = FIELD_PREP(MPI_TX_CSID, cs) |
>> +			FIELD_PREP(MPI_TX_LEAVECS, 1) |
>> +			FIELD_PREP(MPI_TX_TXNUM, tx_data ? 8 : 0) |
>> +			FIELD_PREP(MPI_TX_TOTNUM, 8);
>> +		writeq(mpi_tx, base + MPI_TX);
>> +
>> +		octeon_spi_wait_ready(dev);
>> +
>> +		debug("\n ");
>> +
>> +		if (rx_data) {
>> +			wide_dat = readq(base + MPI_WIDE_DAT);
>> +			debug("  rx: %016llx\t", (unsigned long long)wide_dat);
>> +			*(u64 *)rx_data = wide_dat;
>> +			rx_data += 8;
>> +		}
>> +		len -= 8;
>> +	}
>> +
>> +	debug("\n ");
>> +
>> +	/* Write and read the rest of the data */
>> +	if (tx_data) {
>> +		for (i = 0; i < len; i++) {
>> +			debug("  tx: %02x\n", *tx_data);
>> +			writeq(*tx_data++, base + MPI_DAT(i));
>> +		}
>> +	}
>> +
>> +	mpi_tx = FIELD_PREP(MPI_TX_CSID, cs) |
>> +		FIELD_PREP(MPI_TX_LEAVECS, !(flags & SPI_XFER_END)) |
>> +		FIELD_PREP(MPI_TX_TXNUM, tx_data ? len : 0) |
>> +		FIELD_PREP(MPI_TX_TOTNUM, len);
>> +	writeq(mpi_tx, base + MPI_TX);
>> +
>> +	octeon_spi_wait_ready(dev);
>> +
>> +	debug("\n ");
>> +
>> +	if (rx_data) {
>> +		for (i = 0; i < len; i++) {
>> +			*rx_data = readq(base + MPI_DAT(i)) & 0xff;
>> +			debug("  rx: %02x\n", *rx_data);
>> +			rx_data++;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int octeontx2_spi_xfer(struct udevice *dev, unsigned int bitlen,
>> +			      const void *dout, void *din, unsigned long flags)
>> +{
>> +	struct udevice *bus = dev_get_parent(dev);
>> +	struct octeon_spi *priv = dev_get_priv(bus);
>> +	void *base = priv->base;
>> +	u64 mpi_xmit;
>> +	u64 mpi_cfg;
>> +	u64 wide_dat = 0;
>> +	int len = bitlen / 8;
>> +	int rem;
>> +	int i;
>> +	const u8 *tx_data = dout;
>> +	u8 *rx_data = din;
>> +	int cs = spi_chip_select(dev);
>> +
>> +	if (!OCTEON_SPI_CS_VALID(cs))
>> +		return -EINVAL;
>> +
>> +	debug("\n %s(%s, %u, %p, %p, 0x%lx), cs: %d\n",
>> +	      __func__, dev->name, bitlen, dout, din, flags, cs);
>> +
>> +	mpi_cfg = octeon_spi_set_mpicfg(dev);
>> +
>> +	mpi_cfg |= MPI_CFG_TRITX | MPI_CFG_LEGACY_DIS | MPI_CFG_CS_STICKY |
>> +		MPI_CFG_TB100_EN;
>> +
>> +	mpi_cfg &= ~MPI_CFG_IOMODE;
>> +	if (flags & (SPI_TX_DUAL | SPI_RX_DUAL))
>> +		mpi_cfg |= FIELD_PREP(MPI_CFG_IOMODE, 2);
>> +	if (flags & (SPI_TX_QUAD | SPI_RX_QUAD))
>> +		mpi_cfg |= FIELD_PREP(MPI_CFG_IOMODE, 3);
>> +
>> +	if (mpi_cfg != readq(base + MPI_CFG)) {
>> +		writeq(mpi_cfg, base + MPI_CFG);
>> +		mpi_cfg = readq(base + MPI_CFG);
>> +		udelay(10);
>> +	}
>> +
>> +	debug("\n mpi_cfg upd %llx\n\n", mpi_cfg);
>> +
>> +	/* Start by writing or reading 1024 bytes at a time. */
>> +	while (len > 1024) {
>> +		if (tx_data) {
>> +			/* 8 bytes per iteration */
>> +			for (i = 0; i < 128; i++) {
>> +				wide_dat = get_unaligned((u64 *)tx_data);
>> +				debug("  tx: %016llx \t",
>> +				      (unsigned long long)wide_dat);
>> +				if ((i % 4) == 3)
>> +					debug("\n");
>> +				tx_data += 8;
>> +				writeq(wide_dat, base + MPI_WIDE_BUF(i));
>> +			}
>> +		}
>> +
>> +		mpi_xmit = FIELD_PREP(MPI_XMIT_CSID, cs) | MPI_XMIT_LEAVECS |
>> +			FIELD_PREP(MPI_XMIT_TXNUM, tx_data ? 1024 : 0) |
>> +			FIELD_PREP(MPI_XMIT_TOTNUM, 1024);
>> +		writeq(mpi_xmit, base + MPI_XMIT);
>> +
>> +		octeon_spi_wait_ready(dev);
>> +
>> +		debug("\n ");
>> +
>> +		if (rx_data) {
>> +			/* 8 bytes per iteration */
>> +			for (i = 0; i < 128; i++) {
>> +				wide_dat = readq(base + MPI_WIDE_BUF(i));
>> +				debug("  rx: %016llx\t",
>> +				      (unsigned long long)wide_dat);
>> +				if ((i % 4) == 3)
>> +					debug("\n");
>> +				*(u64 *)rx_data = wide_dat;
>> +				rx_data += 8;
>> +			}
>> +		}
>> +		len -= 1024;
>> +	}
>> +
>> +	if (tx_data) {
>> +		rem = len % 8;
>> +		/* 8 bytes per iteration */
>> +		for (i = 0; i < len / 8; i++) {
>> +			wide_dat = get_unaligned((u64 *)tx_data);
>> +			debug("  tx: %016llx \t",
>> +			      (unsigned long long)wide_dat);
>> +			if ((i % 4) == 3)
>> +				debug("\n");
>> +			tx_data += 8;
>> +			writeq(wide_dat, base + MPI_WIDE_BUF(i));
>> +		}
>> +		if (rem) {
>> +			memcpy(&wide_dat, tx_data, rem);
>> +			debug("  rtx: %016llx\t", wide_dat);
>> +			writeq(wide_dat, base + MPI_WIDE_BUF(i));
>> +		}
>> +	}
>> +
>> +	mpi_xmit = FIELD_PREP(MPI_XMIT_CSID, cs) |
>> +		FIELD_PREP(MPI_XMIT_LEAVECS, !(flags & SPI_XFER_END)) |
>> +		FIELD_PREP(MPI_XMIT_TXNUM, tx_data ? len : 0) |
>> +		FIELD_PREP(MPI_XMIT_TOTNUM, len);
>> +	writeq(mpi_xmit, base + MPI_XMIT);
>> +
>> +	octeon_spi_wait_ready(dev);
>> +
>> +	debug("\n ");
>> +
>> +	if (rx_data) {
>> +		rem = len % 8;
>> +		/* 8 bytes per iteration */
>> +		for (i = 0; i < len / 8; i++) {
>> +			wide_dat = readq(base + MPI_WIDE_BUF(i));
>> +			debug("  rx: %016llx\t",
>> +			      (unsigned long long)wide_dat);
>> +			if ((i % 4) == 3)
>> +				debug("\n");
>> +			*(u64 *)rx_data = wide_dat;
>> +			rx_data += 8;
>> +		}
>> +		if (rem) {
>> +			wide_dat = readq(base + MPI_WIDE_BUF(i));
>> +			debug("  rrx: %016llx\t",
>> +			      (unsigned long long)wide_dat);
>> +			memcpy(rx_data, &wide_dat, rem);
>> +			rx_data += rem;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static bool octeon_spi_supports_op(struct spi_slave *slave,
>> +				   const struct spi_mem_op *op)
>> +{
>> +	/* For now, support only below combinations
>> +	 * 1-1-1
>> +	 * 1-1-2 1-2-2
>> +	 * 1-1-4 1-4-4
>> +	 */
>> +	if (op->cmd.buswidth != 1)
>> +		return false;
>> +	return true;
>> +}
>> +
>> +static int octeon_spi_exec_op(struct spi_slave *slave,
>> +			      const struct spi_mem_op *op)
>> +{
>> +	unsigned long flags = SPI_XFER_BEGIN;
>> +	const void *tx;
>> +	void *rx;
>> +	u8 opcode, *buf;
>> +	u8 *addr;
>> +	int i, temp, ret;
>> +
>> +	if (op->cmd.buswidth != 1)
>> +		return -ENOTSUPP;
>> +
>> +	/* Send CMD */
>> +	i = 0;
>> +	opcode = op->cmd.opcode;
>> +
>> +	if (!op->data.nbytes && !op->addr.nbytes && !op->dummy.nbytes)
>> +		flags |= SPI_XFER_END;
>> +
>> +	ret = octeontx2_spi_xfer(slave->dev, 8, (void *)&opcode, NULL, flags);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* Send Address and dummy */
>> +	if (op->addr.nbytes) {
>> +		/* Alloc buffer for address+dummy */
>> +		buf = (u8 *)calloc(1, op->addr.nbytes + op->dummy.nbytes);
>> +		if (!buf) {
>> +			printf("%s Out of memory\n", __func__);
>> +			return -ENOMEM;
>> +		}
>> +		addr = (u8 *)&op->addr.val;
>> +		for (temp = 0; temp < op->addr.nbytes; temp++)
>> +			buf[i++] = *(u8 *)(addr + op->addr.nbytes - 1 - temp);
>> +		for (temp = 0; temp < op->dummy.nbytes; temp++)
>> +			buf[i++] = 0xff;
>> +		if (op->addr.buswidth == 2)
>> +			flags |= SPI_RX_DUAL;
>> +		if (op->addr.buswidth == 4)
>> +			flags |= SPI_RX_QUAD;
>> +
>> +		if (!op->data.nbytes)
>> +			flags |= SPI_XFER_END;
>> +		ret = octeontx2_spi_xfer(slave->dev, i * 8, (void *)buf, NULL,
>> +					 flags);
>> +		free(buf);
>> +		if (ret < 0)
>> +			return ret;
>> +	}
>> +	if (!op->data.nbytes)
>> +		return 0;
>> +
>> +	/* Send/Receive Data */
>> +	flags |= SPI_XFER_END;
>> +	if (op->data.buswidth == 2)
>> +		flags |= SPI_RX_DUAL;
>> +	if (op->data.buswidth == 4)
>> +		flags |= SPI_RX_QUAD;
>> +
>> +	rx = (op->data.dir == SPI_MEM_DATA_IN) ? op->data.buf.in : NULL;
>> +	tx = (op->data.dir == SPI_MEM_DATA_OUT) ? op->data.buf.out : NULL;
>> +
>> +	ret = octeontx2_spi_xfer(slave->dev, (op->data.nbytes * 8), tx, rx,
>> +				 flags);
>> +	return ret;
>> +}
>> +
>> +static const struct spi_controller_mem_ops octeontx2_spi_mem_ops = {
>> +	.supports_op = octeon_spi_supports_op,
>> +	.exec_op = octeon_spi_exec_op,
>> +};
>> +
>> +/**
>> + * Set the speed of the SPI bus
>> + *
>> + * @param	bus	bus to set
>> + * @param	max_hz	maximum speed supported
>> + */
>> +static int octeon_spi_set_speed(struct udevice *bus, uint max_hz)
>> +{
>> +	struct octeon_spi *priv = dev_get_priv(bus);
>> +	ulong clk_rate;
>> +	u32 calc_hz;
>> +
>> +	if (max_hz > OCTEON_SPI_MAX_CLOCK_HZ)
>> +		max_hz = OCTEON_SPI_MAX_CLOCK_HZ;
>> +
>> +	clk_rate = clk_get_rate(&priv->clk);
>> +	if (IS_ERR_VALUE(clk_rate))
>> +		return -EINVAL;
>> +
>> +	debug("%s(%s, %u, %lu)\n", __func__, bus->name, max_hz, clk_rate);
>> +
>> +	priv->clkdiv = clk_rate / (2 * max_hz);
>> +	while (1) {
>> +		calc_hz = clk_rate / (2 * priv->clkdiv);
>> +		if (calc_hz <= max_hz)
>> +			break;
>> +		priv->clkdiv += 1;
>> +	}
>> +
>> +	if (priv->clkdiv > 8191)
>> +		return -EINVAL;
>> +
>> +	debug("%s: clkdiv=%d\n", __func__, priv->clkdiv);
>> +
>> +	return 0;
>> +}
>> +
>> +static int octeon_spi_set_mode(struct udevice *bus, uint mode)
>> +{
>> +	/* We don't set it here */
>> +	return 0;
>> +}
>> +
>> +static const struct dm_spi_ops octeon_spi_ops = {
>> +	.claim_bus	= octeon_spi_claim_bus,
>> +	.release_bus	= octeon_spi_release_bus,
>> +	.set_speed	= octeon_spi_set_speed,
>> +	.set_mode	= octeon_spi_set_mode,
>> +	.xfer		= octeon_spi_xfer,
>> +};
>> +
>> +static const struct dm_spi_ops octeontx2_spi_ops = {
>> +	.claim_bus	= octeon_spi_claim_bus,
>> +	.release_bus	= octeon_spi_release_bus,
>> +	.set_speed	= octeon_spi_set_speed,
>> +	.set_mode	= octeon_spi_set_mode,
>> +	.xfer		= octeontx2_spi_xfer,
>> +	.mem_ops	= &octeontx2_spi_mem_ops,
>> +};
>> +
>> +static int octeon_spi_probe(struct udevice *dev)
>> +{
>> +	struct octeon_spi *priv = dev_get_priv(dev);
>> +	const struct octeon_spi_data *data;
>> +	int ret;
>> +
>> +	data = (const struct octeon_spi_data *)dev_get_driver_data(dev);
>> +	if (data->probe == PROBE_PCI) {
>> +		pci_dev_t bdf = dm_pci_get_bdf(dev);
>> +
>> +		debug("SPI PCI device: %x\n", bdf);
>> +		priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
>> +					    PCI_REGION_MEM);
>> +	} else {
>> +		priv->base = dev_remap_addr(dev);
>> +	}
>> +
>> +	priv->base += data->reg_offs;
>> +
>> +	/* Octeon TX2 needs a different ops struct */
>> +	if (device_is_compatible(dev, "cavium,thunderx-spi")) {
>> +		/*
>> +		 * "ops" is const and can't be written directly. So we need
>> +		 * to write the Octeon TX2 ops value using this trick
>> +		 */
>> +		writeq((u64)&octeontx2_spi_ops, (void *)&dev->driver->ops);
>> +	}
> 
> can't you simply add a xfer() function pointer to "struct
> octeon_spi_data" and assign the according xfer function to it? Then
> in octeon_spi_xfer() you can simply call that function pointer. With
> this you only need one instance of "struct dm_spi_ops" and don't need
> this ugly hack ;)

Thanks for the suggestion. I'm leaving for a short vacation later today
and will return end of next week. I'll look into it then.

> Maybe you can add some common code to
> octeon_spi_xfer() itself and reduce the size of the SoC specific xfer
> functions.

Yes, maybe. Please keep in mind that I'm not the original author of
the code, so refactoring is sometimes not that easy. But I'll look
into this as well.

Thanks,
Stefan

>> +
>> +	ret = clk_get_by_index(dev, 0, &priv->clk);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = clk_enable(&priv->clk);
>> +	if (ret)
>> +		return ret;
>> +
>> +	debug("SPI bus %s %d at %p\n", dev->name, dev->seq, priv->base);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct octeon_spi_data spi_octeon_data = {
>> +	.probe = PROBE_DT,
>> +	.reg_offs = 0x0000,
>> +};
>> +
>> +static const struct octeon_spi_data spi_octeontx_data = {
>> +	.probe = PROBE_PCI,
>> +	.reg_offs = 0x1000,
>> +};
>> +
>> +static const struct udevice_id octeon_spi_ids[] = {
>> +	/* MIPS Octeon */
>> +	{ .compatible = "cavium,octeon-3010-spi",
>> +	  .data = (ulong)&spi_octeon_data },
>> +	/* ARM Octeon TX / TX2 */
>> +	{ .compatible = "cavium,thunder-8190-spi",
>> +	  .data = (ulong)&spi_octeontx_data },
>> +	{ }
>> +};
>> +
>> +U_BOOT_DRIVER(octeon_spi) = {
>> +	.name			= "spi_octeon",
>> +	.id			= UCLASS_SPI,
>> +	.of_match		= octeon_spi_ids,
>> +	.probe			= octeon_spi_probe,
>> +	.priv_auto_alloc_size	= sizeof(struct octeon_spi),
>> +	.ops			= &octeon_spi_ops,
>> +};


Viele Gr??e,
Stefan

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr at denx.de

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon
  2020-07-24 13:56   ` Daniel Schwierzeck
  2020-07-24 14:27     ` Stefan Roese
@ 2020-07-30  5:49     ` Stefan Roese
  2020-07-30  6:23       ` Stefan Roese
  2020-07-30  7:50     ` Jagan Teki
  2 siblings, 1 reply; 22+ messages in thread
From: Stefan Roese @ 2020-07-30  5:49 UTC (permalink / raw)
  To: u-boot

Hi Daniel,

On 24.07.20 15:56, Daniel Schwierzeck wrote:
> Am Donnerstag, den 23.07.2020, 12:17 +0200 schrieb Stefan Roese:
>> From: Suneel Garapati <sgarapati@marvell.com>
>>
>> Adds support for SPI controllers found on Octeon II/III and Octeon TX
>> TX2 SoC platforms.
>>
>> Signed-off-by: Aaron Williams <awilliams@marvell.com>
>> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
>> Signed-off-by: Stefan Roese <sr@denx.de>
>> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
>> Cc: Aaron Williams <awilliams@marvell.com>
>> Cc: Chandrakala Chavva <cchavva@marvell.com>
>> Cc: Jagan Teki <jagan@amarulasolutions.com>

<snip>

>> +static const struct dm_spi_ops octeon_spi_ops = {
>> +	.claim_bus	= octeon_spi_claim_bus,
>> +	.release_bus	= octeon_spi_release_bus,
>> +	.set_speed	= octeon_spi_set_speed,
>> +	.set_mode	= octeon_spi_set_mode,
>> +	.xfer		= octeon_spi_xfer,
>> +};
>> +
>> +static const struct dm_spi_ops octeontx2_spi_ops = {
>> +	.claim_bus	= octeon_spi_claim_bus,
>> +	.release_bus	= octeon_spi_release_bus,
>> +	.set_speed	= octeon_spi_set_speed,
>> +	.set_mode	= octeon_spi_set_mode,
>> +	.xfer		= octeontx2_spi_xfer,
>> +	.mem_ops	= &octeontx2_spi_mem_ops,
>> +};
>> +
>> +static int octeon_spi_probe(struct udevice *dev)
>> +{
>> +	struct octeon_spi *priv = dev_get_priv(dev);
>> +	const struct octeon_spi_data *data;
>> +	int ret;
>> +
>> +	data = (const struct octeon_spi_data *)dev_get_driver_data(dev);
>> +	if (data->probe == PROBE_PCI) {
>> +		pci_dev_t bdf = dm_pci_get_bdf(dev);
>> +
>> +		debug("SPI PCI device: %x\n", bdf);
>> +		priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
>> +					    PCI_REGION_MEM);
>> +	} else {
>> +		priv->base = dev_remap_addr(dev);
>> +	}
>> +
>> +	priv->base += data->reg_offs;
>> +
>> +	/* Octeon TX2 needs a different ops struct */
>> +	if (device_is_compatible(dev, "cavium,thunderx-spi")) {
>> +		/*
>> +		 * "ops" is const and can't be written directly. So we need
>> +		 * to write the Octeon TX2 ops value using this trick
>> +		 */
>> +		writeq((u64)&octeontx2_spi_ops, (void *)&dev->driver->ops);
>> +	}
> 
> can't you simply add a xfer() function pointer to "struct
> octeon_spi_data" and assign the according xfer function to it? Then
> in octeon_spi_xfer() you can simply call that function pointer. With
> this you only need one instance of "struct dm_spi_ops" and don't need
> this ugly hack ;)

Unfortuantely its not that easy, as the Octeon TX2 ops struct also has
a " mem_ops" member, which the driver does not support for the other
Octeon models. I could clear this "mem_ops" member in the non Octeon
TX2 case, which is a bit better than the 2nd ops struct. But its still
not really elegent.

Or do you have some other idea on how to implement this in a "better
way"?

Thanks,
Stefan

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon
  2020-07-30  5:49     ` Stefan Roese
@ 2020-07-30  6:23       ` Stefan Roese
  2020-07-30 11:00         ` Daniel Schwierzeck
  0 siblings, 1 reply; 22+ messages in thread
From: Stefan Roese @ 2020-07-30  6:23 UTC (permalink / raw)
  To: u-boot

Hi Daniel,

On 30.07.20 07:49, Stefan Roese wrote:
> Hi Daniel,
> 
> On 24.07.20 15:56, Daniel Schwierzeck wrote:
>> Am Donnerstag, den 23.07.2020, 12:17 +0200 schrieb Stefan Roese:
>>> From: Suneel Garapati <sgarapati@marvell.com>
>>>
>>> Adds support for SPI controllers found on Octeon II/III and Octeon TX
>>> TX2 SoC platforms.
>>>
>>> Signed-off-by: Aaron Williams <awilliams@marvell.com>
>>> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
>>> Signed-off-by: Stefan Roese <sr@denx.de>
>>> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
>>> Cc: Aaron Williams <awilliams@marvell.com>
>>> Cc: Chandrakala Chavva <cchavva@marvell.com>
>>> Cc: Jagan Teki <jagan@amarulasolutions.com>
> 
> <snip>
> 
>>> +static const struct dm_spi_ops octeon_spi_ops = {
>>> +??? .claim_bus??? = octeon_spi_claim_bus,
>>> +??? .release_bus??? = octeon_spi_release_bus,
>>> +??? .set_speed??? = octeon_spi_set_speed,
>>> +??? .set_mode??? = octeon_spi_set_mode,
>>> +??? .xfer??????? = octeon_spi_xfer,
>>> +};
>>> +
>>> +static const struct dm_spi_ops octeontx2_spi_ops = {
>>> +??? .claim_bus??? = octeon_spi_claim_bus,
>>> +??? .release_bus??? = octeon_spi_release_bus,
>>> +??? .set_speed??? = octeon_spi_set_speed,
>>> +??? .set_mode??? = octeon_spi_set_mode,
>>> +??? .xfer??????? = octeontx2_spi_xfer,
>>> +??? .mem_ops??? = &octeontx2_spi_mem_ops,
>>> +};
>>> +
>>> +static int octeon_spi_probe(struct udevice *dev)
>>> +{
>>> +??? struct octeon_spi *priv = dev_get_priv(dev);
>>> +??? const struct octeon_spi_data *data;
>>> +??? int ret;
>>> +
>>> +??? data = (const struct octeon_spi_data *)dev_get_driver_data(dev);
>>> +??? if (data->probe == PROBE_PCI) {
>>> +??????? pci_dev_t bdf = dm_pci_get_bdf(dev);
>>> +
>>> +??????? debug("SPI PCI device: %x\n", bdf);
>>> +??????? priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
>>> +??????????????????????? PCI_REGION_MEM);
>>> +??? } else {
>>> +??????? priv->base = dev_remap_addr(dev);
>>> +??? }
>>> +
>>> +??? priv->base += data->reg_offs;
>>> +
>>> +??? /* Octeon TX2 needs a different ops struct */
>>> +??? if (device_is_compatible(dev, "cavium,thunderx-spi")) {
>>> +??????? /*
>>> +???????? * "ops" is const and can't be written directly. So we need
>>> +???????? * to write the Octeon TX2 ops value using this trick
>>> +???????? */
>>> +??????? writeq((u64)&octeontx2_spi_ops, (void *)&dev->driver->ops);
>>> +??? }
>>
>> can't you simply add a xfer() function pointer to "struct
>> octeon_spi_data" and assign the according xfer function to it? Then
>> in octeon_spi_xfer() you can simply call that function pointer. With
>> this you only need one instance of "struct dm_spi_ops" and don't need
>> this ugly hack ;)
> 
> Unfortuantely its not that easy, as the Octeon TX2 ops struct also has
> a " mem_ops" member, which the driver does not support for the other
> Octeon models. I could clear this "mem_ops" member in the non Octeon
> TX2 case, which is a bit better than the 2nd ops struct. But its still
> not really elegent.
> 
> Or do you have some other idea on how to implement this in a "better
> way"?

BTW: If this SPI driver is the only patch in this series, that you feel
is not ready, then I suggest to drop this one from this patch series
and push the remaining ones to mainline (if they have no issues of
course).

Thanks,
Stefan

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon
  2020-07-23 10:17 ` [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon Stefan Roese
  2020-07-24 13:56   ` Daniel Schwierzeck
@ 2020-07-30  7:44   ` Jagan Teki
  2020-07-30  8:06     ` Stefan Roese
  1 sibling, 1 reply; 22+ messages in thread
From: Jagan Teki @ 2020-07-30  7:44 UTC (permalink / raw)
  To: u-boot

On Thu, Jul 23, 2020 at 3:47 PM Stefan Roese <sr@denx.de> wrote:
>
> From: Suneel Garapati <sgarapati@marvell.com>
>
> Adds support for SPI controllers found on Octeon II/III and Octeon TX
> TX2 SoC platforms.
>
> Signed-off-by: Aaron Williams <awilliams@marvell.com>
> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
> Signed-off-by: Stefan Roese <sr@denx.de>
> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
> Cc: Aaron Williams <awilliams@marvell.com>
> Cc: Chandrakala Chavva <cchavva@marvell.com>
> Cc: Jagan Teki <jagan@amarulasolutions.com>
>
> ---
>
> Changes in v2:
> - Newly added to this series
> - Removed inclusion of "common.h"
> - Added "depends on DM_PCI" to Kconfig
> - Tested on MIPS Octeon and ARM Octeon TX2
> - Fixed issues with Octeon TX2 registration. Now only one driver is
>   registered and the "ops" is overwritten in the Octeon TX2 case.
> - Use dev_get_driver_data() to get the driver data struct
> - Removed "struct pci_device_id" definition and U_BOOT_PCI_DEVICE()
>   as its not needed for the PCI based probing on Octeon TX2
>
>  drivers/spi/Kconfig      |   8 +
>  drivers/spi/Makefile     |   1 +
>  drivers/spi/octeon_spi.c | 647 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 656 insertions(+)
>  create mode 100644 drivers/spi/octeon_spi.c
>
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index 30d808d7bb..3fc2d0674a 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -240,6 +240,14 @@ config NXP_FSPI
>           Enable the NXP FlexSPI (FSPI) driver. This driver can be used to
>           access the SPI NOR flash on platforms embedding this NXP IP core.
>
> +config OCTEON_SPI
> +       bool "Octeon SPI driver"
> +       depends on DM_PCI && (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2)
> +       help
> +         Enable the Octeon SPI driver. This driver can be used to
> +         access the SPI NOR flash on Octeon II/III and OcteonTX/TX2
> +         SoC platforms.
> +
>  config OMAP3_SPI
>         bool "McSPI driver for OMAP"
>         help
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index 4e7461771f..b5c9ff1af8 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_MXC_SPI) += mxc_spi.o
>  obj-$(CONFIG_MXS_SPI) += mxs_spi.o
>  obj-$(CONFIG_NXP_FSPI) += nxp_fspi.o
>  obj-$(CONFIG_ATCSPI200_SPI) += atcspi200_spi.o
> +obj-$(CONFIG_OCTEON_SPI) += octeon_spi.o
>  obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
>  obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
>  obj-$(CONFIG_PL022_SPI) += pl022_spi.o
> diff --git a/drivers/spi/octeon_spi.c b/drivers/spi/octeon_spi.c
> new file mode 100644
> index 0000000000..2fb39e444c
> --- /dev/null
> +++ b/drivers/spi/octeon_spi.c
> @@ -0,0 +1,647 @@
> +// SPDX-License-Identifier:    GPL-2.0
> +/*
> + * Copyright (C) 2018 Marvell International Ltd.
> + *
> + * https://spdx.org/licenses
> + */
> +
> +#include <clk.h>
> +#include <dm.h>
> +#include <malloc.h>
> +#include <spi.h>
> +#include <spi-mem.h>
> +#include <watchdog.h>
> +#include <asm/io.h>
> +#include <asm/unaligned.h>
> +#include <linux/bitfield.h>
> +#include <linux/compat.h>
> +#include <linux/delay.h>
> +
> +#define OCTEON_SPI_MAX_BYTES   9
> +#define OCTEON_SPI_MAX_CLOCK_HZ        50000000
> +
> +#define OCTEON_SPI_NUM_CS      4
> +
> +#define OCTEON_SPI_CS_VALID(cs)        ((cs) < OCTEON_SPI_NUM_CS)
> +
> +#define MPI_CFG                        0x0000
> +#define MPI_STS                        0x0008
> +#define MPI_TX                 0x0010
> +#define MPI_XMIT               0x0018
> +#define MPI_WIDE_DAT           0x0040
> +#define MPI_IO_CTL             0x0048
> +#define MPI_DAT(X)             (0x0080 + ((X) << 3))
> +#define MPI_WIDE_BUF(X)                (0x0800 + ((X) << 3))
> +#define MPI_CYA_CFG            0x1000
> +#define MPI_CLKEN              0x1080
> +
> +#define MPI_CFG_ENABLE         BIT_ULL(0)
> +#define MPI_CFG_IDLELO         BIT_ULL(1)
> +#define MPI_CFG_CLK_CONT       BIT_ULL(2)
> +#define MPI_CFG_WIREOR         BIT_ULL(3)
> +#define MPI_CFG_LSBFIRST       BIT_ULL(4)
> +#define MPI_CFG_CS_STICKY      BIT_ULL(5)
> +#define MPI_CFG_CSHI           BIT_ULL(7)
> +#define MPI_CFG_IDLECLKS       GENMASK_ULL(9, 8)
> +#define MPI_CFG_TRITX          BIT_ULL(10)
> +#define MPI_CFG_CSLATE         BIT_ULL(11)
> +#define MPI_CFG_CSENA0         BIT_ULL(12)
> +#define MPI_CFG_CSENA1         BIT_ULL(13)
> +#define MPI_CFG_CSENA2         BIT_ULL(14)
> +#define MPI_CFG_CSENA3         BIT_ULL(15)
> +#define MPI_CFG_CLKDIV         GENMASK_ULL(28, 16)
> +#define MPI_CFG_LEGACY_DIS     BIT_ULL(31)
> +#define MPI_CFG_IOMODE         GENMASK_ULL(35, 34)
> +#define MPI_CFG_TB100_EN       BIT_ULL(49)
> +
> +#define MPI_DAT_DATA           GENMASK_ULL(7, 0)
> +
> +#define MPI_STS_BUSY           BIT_ULL(0)
> +#define MPI_STS_MPI_INTR       BIT_ULL(1)
> +#define MPI_STS_RXNUM          GENMASK_ULL(12, 8)
> +
> +#define MPI_TX_TOTNUM          GENMASK_ULL(4, 0)
> +#define MPI_TX_TXNUM           GENMASK_ULL(12, 8)
> +#define MPI_TX_LEAVECS         BIT_ULL(16)
> +#define MPI_TX_CSID            GENMASK_ULL(21, 20)
> +
> +#define MPI_XMIT_TOTNUM                GENMASK_ULL(10, 0)
> +#define MPI_XMIT_TXNUM         GENMASK_ULL(30, 20)
> +#define MPI_XMIT_BUF_SEL       BIT_ULL(59)
> +#define MPI_XMIT_LEAVECS       BIT_ULL(60)
> +#define MPI_XMIT_CSID          GENMASK_ULL(62, 61)
> +
> +enum {
> +       PROBE_PCI = 0,          /* PCI based probing */
> +       PROBE_DT,               /* DT based probing */
> +};
> +
> +/* Used on Octeon TX2 */
> +void board_acquire_flash_arb(bool acquire);
> +
> +struct octeon_spi_data {
> +       int probe;
> +       u32 reg_offs;
> +};
> +
> +/* Local driver data structure */
> +struct octeon_spi {
> +       void __iomem *base;     /* Register base address */
> +       struct clk clk;
> +       u32 clkdiv;             /* Clock divisor for device speed */
> +};
> +
> +static u64 octeon_spi_set_mpicfg(struct udevice *dev)
> +{
> +       struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
> +       struct udevice *bus = dev_get_parent(dev);
> +       struct octeon_spi *priv = dev_get_priv(bus);
> +       u64 mpi_cfg;
> +       uint max_speed = slave->max_hz;
> +       bool cpha, cpol;
> +
> +       if (!max_speed)
> +               max_speed = 12500000;
> +       if (max_speed > OCTEON_SPI_MAX_CLOCK_HZ)
> +               max_speed = OCTEON_SPI_MAX_CLOCK_HZ;
> +
> +       debug("\n slave params %d %d %d\n", slave->cs,
> +             slave->max_hz, slave->mode);
> +       cpha = !!(slave->mode & SPI_CPHA);
> +       cpol = !!(slave->mode & SPI_CPOL);
> +
> +       mpi_cfg = FIELD_PREP(MPI_CFG_CLKDIV, priv->clkdiv & 0x1fff) |
> +               FIELD_PREP(MPI_CFG_CSHI, !!(slave->mode & SPI_CS_HIGH)) |
> +               FIELD_PREP(MPI_CFG_LSBFIRST, !!(slave->mode & SPI_LSB_FIRST)) |
> +               FIELD_PREP(MPI_CFG_WIREOR, !!(slave->mode & SPI_3WIRE)) |
> +               FIELD_PREP(MPI_CFG_IDLELO, cpha != cpol) |
> +               FIELD_PREP(MPI_CFG_CSLATE, cpha) |
> +               MPI_CFG_CSENA0 | MPI_CFG_CSENA1 |
> +               MPI_CFG_CSENA2 | MPI_CFG_CSENA1 |
> +               MPI_CFG_ENABLE;
> +
> +       debug("\n mpi_cfg %llx\n", mpi_cfg);
> +       return mpi_cfg;
> +}
> +
> +/**
> + * Wait until the SPI bus is ready
> + *
> + * @param      dev     SPI device to wait for
> + */
> +static void octeon_spi_wait_ready(struct udevice *dev)
> +{
> +       struct udevice *bus = dev_get_parent(dev);
> +       struct octeon_spi *priv = dev_get_priv(bus);
> +       void *base = priv->base;
> +       u64 mpi_sts;
> +
> +       do {
> +               mpi_sts = readq(base + MPI_STS);
> +               WATCHDOG_RESET();
> +       } while (mpi_sts & MPI_STS_BUSY);
> +
> +       debug("%s(%s)\n", __func__, dev->name);
> +}
> +
> +/**
> + * Claim the bus for a slave device
> + *
> + * @param      dev     SPI bus
> + *
> + * @return     0 for success, -EINVAL if chip select is invalid
> + */
> +static int octeon_spi_claim_bus(struct udevice *dev)
> +{
> +       struct udevice *bus = dev_get_parent(dev);
> +       struct octeon_spi *priv = dev_get_priv(bus);
> +       void *base = priv->base;
> +       u64 mpi_cfg;
> +
> +       debug("\n\n%s(%s)\n", __func__, dev->name);
> +       if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
> +               return -EINVAL;
> +
> +       if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
> +               board_acquire_flash_arb(true);
> +
> +       mpi_cfg = readq(base + MPI_CFG);
> +       mpi_cfg &= ~MPI_CFG_TRITX;
> +       mpi_cfg |= MPI_CFG_ENABLE;
> +       writeq(mpi_cfg, base + MPI_CFG);
> +       mpi_cfg = readq(base + MPI_CFG);
> +       udelay(5);      /** Wait for bus to settle */
> +
> +       return 0;
> +}
> +
> +/**
> + * Release the bus to a slave device
> + *
> + * @param      dev     SPI bus
> + *
> + * @return     0 for success, -EINVAL if chip select is invalid
> + */
> +static int octeon_spi_release_bus(struct udevice *dev)
> +{
> +       struct udevice *bus = dev_get_parent(dev);
> +       struct octeon_spi *priv = dev_get_priv(bus);
> +       void *base = priv->base;
> +       u64 mpi_cfg;
> +
> +       debug("%s(%s)\n\n", __func__, dev->name);
> +       if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
> +               return -EINVAL;
> +
> +       if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
> +               board_acquire_flash_arb(false);

Does this acquire flash depends on soc? what exactly it does? I
believe it can go in separate uclass drivers and spi drivers get that
functionality like other drivers. I'm not fond of calling an
architecture or other places code in the driver. However if it local
spi driver then attach driver_data on respective compatible and use it
locally.

Jagan.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon
  2020-07-24 13:56   ` Daniel Schwierzeck
  2020-07-24 14:27     ` Stefan Roese
  2020-07-30  5:49     ` Stefan Roese
@ 2020-07-30  7:50     ` Jagan Teki
  2020-07-30  7:53       ` Stefan Roese
  2 siblings, 1 reply; 22+ messages in thread
From: Jagan Teki @ 2020-07-30  7:50 UTC (permalink / raw)
  To: u-boot

On Fri, Jul 24, 2020 at 7:26 PM Daniel Schwierzeck
<daniel.schwierzeck@gmail.com> wrote:
>
> Am Donnerstag, den 23.07.2020, 12:17 +0200 schrieb Stefan Roese:
> > From: Suneel Garapati <sgarapati@marvell.com>
> >
> > Adds support for SPI controllers found on Octeon II/III and Octeon TX
> > TX2 SoC platforms.
> >
> > Signed-off-by: Aaron Williams <awilliams@marvell.com>
> > Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
> > Signed-off-by: Stefan Roese <sr@denx.de>
> > Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
> > Cc: Aaron Williams <awilliams@marvell.com>
> > Cc: Chandrakala Chavva <cchavva@marvell.com>
> > Cc: Jagan Teki <jagan@amarulasolutions.com>
> >
> > ---
> >
> > Changes in v2:
> > - Newly added to this series
> > - Removed inclusion of "common.h"
> > - Added "depends on DM_PCI" to Kconfig
> > - Tested on MIPS Octeon and ARM Octeon TX2
> > - Fixed issues with Octeon TX2 registration. Now only one driver is
> >   registered and the "ops" is overwritten in the Octeon TX2 case.
> > - Use dev_get_driver_data() to get the driver data struct
> > - Removed "struct pci_device_id" definition and U_BOOT_PCI_DEVICE()
> >   as its not needed for the PCI based probing on Octeon TX2
> >
> >  drivers/spi/Kconfig      |   8 +
> >  drivers/spi/Makefile     |   1 +
> >  drivers/spi/octeon_spi.c | 647 +++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 656 insertions(+)
> >  create mode 100644 drivers/spi/octeon_spi.c
> >
> > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> > index 30d808d7bb..3fc2d0674a 100644
> > --- a/drivers/spi/Kconfig
> > +++ b/drivers/spi/Kconfig
> > @@ -240,6 +240,14 @@ config NXP_FSPI
> >         Enable the NXP FlexSPI (FSPI) driver. This driver can be used to
> >         access the SPI NOR flash on platforms embedding this NXP IP core.
> >
> > +config OCTEON_SPI
> > +     bool "Octeon SPI driver"
> > +     depends on DM_PCI && (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2)
> > +     help
> > +       Enable the Octeon SPI driver. This driver can be used to
> > +       access the SPI NOR flash on Octeon II/III and OcteonTX/TX2
> > +       SoC platforms.
> > +
> >  config OMAP3_SPI
> >       bool "McSPI driver for OMAP"
> >       help
> > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> > index 4e7461771f..b5c9ff1af8 100644
> > --- a/drivers/spi/Makefile
> > +++ b/drivers/spi/Makefile
> > @@ -43,6 +43,7 @@ obj-$(CONFIG_MXC_SPI) += mxc_spi.o
> >  obj-$(CONFIG_MXS_SPI) += mxs_spi.o
> >  obj-$(CONFIG_NXP_FSPI) += nxp_fspi.o
> >  obj-$(CONFIG_ATCSPI200_SPI) += atcspi200_spi.o
> > +obj-$(CONFIG_OCTEON_SPI) += octeon_spi.o
> >  obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
> >  obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
> >  obj-$(CONFIG_PL022_SPI) += pl022_spi.o
> > diff --git a/drivers/spi/octeon_spi.c b/drivers/spi/octeon_spi.c
> > new file mode 100644
> > index 0000000000..2fb39e444c
> > --- /dev/null
> > +++ b/drivers/spi/octeon_spi.c
> > @@ -0,0 +1,647 @@
> > +// SPDX-License-Identifier:    GPL-2.0
> > +/*
> > + * Copyright (C) 2018 Marvell International Ltd.
> > + *
> > + * https://spdx.org/licenses
> > + */
> > +
> > +#include <clk.h>
> > +#include <dm.h>
> > +#include <malloc.h>
> > +#include <spi.h>
> > +#include <spi-mem.h>
> > +#include <watchdog.h>
> > +#include <asm/io.h>
> > +#include <asm/unaligned.h>
> > +#include <linux/bitfield.h>
> > +#include <linux/compat.h>
> > +#include <linux/delay.h>
> > +
> > +#define OCTEON_SPI_MAX_BYTES 9
> > +#define OCTEON_SPI_MAX_CLOCK_HZ      50000000
> > +
> > +#define OCTEON_SPI_NUM_CS    4
> > +
> > +#define OCTEON_SPI_CS_VALID(cs)      ((cs) < OCTEON_SPI_NUM_CS)
> > +
> > +#define MPI_CFG                      0x0000
> > +#define MPI_STS                      0x0008
> > +#define MPI_TX                       0x0010
> > +#define MPI_XMIT             0x0018
> > +#define MPI_WIDE_DAT         0x0040
> > +#define MPI_IO_CTL           0x0048
> > +#define MPI_DAT(X)           (0x0080 + ((X) << 3))
> > +#define MPI_WIDE_BUF(X)              (0x0800 + ((X) << 3))
> > +#define MPI_CYA_CFG          0x1000
> > +#define MPI_CLKEN            0x1080
> > +
> > +#define MPI_CFG_ENABLE               BIT_ULL(0)
> > +#define MPI_CFG_IDLELO               BIT_ULL(1)
> > +#define MPI_CFG_CLK_CONT     BIT_ULL(2)
> > +#define MPI_CFG_WIREOR               BIT_ULL(3)
> > +#define MPI_CFG_LSBFIRST     BIT_ULL(4)
> > +#define MPI_CFG_CS_STICKY    BIT_ULL(5)
> > +#define MPI_CFG_CSHI         BIT_ULL(7)
> > +#define MPI_CFG_IDLECLKS     GENMASK_ULL(9, 8)
> > +#define MPI_CFG_TRITX                BIT_ULL(10)
> > +#define MPI_CFG_CSLATE               BIT_ULL(11)
> > +#define MPI_CFG_CSENA0               BIT_ULL(12)
> > +#define MPI_CFG_CSENA1               BIT_ULL(13)
> > +#define MPI_CFG_CSENA2               BIT_ULL(14)
> > +#define MPI_CFG_CSENA3               BIT_ULL(15)
> > +#define MPI_CFG_CLKDIV               GENMASK_ULL(28, 16)
> > +#define MPI_CFG_LEGACY_DIS   BIT_ULL(31)
> > +#define MPI_CFG_IOMODE               GENMASK_ULL(35, 34)
> > +#define MPI_CFG_TB100_EN     BIT_ULL(49)
> > +
> > +#define MPI_DAT_DATA         GENMASK_ULL(7, 0)
> > +
> > +#define MPI_STS_BUSY         BIT_ULL(0)
> > +#define MPI_STS_MPI_INTR     BIT_ULL(1)
> > +#define MPI_STS_RXNUM                GENMASK_ULL(12, 8)
> > +
> > +#define MPI_TX_TOTNUM                GENMASK_ULL(4, 0)
> > +#define MPI_TX_TXNUM         GENMASK_ULL(12, 8)
> > +#define MPI_TX_LEAVECS               BIT_ULL(16)
> > +#define MPI_TX_CSID          GENMASK_ULL(21, 20)
> > +
> > +#define MPI_XMIT_TOTNUM              GENMASK_ULL(10, 0)
> > +#define MPI_XMIT_TXNUM               GENMASK_ULL(30, 20)
> > +#define MPI_XMIT_BUF_SEL     BIT_ULL(59)
> > +#define MPI_XMIT_LEAVECS     BIT_ULL(60)
> > +#define MPI_XMIT_CSID                GENMASK_ULL(62, 61)
> > +
> > +enum {
> > +     PROBE_PCI = 0,          /* PCI based probing */
> > +     PROBE_DT,               /* DT based probing */
> > +};
> > +
> > +/* Used on Octeon TX2 */
> > +void board_acquire_flash_arb(bool acquire);
> > +
> > +struct octeon_spi_data {
> > +     int probe;
> > +     u32 reg_offs;
> > +};
> > +
> > +/* Local driver data structure */
> > +struct octeon_spi {
> > +     void __iomem *base;     /* Register base address */
> > +     struct clk clk;
> > +     u32 clkdiv;             /* Clock divisor for device speed */
> > +};
> > +
> > +static u64 octeon_spi_set_mpicfg(struct udevice *dev)
> > +{
> > +     struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
> > +     struct udevice *bus = dev_get_parent(dev);
> > +     struct octeon_spi *priv = dev_get_priv(bus);
> > +     u64 mpi_cfg;
> > +     uint max_speed = slave->max_hz;
> > +     bool cpha, cpol;
> > +
> > +     if (!max_speed)
> > +             max_speed = 12500000;
> > +     if (max_speed > OCTEON_SPI_MAX_CLOCK_HZ)
> > +             max_speed = OCTEON_SPI_MAX_CLOCK_HZ;
> > +
> > +     debug("\n slave params %d %d %d\n", slave->cs,
> > +           slave->max_hz, slave->mode);
> > +     cpha = !!(slave->mode & SPI_CPHA);
> > +     cpol = !!(slave->mode & SPI_CPOL);
> > +
> > +     mpi_cfg = FIELD_PREP(MPI_CFG_CLKDIV, priv->clkdiv & 0x1fff) |
> > +             FIELD_PREP(MPI_CFG_CSHI, !!(slave->mode & SPI_CS_HIGH)) |
> > +             FIELD_PREP(MPI_CFG_LSBFIRST, !!(slave->mode & SPI_LSB_FIRST)) |
> > +             FIELD_PREP(MPI_CFG_WIREOR, !!(slave->mode & SPI_3WIRE)) |
> > +             FIELD_PREP(MPI_CFG_IDLELO, cpha != cpol) |
> > +             FIELD_PREP(MPI_CFG_CSLATE, cpha) |
> > +             MPI_CFG_CSENA0 | MPI_CFG_CSENA1 |
> > +             MPI_CFG_CSENA2 | MPI_CFG_CSENA1 |
> > +             MPI_CFG_ENABLE;
> > +
> > +     debug("\n mpi_cfg %llx\n", mpi_cfg);
> > +     return mpi_cfg;
> > +}
> > +
> > +/**
> > + * Wait until the SPI bus is ready
> > + *
> > + * @param    dev     SPI device to wait for
> > + */
> > +static void octeon_spi_wait_ready(struct udevice *dev)
> > +{
> > +     struct udevice *bus = dev_get_parent(dev);
> > +     struct octeon_spi *priv = dev_get_priv(bus);
> > +     void *base = priv->base;
> > +     u64 mpi_sts;
> > +
> > +     do {
> > +             mpi_sts = readq(base + MPI_STS);
> > +             WATCHDOG_RESET();
> > +     } while (mpi_sts & MPI_STS_BUSY);
> > +
> > +     debug("%s(%s)\n", __func__, dev->name);
> > +}
> > +
> > +/**
> > + * Claim the bus for a slave device
> > + *
> > + * @param    dev     SPI bus
> > + *
> > + * @return   0 for success, -EINVAL if chip select is invalid
> > + */
> > +static int octeon_spi_claim_bus(struct udevice *dev)
> > +{
> > +     struct udevice *bus = dev_get_parent(dev);
> > +     struct octeon_spi *priv = dev_get_priv(bus);
> > +     void *base = priv->base;
> > +     u64 mpi_cfg;
> > +
> > +     debug("\n\n%s(%s)\n", __func__, dev->name);
> > +     if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
> > +             return -EINVAL;
> > +
> > +     if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
> > +             board_acquire_flash_arb(true);
> > +
> > +     mpi_cfg = readq(base + MPI_CFG);
> > +     mpi_cfg &= ~MPI_CFG_TRITX;
> > +     mpi_cfg |= MPI_CFG_ENABLE;
> > +     writeq(mpi_cfg, base + MPI_CFG);
> > +     mpi_cfg = readq(base + MPI_CFG);
> > +     udelay(5);      /** Wait for bus to settle */
> > +
> > +     return 0;
> > +}
> > +
> > +/**
> > + * Release the bus to a slave device
> > + *
> > + * @param    dev     SPI bus
> > + *
> > + * @return   0 for success, -EINVAL if chip select is invalid
> > + */
> > +static int octeon_spi_release_bus(struct udevice *dev)
> > +{
> > +     struct udevice *bus = dev_get_parent(dev);
> > +     struct octeon_spi *priv = dev_get_priv(bus);
> > +     void *base = priv->base;
> > +     u64 mpi_cfg;
> > +
> > +     debug("%s(%s)\n\n", __func__, dev->name);
> > +     if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
> > +             return -EINVAL;
> > +
> > +     if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
> > +             board_acquire_flash_arb(false);
> > +
> > +     mpi_cfg = readq(base + MPI_CFG);
> > +     mpi_cfg &= ~MPI_CFG_ENABLE;
> > +     writeq(mpi_cfg, base + MPI_CFG);
> > +     mpi_cfg = readq(base + MPI_CFG);
> > +     udelay(1);
> > +
> > +     return 0;
> > +}
> > +
> > +static int octeon_spi_xfer(struct udevice *dev, unsigned int bitlen,
> > +                        const void *dout, void *din, unsigned long flags)
> > +{
> > +     struct udevice *bus = dev_get_parent(dev);
> > +     struct octeon_spi *priv = dev_get_priv(bus);
> > +     void *base = priv->base;
> > +     u64 mpi_tx;
> > +     u64 mpi_cfg;
> > +     u64 wide_dat = 0;
> > +     int len = bitlen / 8;
> > +     int i;
> > +     const u8 *tx_data = dout;
> > +     u8 *rx_data = din;
> > +     int cs = spi_chip_select(dev);
> > +
> > +     if (!OCTEON_SPI_CS_VALID(cs))
> > +             return -EINVAL;
> > +
> > +     debug("\n %s(%s, %u, %p, %p, 0x%lx), cs: %d\n",
> > +           __func__, dev->name, bitlen, dout, din, flags, cs);
> > +
> > +     mpi_cfg = octeon_spi_set_mpicfg(dev);
> > +     if (mpi_cfg != readq(base + MPI_CFG)) {
> > +             writeq(mpi_cfg, base + MPI_CFG);
> > +             mpi_cfg = readq(base + MPI_CFG);
> > +             udelay(10);
> > +     }
> > +
> > +     debug("\n mpi_cfg upd %llx\n", mpi_cfg);
> > +
> > +     /*
> > +      * Start by writing and reading 8 bytes at a time. While we can support
> > +      * up to 10, it's easier to just use 8 with the MPI_WIDE_DAT register.
> > +      */
> > +     while (len > 8) {
> > +             if (tx_data) {
> > +                     wide_dat = get_unaligned((u64 *)tx_data);
> > +                     debug("  tx: %016llx \t", (unsigned long long)wide_dat);
> > +                     tx_data += 8;
> > +                     writeq(wide_dat, base + MPI_WIDE_DAT);
> > +             }
> > +
> > +             mpi_tx = FIELD_PREP(MPI_TX_CSID, cs) |
> > +                     FIELD_PREP(MPI_TX_LEAVECS, 1) |
> > +                     FIELD_PREP(MPI_TX_TXNUM, tx_data ? 8 : 0) |
> > +                     FIELD_PREP(MPI_TX_TOTNUM, 8);
> > +             writeq(mpi_tx, base + MPI_TX);
> > +
> > +             octeon_spi_wait_ready(dev);
> > +
> > +             debug("\n ");
> > +
> > +             if (rx_data) {
> > +                     wide_dat = readq(base + MPI_WIDE_DAT);
> > +                     debug("  rx: %016llx\t", (unsigned long long)wide_dat);
> > +                     *(u64 *)rx_data = wide_dat;
> > +                     rx_data += 8;
> > +             }
> > +             len -= 8;
> > +     }
> > +
> > +     debug("\n ");
> > +
> > +     /* Write and read the rest of the data */
> > +     if (tx_data) {
> > +             for (i = 0; i < len; i++) {
> > +                     debug("  tx: %02x\n", *tx_data);
> > +                     writeq(*tx_data++, base + MPI_DAT(i));
> > +             }
> > +     }
> > +
> > +     mpi_tx = FIELD_PREP(MPI_TX_CSID, cs) |
> > +             FIELD_PREP(MPI_TX_LEAVECS, !(flags & SPI_XFER_END)) |
> > +             FIELD_PREP(MPI_TX_TXNUM, tx_data ? len : 0) |
> > +             FIELD_PREP(MPI_TX_TOTNUM, len);
> > +     writeq(mpi_tx, base + MPI_TX);
> > +
> > +     octeon_spi_wait_ready(dev);
> > +
> > +     debug("\n ");
> > +
> > +     if (rx_data) {
> > +             for (i = 0; i < len; i++) {
> > +                     *rx_data = readq(base + MPI_DAT(i)) & 0xff;
> > +                     debug("  rx: %02x\n", *rx_data);
> > +                     rx_data++;
> > +             }
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int octeontx2_spi_xfer(struct udevice *dev, unsigned int bitlen,
> > +                           const void *dout, void *din, unsigned long flags)
> > +{
> > +     struct udevice *bus = dev_get_parent(dev);
> > +     struct octeon_spi *priv = dev_get_priv(bus);
> > +     void *base = priv->base;
> > +     u64 mpi_xmit;
> > +     u64 mpi_cfg;
> > +     u64 wide_dat = 0;
> > +     int len = bitlen / 8;
> > +     int rem;
> > +     int i;
> > +     const u8 *tx_data = dout;
> > +     u8 *rx_data = din;
> > +     int cs = spi_chip_select(dev);
> > +
> > +     if (!OCTEON_SPI_CS_VALID(cs))
> > +             return -EINVAL;
> > +
> > +     debug("\n %s(%s, %u, %p, %p, 0x%lx), cs: %d\n",
> > +           __func__, dev->name, bitlen, dout, din, flags, cs);
> > +
> > +     mpi_cfg = octeon_spi_set_mpicfg(dev);
> > +
> > +     mpi_cfg |= MPI_CFG_TRITX | MPI_CFG_LEGACY_DIS | MPI_CFG_CS_STICKY |
> > +             MPI_CFG_TB100_EN;
> > +
> > +     mpi_cfg &= ~MPI_CFG_IOMODE;
> > +     if (flags & (SPI_TX_DUAL | SPI_RX_DUAL))
> > +             mpi_cfg |= FIELD_PREP(MPI_CFG_IOMODE, 2);
> > +     if (flags & (SPI_TX_QUAD | SPI_RX_QUAD))
> > +             mpi_cfg |= FIELD_PREP(MPI_CFG_IOMODE, 3);
> > +
> > +     if (mpi_cfg != readq(base + MPI_CFG)) {
> > +             writeq(mpi_cfg, base + MPI_CFG);
> > +             mpi_cfg = readq(base + MPI_CFG);
> > +             udelay(10);
> > +     }
> > +
> > +     debug("\n mpi_cfg upd %llx\n\n", mpi_cfg);
> > +
> > +     /* Start by writing or reading 1024 bytes at a time. */
> > +     while (len > 1024) {
> > +             if (tx_data) {
> > +                     /* 8 bytes per iteration */
> > +                     for (i = 0; i < 128; i++) {
> > +                             wide_dat = get_unaligned((u64 *)tx_data);
> > +                             debug("  tx: %016llx \t",
> > +                                   (unsigned long long)wide_dat);
> > +                             if ((i % 4) == 3)
> > +                                     debug("\n");
> > +                             tx_data += 8;
> > +                             writeq(wide_dat, base + MPI_WIDE_BUF(i));
> > +                     }
> > +             }
> > +
> > +             mpi_xmit = FIELD_PREP(MPI_XMIT_CSID, cs) | MPI_XMIT_LEAVECS |
> > +                     FIELD_PREP(MPI_XMIT_TXNUM, tx_data ? 1024 : 0) |
> > +                     FIELD_PREP(MPI_XMIT_TOTNUM, 1024);
> > +             writeq(mpi_xmit, base + MPI_XMIT);
> > +
> > +             octeon_spi_wait_ready(dev);
> > +
> > +             debug("\n ");
> > +
> > +             if (rx_data) {
> > +                     /* 8 bytes per iteration */
> > +                     for (i = 0; i < 128; i++) {
> > +                             wide_dat = readq(base + MPI_WIDE_BUF(i));
> > +                             debug("  rx: %016llx\t",
> > +                                   (unsigned long long)wide_dat);
> > +                             if ((i % 4) == 3)
> > +                                     debug("\n");
> > +                             *(u64 *)rx_data = wide_dat;
> > +                             rx_data += 8;
> > +                     }
> > +             }
> > +             len -= 1024;
> > +     }
> > +
> > +     if (tx_data) {
> > +             rem = len % 8;
> > +             /* 8 bytes per iteration */
> > +             for (i = 0; i < len / 8; i++) {
> > +                     wide_dat = get_unaligned((u64 *)tx_data);
> > +                     debug("  tx: %016llx \t",
> > +                           (unsigned long long)wide_dat);
> > +                     if ((i % 4) == 3)
> > +                             debug("\n");
> > +                     tx_data += 8;
> > +                     writeq(wide_dat, base + MPI_WIDE_BUF(i));
> > +             }
> > +             if (rem) {
> > +                     memcpy(&wide_dat, tx_data, rem);
> > +                     debug("  rtx: %016llx\t", wide_dat);
> > +                     writeq(wide_dat, base + MPI_WIDE_BUF(i));
> > +             }
> > +     }
> > +
> > +     mpi_xmit = FIELD_PREP(MPI_XMIT_CSID, cs) |
> > +             FIELD_PREP(MPI_XMIT_LEAVECS, !(flags & SPI_XFER_END)) |
> > +             FIELD_PREP(MPI_XMIT_TXNUM, tx_data ? len : 0) |
> > +             FIELD_PREP(MPI_XMIT_TOTNUM, len);
> > +     writeq(mpi_xmit, base + MPI_XMIT);
> > +
> > +     octeon_spi_wait_ready(dev);
> > +
> > +     debug("\n ");
> > +
> > +     if (rx_data) {
> > +             rem = len % 8;
> > +             /* 8 bytes per iteration */
> > +             for (i = 0; i < len / 8; i++) {
> > +                     wide_dat = readq(base + MPI_WIDE_BUF(i));
> > +                     debug("  rx: %016llx\t",
> > +                           (unsigned long long)wide_dat);
> > +                     if ((i % 4) == 3)
> > +                             debug("\n");
> > +                     *(u64 *)rx_data = wide_dat;
> > +                     rx_data += 8;
> > +             }
> > +             if (rem) {
> > +                     wide_dat = readq(base + MPI_WIDE_BUF(i));
> > +                     debug("  rrx: %016llx\t",
> > +                           (unsigned long long)wide_dat);
> > +                     memcpy(rx_data, &wide_dat, rem);
> > +                     rx_data += rem;
> > +             }
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static bool octeon_spi_supports_op(struct spi_slave *slave,
> > +                                const struct spi_mem_op *op)
> > +{
> > +     /* For now, support only below combinations
> > +      * 1-1-1
> > +      * 1-1-2 1-2-2
> > +      * 1-1-4 1-4-4
> > +      */
> > +     if (op->cmd.buswidth != 1)
> > +             return false;
> > +     return true;
> > +}
> > +
> > +static int octeon_spi_exec_op(struct spi_slave *slave,
> > +                           const struct spi_mem_op *op)
> > +{
> > +     unsigned long flags = SPI_XFER_BEGIN;
> > +     const void *tx;
> > +     void *rx;
> > +     u8 opcode, *buf;
> > +     u8 *addr;
> > +     int i, temp, ret;
> > +
> > +     if (op->cmd.buswidth != 1)
> > +             return -ENOTSUPP;
> > +
> > +     /* Send CMD */
> > +     i = 0;
> > +     opcode = op->cmd.opcode;
> > +
> > +     if (!op->data.nbytes && !op->addr.nbytes && !op->dummy.nbytes)
> > +             flags |= SPI_XFER_END;
> > +
> > +     ret = octeontx2_spi_xfer(slave->dev, 8, (void *)&opcode, NULL, flags);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     /* Send Address and dummy */
> > +     if (op->addr.nbytes) {
> > +             /* Alloc buffer for address+dummy */
> > +             buf = (u8 *)calloc(1, op->addr.nbytes + op->dummy.nbytes);
> > +             if (!buf) {
> > +                     printf("%s Out of memory\n", __func__);
> > +                     return -ENOMEM;
> > +             }
> > +             addr = (u8 *)&op->addr.val;
> > +             for (temp = 0; temp < op->addr.nbytes; temp++)
> > +                     buf[i++] = *(u8 *)(addr + op->addr.nbytes - 1 - temp);
> > +             for (temp = 0; temp < op->dummy.nbytes; temp++)
> > +                     buf[i++] = 0xff;
> > +             if (op->addr.buswidth == 2)
> > +                     flags |= SPI_RX_DUAL;
> > +             if (op->addr.buswidth == 4)
> > +                     flags |= SPI_RX_QUAD;
> > +
> > +             if (!op->data.nbytes)
> > +                     flags |= SPI_XFER_END;
> > +             ret = octeontx2_spi_xfer(slave->dev, i * 8, (void *)buf, NULL,
> > +                                      flags);
> > +             free(buf);
> > +             if (ret < 0)
> > +                     return ret;
> > +     }
> > +     if (!op->data.nbytes)
> > +             return 0;
> > +
> > +     /* Send/Receive Data */
> > +     flags |= SPI_XFER_END;
> > +     if (op->data.buswidth == 2)
> > +             flags |= SPI_RX_DUAL;
> > +     if (op->data.buswidth == 4)
> > +             flags |= SPI_RX_QUAD;
> > +
> > +     rx = (op->data.dir == SPI_MEM_DATA_IN) ? op->data.buf.in : NULL;
> > +     tx = (op->data.dir == SPI_MEM_DATA_OUT) ? op->data.buf.out : NULL;
> > +
> > +     ret = octeontx2_spi_xfer(slave->dev, (op->data.nbytes * 8), tx, rx,
> > +                              flags);
> > +     return ret;
> > +}
> > +
> > +static const struct spi_controller_mem_ops octeontx2_spi_mem_ops = {
> > +     .supports_op = octeon_spi_supports_op,
> > +     .exec_op = octeon_spi_exec_op,
> > +};
> > +
> > +/**
> > + * Set the speed of the SPI bus
> > + *
> > + * @param    bus     bus to set
> > + * @param    max_hz  maximum speed supported
> > + */
> > +static int octeon_spi_set_speed(struct udevice *bus, uint max_hz)
> > +{
> > +     struct octeon_spi *priv = dev_get_priv(bus);
> > +     ulong clk_rate;
> > +     u32 calc_hz;
> > +
> > +     if (max_hz > OCTEON_SPI_MAX_CLOCK_HZ)
> > +             max_hz = OCTEON_SPI_MAX_CLOCK_HZ;
> > +
> > +     clk_rate = clk_get_rate(&priv->clk);
> > +     if (IS_ERR_VALUE(clk_rate))
> > +             return -EINVAL;
> > +
> > +     debug("%s(%s, %u, %lu)\n", __func__, bus->name, max_hz, clk_rate);
> > +
> > +     priv->clkdiv = clk_rate / (2 * max_hz);
> > +     while (1) {
> > +             calc_hz = clk_rate / (2 * priv->clkdiv);
> > +             if (calc_hz <= max_hz)
> > +                     break;
> > +             priv->clkdiv += 1;
> > +     }
> > +
> > +     if (priv->clkdiv > 8191)
> > +             return -EINVAL;
> > +
> > +     debug("%s: clkdiv=%d\n", __func__, priv->clkdiv);
> > +
> > +     return 0;
> > +}
> > +
> > +static int octeon_spi_set_mode(struct udevice *bus, uint mode)
> > +{
> > +     /* We don't set it here */
> > +     return 0;
> > +}
> > +
> > +static const struct dm_spi_ops octeon_spi_ops = {
> > +     .claim_bus      = octeon_spi_claim_bus,
> > +     .release_bus    = octeon_spi_release_bus,
> > +     .set_speed      = octeon_spi_set_speed,
> > +     .set_mode       = octeon_spi_set_mode,
> > +     .xfer           = octeon_spi_xfer,
> > +};
> > +
> > +static const struct dm_spi_ops octeontx2_spi_ops = {
> > +     .claim_bus      = octeon_spi_claim_bus,
> > +     .release_bus    = octeon_spi_release_bus,
> > +     .set_speed      = octeon_spi_set_speed,
> > +     .set_mode       = octeon_spi_set_mode,
> > +     .xfer           = octeontx2_spi_xfer,
> > +     .mem_ops        = &octeontx2_spi_mem_ops,
> > +};
> > +
> > +static int octeon_spi_probe(struct udevice *dev)
> > +{
> > +     struct octeon_spi *priv = dev_get_priv(dev);
> > +     const struct octeon_spi_data *data;
> > +     int ret;
> > +
> > +     data = (const struct octeon_spi_data *)dev_get_driver_data(dev);
> > +     if (data->probe == PROBE_PCI) {
> > +             pci_dev_t bdf = dm_pci_get_bdf(dev);
> > +
> > +             debug("SPI PCI device: %x\n", bdf);
> > +             priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
> > +                                         PCI_REGION_MEM);
> > +     } else {
> > +             priv->base = dev_remap_addr(dev);
> > +     }
> > +
> > +     priv->base += data->reg_offs;
> > +
> > +     /* Octeon TX2 needs a different ops struct */
> > +     if (device_is_compatible(dev, "cavium,thunderx-spi")) {
> > +             /*
> > +              * "ops" is const and can't be written directly. So we need
> > +              * to write the Octeon TX2 ops value using this trick
> > +              */
> > +             writeq((u64)&octeontx2_spi_ops, (void *)&dev->driver->ops);
> > +     }
>
> can't you simply add a xfer() function pointer to "struct
> octeon_spi_data" and assign the according xfer function to it? Then
> in octeon_spi_xfer() you can simply call that function pointer. With
> this you only need one instance of "struct dm_spi_ops" and don't need
> this ugly hack ;) Maybe you can add some common code to
> octeon_spi_xfer() itself and reduce the size of the SoC specific xfer
> functions.

This seems to be a valid point.

Here are my comments.

1. Have common ops structure (w/o mem_ops)
2. In common xfer, identify compatible or flag via driver_data and
call specific xfer
3. For mem_ops it is possible to assign them in the probe, if I'm not
wrong like this.
 static int octeon_spi_probe(struct udevice *dev)
 {
     struct dm_spi_ops *ops = spi_get_ops(dev);

    check if new compatible or flag
                     ops->mem_ops = &octeontx2_spi_mem_ops;
}

Jagan.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon
  2020-07-30  7:50     ` Jagan Teki
@ 2020-07-30  7:53       ` Stefan Roese
  0 siblings, 0 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-30  7:53 UTC (permalink / raw)
  To: u-boot

Hi Jagan,

On 30.07.20 09:50, Jagan Teki wrote:
> On Fri, Jul 24, 2020 at 7:26 PM Daniel Schwierzeck
> <daniel.schwierzeck@gmail.com> wrote:
>>
>> Am Donnerstag, den 23.07.2020, 12:17 +0200 schrieb Stefan Roese:
>>> From: Suneel Garapati <sgarapati@marvell.com>
>>>
>>> Adds support for SPI controllers found on Octeon II/III and Octeon TX
>>> TX2 SoC platforms.
>>>
>>> Signed-off-by: Aaron Williams <awilliams@marvell.com>
>>> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
>>> Signed-off-by: Stefan Roese <sr@denx.de>
>>> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
>>> Cc: Aaron Williams <awilliams@marvell.com>
>>> Cc: Chandrakala Chavva <cchavva@marvell.com>
>>> Cc: Jagan Teki <jagan@amarulasolutions.com>
>>>
>>> ---
>>>
>>> Changes in v2:
>>> - Newly added to this series
>>> - Removed inclusion of "common.h"
>>> - Added "depends on DM_PCI" to Kconfig
>>> - Tested on MIPS Octeon and ARM Octeon TX2
>>> - Fixed issues with Octeon TX2 registration. Now only one driver is
>>>    registered and the "ops" is overwritten in the Octeon TX2 case.
>>> - Use dev_get_driver_data() to get the driver data struct
>>> - Removed "struct pci_device_id" definition and U_BOOT_PCI_DEVICE()
>>>    as its not needed for the PCI based probing on Octeon TX2
>>>
>>>   drivers/spi/Kconfig      |   8 +
>>>   drivers/spi/Makefile     |   1 +
>>>   drivers/spi/octeon_spi.c | 647 +++++++++++++++++++++++++++++++++++++++
>>>   3 files changed, 656 insertions(+)
>>>   create mode 100644 drivers/spi/octeon_spi.c
>>>
>>> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
>>> index 30d808d7bb..3fc2d0674a 100644
>>> --- a/drivers/spi/Kconfig
>>> +++ b/drivers/spi/Kconfig
>>> @@ -240,6 +240,14 @@ config NXP_FSPI
>>>          Enable the NXP FlexSPI (FSPI) driver. This driver can be used to
>>>          access the SPI NOR flash on platforms embedding this NXP IP core.
>>>
>>> +config OCTEON_SPI
>>> +     bool "Octeon SPI driver"
>>> +     depends on DM_PCI && (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2)
>>> +     help
>>> +       Enable the Octeon SPI driver. This driver can be used to
>>> +       access the SPI NOR flash on Octeon II/III and OcteonTX/TX2
>>> +       SoC platforms.
>>> +
>>>   config OMAP3_SPI
>>>        bool "McSPI driver for OMAP"
>>>        help
>>> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
>>> index 4e7461771f..b5c9ff1af8 100644
>>> --- a/drivers/spi/Makefile
>>> +++ b/drivers/spi/Makefile
>>> @@ -43,6 +43,7 @@ obj-$(CONFIG_MXC_SPI) += mxc_spi.o
>>>   obj-$(CONFIG_MXS_SPI) += mxs_spi.o
>>>   obj-$(CONFIG_NXP_FSPI) += nxp_fspi.o
>>>   obj-$(CONFIG_ATCSPI200_SPI) += atcspi200_spi.o
>>> +obj-$(CONFIG_OCTEON_SPI) += octeon_spi.o
>>>   obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
>>>   obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
>>>   obj-$(CONFIG_PL022_SPI) += pl022_spi.o
>>> diff --git a/drivers/spi/octeon_spi.c b/drivers/spi/octeon_spi.c
>>> new file mode 100644
>>> index 0000000000..2fb39e444c
>>> --- /dev/null
>>> +++ b/drivers/spi/octeon_spi.c
>>> @@ -0,0 +1,647 @@
>>> +// SPDX-License-Identifier:    GPL-2.0
>>> +/*
>>> + * Copyright (C) 2018 Marvell International Ltd.
>>> + *
>>> + * https://spdx.org/licenses
>>> + */
>>> +
>>> +#include <clk.h>
>>> +#include <dm.h>
>>> +#include <malloc.h>
>>> +#include <spi.h>
>>> +#include <spi-mem.h>
>>> +#include <watchdog.h>
>>> +#include <asm/io.h>
>>> +#include <asm/unaligned.h>
>>> +#include <linux/bitfield.h>
>>> +#include <linux/compat.h>
>>> +#include <linux/delay.h>
>>> +
>>> +#define OCTEON_SPI_MAX_BYTES 9
>>> +#define OCTEON_SPI_MAX_CLOCK_HZ      50000000
>>> +
>>> +#define OCTEON_SPI_NUM_CS    4
>>> +
>>> +#define OCTEON_SPI_CS_VALID(cs)      ((cs) < OCTEON_SPI_NUM_CS)
>>> +
>>> +#define MPI_CFG                      0x0000
>>> +#define MPI_STS                      0x0008
>>> +#define MPI_TX                       0x0010
>>> +#define MPI_XMIT             0x0018
>>> +#define MPI_WIDE_DAT         0x0040
>>> +#define MPI_IO_CTL           0x0048
>>> +#define MPI_DAT(X)           (0x0080 + ((X) << 3))
>>> +#define MPI_WIDE_BUF(X)              (0x0800 + ((X) << 3))
>>> +#define MPI_CYA_CFG          0x1000
>>> +#define MPI_CLKEN            0x1080
>>> +
>>> +#define MPI_CFG_ENABLE               BIT_ULL(0)
>>> +#define MPI_CFG_IDLELO               BIT_ULL(1)
>>> +#define MPI_CFG_CLK_CONT     BIT_ULL(2)
>>> +#define MPI_CFG_WIREOR               BIT_ULL(3)
>>> +#define MPI_CFG_LSBFIRST     BIT_ULL(4)
>>> +#define MPI_CFG_CS_STICKY    BIT_ULL(5)
>>> +#define MPI_CFG_CSHI         BIT_ULL(7)
>>> +#define MPI_CFG_IDLECLKS     GENMASK_ULL(9, 8)
>>> +#define MPI_CFG_TRITX                BIT_ULL(10)
>>> +#define MPI_CFG_CSLATE               BIT_ULL(11)
>>> +#define MPI_CFG_CSENA0               BIT_ULL(12)
>>> +#define MPI_CFG_CSENA1               BIT_ULL(13)
>>> +#define MPI_CFG_CSENA2               BIT_ULL(14)
>>> +#define MPI_CFG_CSENA3               BIT_ULL(15)
>>> +#define MPI_CFG_CLKDIV               GENMASK_ULL(28, 16)
>>> +#define MPI_CFG_LEGACY_DIS   BIT_ULL(31)
>>> +#define MPI_CFG_IOMODE               GENMASK_ULL(35, 34)
>>> +#define MPI_CFG_TB100_EN     BIT_ULL(49)
>>> +
>>> +#define MPI_DAT_DATA         GENMASK_ULL(7, 0)
>>> +
>>> +#define MPI_STS_BUSY         BIT_ULL(0)
>>> +#define MPI_STS_MPI_INTR     BIT_ULL(1)
>>> +#define MPI_STS_RXNUM                GENMASK_ULL(12, 8)
>>> +
>>> +#define MPI_TX_TOTNUM                GENMASK_ULL(4, 0)
>>> +#define MPI_TX_TXNUM         GENMASK_ULL(12, 8)
>>> +#define MPI_TX_LEAVECS               BIT_ULL(16)
>>> +#define MPI_TX_CSID          GENMASK_ULL(21, 20)
>>> +
>>> +#define MPI_XMIT_TOTNUM              GENMASK_ULL(10, 0)
>>> +#define MPI_XMIT_TXNUM               GENMASK_ULL(30, 20)
>>> +#define MPI_XMIT_BUF_SEL     BIT_ULL(59)
>>> +#define MPI_XMIT_LEAVECS     BIT_ULL(60)
>>> +#define MPI_XMIT_CSID                GENMASK_ULL(62, 61)
>>> +
>>> +enum {
>>> +     PROBE_PCI = 0,          /* PCI based probing */
>>> +     PROBE_DT,               /* DT based probing */
>>> +};
>>> +
>>> +/* Used on Octeon TX2 */
>>> +void board_acquire_flash_arb(bool acquire);
>>> +
>>> +struct octeon_spi_data {
>>> +     int probe;
>>> +     u32 reg_offs;
>>> +};
>>> +
>>> +/* Local driver data structure */
>>> +struct octeon_spi {
>>> +     void __iomem *base;     /* Register base address */
>>> +     struct clk clk;
>>> +     u32 clkdiv;             /* Clock divisor for device speed */
>>> +};
>>> +
>>> +static u64 octeon_spi_set_mpicfg(struct udevice *dev)
>>> +{
>>> +     struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
>>> +     struct udevice *bus = dev_get_parent(dev);
>>> +     struct octeon_spi *priv = dev_get_priv(bus);
>>> +     u64 mpi_cfg;
>>> +     uint max_speed = slave->max_hz;
>>> +     bool cpha, cpol;
>>> +
>>> +     if (!max_speed)
>>> +             max_speed = 12500000;
>>> +     if (max_speed > OCTEON_SPI_MAX_CLOCK_HZ)
>>> +             max_speed = OCTEON_SPI_MAX_CLOCK_HZ;
>>> +
>>> +     debug("\n slave params %d %d %d\n", slave->cs,
>>> +           slave->max_hz, slave->mode);
>>> +     cpha = !!(slave->mode & SPI_CPHA);
>>> +     cpol = !!(slave->mode & SPI_CPOL);
>>> +
>>> +     mpi_cfg = FIELD_PREP(MPI_CFG_CLKDIV, priv->clkdiv & 0x1fff) |
>>> +             FIELD_PREP(MPI_CFG_CSHI, !!(slave->mode & SPI_CS_HIGH)) |
>>> +             FIELD_PREP(MPI_CFG_LSBFIRST, !!(slave->mode & SPI_LSB_FIRST)) |
>>> +             FIELD_PREP(MPI_CFG_WIREOR, !!(slave->mode & SPI_3WIRE)) |
>>> +             FIELD_PREP(MPI_CFG_IDLELO, cpha != cpol) |
>>> +             FIELD_PREP(MPI_CFG_CSLATE, cpha) |
>>> +             MPI_CFG_CSENA0 | MPI_CFG_CSENA1 |
>>> +             MPI_CFG_CSENA2 | MPI_CFG_CSENA1 |
>>> +             MPI_CFG_ENABLE;
>>> +
>>> +     debug("\n mpi_cfg %llx\n", mpi_cfg);
>>> +     return mpi_cfg;
>>> +}
>>> +
>>> +/**
>>> + * Wait until the SPI bus is ready
>>> + *
>>> + * @param    dev     SPI device to wait for
>>> + */
>>> +static void octeon_spi_wait_ready(struct udevice *dev)
>>> +{
>>> +     struct udevice *bus = dev_get_parent(dev);
>>> +     struct octeon_spi *priv = dev_get_priv(bus);
>>> +     void *base = priv->base;
>>> +     u64 mpi_sts;
>>> +
>>> +     do {
>>> +             mpi_sts = readq(base + MPI_STS);
>>> +             WATCHDOG_RESET();
>>> +     } while (mpi_sts & MPI_STS_BUSY);
>>> +
>>> +     debug("%s(%s)\n", __func__, dev->name);
>>> +}
>>> +
>>> +/**
>>> + * Claim the bus for a slave device
>>> + *
>>> + * @param    dev     SPI bus
>>> + *
>>> + * @return   0 for success, -EINVAL if chip select is invalid
>>> + */
>>> +static int octeon_spi_claim_bus(struct udevice *dev)
>>> +{
>>> +     struct udevice *bus = dev_get_parent(dev);
>>> +     struct octeon_spi *priv = dev_get_priv(bus);
>>> +     void *base = priv->base;
>>> +     u64 mpi_cfg;
>>> +
>>> +     debug("\n\n%s(%s)\n", __func__, dev->name);
>>> +     if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
>>> +             return -EINVAL;
>>> +
>>> +     if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
>>> +             board_acquire_flash_arb(true);
>>> +
>>> +     mpi_cfg = readq(base + MPI_CFG);
>>> +     mpi_cfg &= ~MPI_CFG_TRITX;
>>> +     mpi_cfg |= MPI_CFG_ENABLE;
>>> +     writeq(mpi_cfg, base + MPI_CFG);
>>> +     mpi_cfg = readq(base + MPI_CFG);
>>> +     udelay(5);      /** Wait for bus to settle */
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +/**
>>> + * Release the bus to a slave device
>>> + *
>>> + * @param    dev     SPI bus
>>> + *
>>> + * @return   0 for success, -EINVAL if chip select is invalid
>>> + */
>>> +static int octeon_spi_release_bus(struct udevice *dev)
>>> +{
>>> +     struct udevice *bus = dev_get_parent(dev);
>>> +     struct octeon_spi *priv = dev_get_priv(bus);
>>> +     void *base = priv->base;
>>> +     u64 mpi_cfg;
>>> +
>>> +     debug("%s(%s)\n\n", __func__, dev->name);
>>> +     if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
>>> +             return -EINVAL;
>>> +
>>> +     if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
>>> +             board_acquire_flash_arb(false);
>>> +
>>> +     mpi_cfg = readq(base + MPI_CFG);
>>> +     mpi_cfg &= ~MPI_CFG_ENABLE;
>>> +     writeq(mpi_cfg, base + MPI_CFG);
>>> +     mpi_cfg = readq(base + MPI_CFG);
>>> +     udelay(1);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int octeon_spi_xfer(struct udevice *dev, unsigned int bitlen,
>>> +                        const void *dout, void *din, unsigned long flags)
>>> +{
>>> +     struct udevice *bus = dev_get_parent(dev);
>>> +     struct octeon_spi *priv = dev_get_priv(bus);
>>> +     void *base = priv->base;
>>> +     u64 mpi_tx;
>>> +     u64 mpi_cfg;
>>> +     u64 wide_dat = 0;
>>> +     int len = bitlen / 8;
>>> +     int i;
>>> +     const u8 *tx_data = dout;
>>> +     u8 *rx_data = din;
>>> +     int cs = spi_chip_select(dev);
>>> +
>>> +     if (!OCTEON_SPI_CS_VALID(cs))
>>> +             return -EINVAL;
>>> +
>>> +     debug("\n %s(%s, %u, %p, %p, 0x%lx), cs: %d\n",
>>> +           __func__, dev->name, bitlen, dout, din, flags, cs);
>>> +
>>> +     mpi_cfg = octeon_spi_set_mpicfg(dev);
>>> +     if (mpi_cfg != readq(base + MPI_CFG)) {
>>> +             writeq(mpi_cfg, base + MPI_CFG);
>>> +             mpi_cfg = readq(base + MPI_CFG);
>>> +             udelay(10);
>>> +     }
>>> +
>>> +     debug("\n mpi_cfg upd %llx\n", mpi_cfg);
>>> +
>>> +     /*
>>> +      * Start by writing and reading 8 bytes at a time. While we can support
>>> +      * up to 10, it's easier to just use 8 with the MPI_WIDE_DAT register.
>>> +      */
>>> +     while (len > 8) {
>>> +             if (tx_data) {
>>> +                     wide_dat = get_unaligned((u64 *)tx_data);
>>> +                     debug("  tx: %016llx \t", (unsigned long long)wide_dat);
>>> +                     tx_data += 8;
>>> +                     writeq(wide_dat, base + MPI_WIDE_DAT);
>>> +             }
>>> +
>>> +             mpi_tx = FIELD_PREP(MPI_TX_CSID, cs) |
>>> +                     FIELD_PREP(MPI_TX_LEAVECS, 1) |
>>> +                     FIELD_PREP(MPI_TX_TXNUM, tx_data ? 8 : 0) |
>>> +                     FIELD_PREP(MPI_TX_TOTNUM, 8);
>>> +             writeq(mpi_tx, base + MPI_TX);
>>> +
>>> +             octeon_spi_wait_ready(dev);
>>> +
>>> +             debug("\n ");
>>> +
>>> +             if (rx_data) {
>>> +                     wide_dat = readq(base + MPI_WIDE_DAT);
>>> +                     debug("  rx: %016llx\t", (unsigned long long)wide_dat);
>>> +                     *(u64 *)rx_data = wide_dat;
>>> +                     rx_data += 8;
>>> +             }
>>> +             len -= 8;
>>> +     }
>>> +
>>> +     debug("\n ");
>>> +
>>> +     /* Write and read the rest of the data */
>>> +     if (tx_data) {
>>> +             for (i = 0; i < len; i++) {
>>> +                     debug("  tx: %02x\n", *tx_data);
>>> +                     writeq(*tx_data++, base + MPI_DAT(i));
>>> +             }
>>> +     }
>>> +
>>> +     mpi_tx = FIELD_PREP(MPI_TX_CSID, cs) |
>>> +             FIELD_PREP(MPI_TX_LEAVECS, !(flags & SPI_XFER_END)) |
>>> +             FIELD_PREP(MPI_TX_TXNUM, tx_data ? len : 0) |
>>> +             FIELD_PREP(MPI_TX_TOTNUM, len);
>>> +     writeq(mpi_tx, base + MPI_TX);
>>> +
>>> +     octeon_spi_wait_ready(dev);
>>> +
>>> +     debug("\n ");
>>> +
>>> +     if (rx_data) {
>>> +             for (i = 0; i < len; i++) {
>>> +                     *rx_data = readq(base + MPI_DAT(i)) & 0xff;
>>> +                     debug("  rx: %02x\n", *rx_data);
>>> +                     rx_data++;
>>> +             }
>>> +     }
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int octeontx2_spi_xfer(struct udevice *dev, unsigned int bitlen,
>>> +                           const void *dout, void *din, unsigned long flags)
>>> +{
>>> +     struct udevice *bus = dev_get_parent(dev);
>>> +     struct octeon_spi *priv = dev_get_priv(bus);
>>> +     void *base = priv->base;
>>> +     u64 mpi_xmit;
>>> +     u64 mpi_cfg;
>>> +     u64 wide_dat = 0;
>>> +     int len = bitlen / 8;
>>> +     int rem;
>>> +     int i;
>>> +     const u8 *tx_data = dout;
>>> +     u8 *rx_data = din;
>>> +     int cs = spi_chip_select(dev);
>>> +
>>> +     if (!OCTEON_SPI_CS_VALID(cs))
>>> +             return -EINVAL;
>>> +
>>> +     debug("\n %s(%s, %u, %p, %p, 0x%lx), cs: %d\n",
>>> +           __func__, dev->name, bitlen, dout, din, flags, cs);
>>> +
>>> +     mpi_cfg = octeon_spi_set_mpicfg(dev);
>>> +
>>> +     mpi_cfg |= MPI_CFG_TRITX | MPI_CFG_LEGACY_DIS | MPI_CFG_CS_STICKY |
>>> +             MPI_CFG_TB100_EN;
>>> +
>>> +     mpi_cfg &= ~MPI_CFG_IOMODE;
>>> +     if (flags & (SPI_TX_DUAL | SPI_RX_DUAL))
>>> +             mpi_cfg |= FIELD_PREP(MPI_CFG_IOMODE, 2);
>>> +     if (flags & (SPI_TX_QUAD | SPI_RX_QUAD))
>>> +             mpi_cfg |= FIELD_PREP(MPI_CFG_IOMODE, 3);
>>> +
>>> +     if (mpi_cfg != readq(base + MPI_CFG)) {
>>> +             writeq(mpi_cfg, base + MPI_CFG);
>>> +             mpi_cfg = readq(base + MPI_CFG);
>>> +             udelay(10);
>>> +     }
>>> +
>>> +     debug("\n mpi_cfg upd %llx\n\n", mpi_cfg);
>>> +
>>> +     /* Start by writing or reading 1024 bytes at a time. */
>>> +     while (len > 1024) {
>>> +             if (tx_data) {
>>> +                     /* 8 bytes per iteration */
>>> +                     for (i = 0; i < 128; i++) {
>>> +                             wide_dat = get_unaligned((u64 *)tx_data);
>>> +                             debug("  tx: %016llx \t",
>>> +                                   (unsigned long long)wide_dat);
>>> +                             if ((i % 4) == 3)
>>> +                                     debug("\n");
>>> +                             tx_data += 8;
>>> +                             writeq(wide_dat, base + MPI_WIDE_BUF(i));
>>> +                     }
>>> +             }
>>> +
>>> +             mpi_xmit = FIELD_PREP(MPI_XMIT_CSID, cs) | MPI_XMIT_LEAVECS |
>>> +                     FIELD_PREP(MPI_XMIT_TXNUM, tx_data ? 1024 : 0) |
>>> +                     FIELD_PREP(MPI_XMIT_TOTNUM, 1024);
>>> +             writeq(mpi_xmit, base + MPI_XMIT);
>>> +
>>> +             octeon_spi_wait_ready(dev);
>>> +
>>> +             debug("\n ");
>>> +
>>> +             if (rx_data) {
>>> +                     /* 8 bytes per iteration */
>>> +                     for (i = 0; i < 128; i++) {
>>> +                             wide_dat = readq(base + MPI_WIDE_BUF(i));
>>> +                             debug("  rx: %016llx\t",
>>> +                                   (unsigned long long)wide_dat);
>>> +                             if ((i % 4) == 3)
>>> +                                     debug("\n");
>>> +                             *(u64 *)rx_data = wide_dat;
>>> +                             rx_data += 8;
>>> +                     }
>>> +             }
>>> +             len -= 1024;
>>> +     }
>>> +
>>> +     if (tx_data) {
>>> +             rem = len % 8;
>>> +             /* 8 bytes per iteration */
>>> +             for (i = 0; i < len / 8; i++) {
>>> +                     wide_dat = get_unaligned((u64 *)tx_data);
>>> +                     debug("  tx: %016llx \t",
>>> +                           (unsigned long long)wide_dat);
>>> +                     if ((i % 4) == 3)
>>> +                             debug("\n");
>>> +                     tx_data += 8;
>>> +                     writeq(wide_dat, base + MPI_WIDE_BUF(i));
>>> +             }
>>> +             if (rem) {
>>> +                     memcpy(&wide_dat, tx_data, rem);
>>> +                     debug("  rtx: %016llx\t", wide_dat);
>>> +                     writeq(wide_dat, base + MPI_WIDE_BUF(i));
>>> +             }
>>> +     }
>>> +
>>> +     mpi_xmit = FIELD_PREP(MPI_XMIT_CSID, cs) |
>>> +             FIELD_PREP(MPI_XMIT_LEAVECS, !(flags & SPI_XFER_END)) |
>>> +             FIELD_PREP(MPI_XMIT_TXNUM, tx_data ? len : 0) |
>>> +             FIELD_PREP(MPI_XMIT_TOTNUM, len);
>>> +     writeq(mpi_xmit, base + MPI_XMIT);
>>> +
>>> +     octeon_spi_wait_ready(dev);
>>> +
>>> +     debug("\n ");
>>> +
>>> +     if (rx_data) {
>>> +             rem = len % 8;
>>> +             /* 8 bytes per iteration */
>>> +             for (i = 0; i < len / 8; i++) {
>>> +                     wide_dat = readq(base + MPI_WIDE_BUF(i));
>>> +                     debug("  rx: %016llx\t",
>>> +                           (unsigned long long)wide_dat);
>>> +                     if ((i % 4) == 3)
>>> +                             debug("\n");
>>> +                     *(u64 *)rx_data = wide_dat;
>>> +                     rx_data += 8;
>>> +             }
>>> +             if (rem) {
>>> +                     wide_dat = readq(base + MPI_WIDE_BUF(i));
>>> +                     debug("  rrx: %016llx\t",
>>> +                           (unsigned long long)wide_dat);
>>> +                     memcpy(rx_data, &wide_dat, rem);
>>> +                     rx_data += rem;
>>> +             }
>>> +     }
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static bool octeon_spi_supports_op(struct spi_slave *slave,
>>> +                                const struct spi_mem_op *op)
>>> +{
>>> +     /* For now, support only below combinations
>>> +      * 1-1-1
>>> +      * 1-1-2 1-2-2
>>> +      * 1-1-4 1-4-4
>>> +      */
>>> +     if (op->cmd.buswidth != 1)
>>> +             return false;
>>> +     return true;
>>> +}
>>> +
>>> +static int octeon_spi_exec_op(struct spi_slave *slave,
>>> +                           const struct spi_mem_op *op)
>>> +{
>>> +     unsigned long flags = SPI_XFER_BEGIN;
>>> +     const void *tx;
>>> +     void *rx;
>>> +     u8 opcode, *buf;
>>> +     u8 *addr;
>>> +     int i, temp, ret;
>>> +
>>> +     if (op->cmd.buswidth != 1)
>>> +             return -ENOTSUPP;
>>> +
>>> +     /* Send CMD */
>>> +     i = 0;
>>> +     opcode = op->cmd.opcode;
>>> +
>>> +     if (!op->data.nbytes && !op->addr.nbytes && !op->dummy.nbytes)
>>> +             flags |= SPI_XFER_END;
>>> +
>>> +     ret = octeontx2_spi_xfer(slave->dev, 8, (void *)&opcode, NULL, flags);
>>> +     if (ret < 0)
>>> +             return ret;
>>> +
>>> +     /* Send Address and dummy */
>>> +     if (op->addr.nbytes) {
>>> +             /* Alloc buffer for address+dummy */
>>> +             buf = (u8 *)calloc(1, op->addr.nbytes + op->dummy.nbytes);
>>> +             if (!buf) {
>>> +                     printf("%s Out of memory\n", __func__);
>>> +                     return -ENOMEM;
>>> +             }
>>> +             addr = (u8 *)&op->addr.val;
>>> +             for (temp = 0; temp < op->addr.nbytes; temp++)
>>> +                     buf[i++] = *(u8 *)(addr + op->addr.nbytes - 1 - temp);
>>> +             for (temp = 0; temp < op->dummy.nbytes; temp++)
>>> +                     buf[i++] = 0xff;
>>> +             if (op->addr.buswidth == 2)
>>> +                     flags |= SPI_RX_DUAL;
>>> +             if (op->addr.buswidth == 4)
>>> +                     flags |= SPI_RX_QUAD;
>>> +
>>> +             if (!op->data.nbytes)
>>> +                     flags |= SPI_XFER_END;
>>> +             ret = octeontx2_spi_xfer(slave->dev, i * 8, (void *)buf, NULL,
>>> +                                      flags);
>>> +             free(buf);
>>> +             if (ret < 0)
>>> +                     return ret;
>>> +     }
>>> +     if (!op->data.nbytes)
>>> +             return 0;
>>> +
>>> +     /* Send/Receive Data */
>>> +     flags |= SPI_XFER_END;
>>> +     if (op->data.buswidth == 2)
>>> +             flags |= SPI_RX_DUAL;
>>> +     if (op->data.buswidth == 4)
>>> +             flags |= SPI_RX_QUAD;
>>> +
>>> +     rx = (op->data.dir == SPI_MEM_DATA_IN) ? op->data.buf.in : NULL;
>>> +     tx = (op->data.dir == SPI_MEM_DATA_OUT) ? op->data.buf.out : NULL;
>>> +
>>> +     ret = octeontx2_spi_xfer(slave->dev, (op->data.nbytes * 8), tx, rx,
>>> +                              flags);
>>> +     return ret;
>>> +}
>>> +
>>> +static const struct spi_controller_mem_ops octeontx2_spi_mem_ops = {
>>> +     .supports_op = octeon_spi_supports_op,
>>> +     .exec_op = octeon_spi_exec_op,
>>> +};
>>> +
>>> +/**
>>> + * Set the speed of the SPI bus
>>> + *
>>> + * @param    bus     bus to set
>>> + * @param    max_hz  maximum speed supported
>>> + */
>>> +static int octeon_spi_set_speed(struct udevice *bus, uint max_hz)
>>> +{
>>> +     struct octeon_spi *priv = dev_get_priv(bus);
>>> +     ulong clk_rate;
>>> +     u32 calc_hz;
>>> +
>>> +     if (max_hz > OCTEON_SPI_MAX_CLOCK_HZ)
>>> +             max_hz = OCTEON_SPI_MAX_CLOCK_HZ;
>>> +
>>> +     clk_rate = clk_get_rate(&priv->clk);
>>> +     if (IS_ERR_VALUE(clk_rate))
>>> +             return -EINVAL;
>>> +
>>> +     debug("%s(%s, %u, %lu)\n", __func__, bus->name, max_hz, clk_rate);
>>> +
>>> +     priv->clkdiv = clk_rate / (2 * max_hz);
>>> +     while (1) {
>>> +             calc_hz = clk_rate / (2 * priv->clkdiv);
>>> +             if (calc_hz <= max_hz)
>>> +                     break;
>>> +             priv->clkdiv += 1;
>>> +     }
>>> +
>>> +     if (priv->clkdiv > 8191)
>>> +             return -EINVAL;
>>> +
>>> +     debug("%s: clkdiv=%d\n", __func__, priv->clkdiv);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int octeon_spi_set_mode(struct udevice *bus, uint mode)
>>> +{
>>> +     /* We don't set it here */
>>> +     return 0;
>>> +}
>>> +
>>> +static const struct dm_spi_ops octeon_spi_ops = {
>>> +     .claim_bus      = octeon_spi_claim_bus,
>>> +     .release_bus    = octeon_spi_release_bus,
>>> +     .set_speed      = octeon_spi_set_speed,
>>> +     .set_mode       = octeon_spi_set_mode,
>>> +     .xfer           = octeon_spi_xfer,
>>> +};
>>> +
>>> +static const struct dm_spi_ops octeontx2_spi_ops = {
>>> +     .claim_bus      = octeon_spi_claim_bus,
>>> +     .release_bus    = octeon_spi_release_bus,
>>> +     .set_speed      = octeon_spi_set_speed,
>>> +     .set_mode       = octeon_spi_set_mode,
>>> +     .xfer           = octeontx2_spi_xfer,
>>> +     .mem_ops        = &octeontx2_spi_mem_ops,
>>> +};
>>> +
>>> +static int octeon_spi_probe(struct udevice *dev)
>>> +{
>>> +     struct octeon_spi *priv = dev_get_priv(dev);
>>> +     const struct octeon_spi_data *data;
>>> +     int ret;
>>> +
>>> +     data = (const struct octeon_spi_data *)dev_get_driver_data(dev);
>>> +     if (data->probe == PROBE_PCI) {
>>> +             pci_dev_t bdf = dm_pci_get_bdf(dev);
>>> +
>>> +             debug("SPI PCI device: %x\n", bdf);
>>> +             priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
>>> +                                         PCI_REGION_MEM);
>>> +     } else {
>>> +             priv->base = dev_remap_addr(dev);
>>> +     }
>>> +
>>> +     priv->base += data->reg_offs;
>>> +
>>> +     /* Octeon TX2 needs a different ops struct */
>>> +     if (device_is_compatible(dev, "cavium,thunderx-spi")) {
>>> +             /*
>>> +              * "ops" is const and can't be written directly. So we need
>>> +              * to write the Octeon TX2 ops value using this trick
>>> +              */
>>> +             writeq((u64)&octeontx2_spi_ops, (void *)&dev->driver->ops);
>>> +     }
>>
>> can't you simply add a xfer() function pointer to "struct
>> octeon_spi_data" and assign the according xfer function to it? Then
>> in octeon_spi_xfer() you can simply call that function pointer. With
>> this you only need one instance of "struct dm_spi_ops" and don't need
>> this ugly hack ;) Maybe you can add some common code to
>> octeon_spi_xfer() itself and reduce the size of the SoC specific xfer
>> functions.
> 
> This seems to be a valid point.
> 
> Here are my comments.
> 
> 1. Have common ops structure (w/o mem_ops)
> 2. In common xfer, identify compatible or flag via driver_data and
> call specific xfer
> 3. For mem_ops it is possible to assign them in the probe, if I'm not
> wrong like this.
>   static int octeon_spi_probe(struct udevice *dev)
>   {
>       struct dm_spi_ops *ops = spi_get_ops(dev);
> 
>      check if new compatible or flag
>                       ops->mem_ops = &octeontx2_spi_mem_ops;
> }

Unfortunately not. "mem_ops" is also "const":

	const struct spi_controller_mem_ops *mem_ops;

So overwriting is not possible, unless I remove the "const" from the
declaration.

Thanks,
Stefan

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon
  2020-07-30  7:44   ` Jagan Teki
@ 2020-07-30  8:06     ` Stefan Roese
  0 siblings, 0 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-30  8:06 UTC (permalink / raw)
  To: u-boot

Hi Jagan,

On 30.07.20 09:44, Jagan Teki wrote:
> On Thu, Jul 23, 2020 at 3:47 PM Stefan Roese <sr@denx.de> wrote:
>>
>> From: Suneel Garapati <sgarapati@marvell.com>
>>
>> Adds support for SPI controllers found on Octeon II/III and Octeon TX
>> TX2 SoC platforms.
>>
>> Signed-off-by: Aaron Williams <awilliams@marvell.com>
>> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
>> Signed-off-by: Stefan Roese <sr@denx.de>
>> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
>> Cc: Aaron Williams <awilliams@marvell.com>
>> Cc: Chandrakala Chavva <cchavva@marvell.com>
>> Cc: Jagan Teki <jagan@amarulasolutions.com>
>>
>> ---
>>
>> Changes in v2:
>> - Newly added to this series
>> - Removed inclusion of "common.h"
>> - Added "depends on DM_PCI" to Kconfig
>> - Tested on MIPS Octeon and ARM Octeon TX2
>> - Fixed issues with Octeon TX2 registration. Now only one driver is
>>    registered and the "ops" is overwritten in the Octeon TX2 case.
>> - Use dev_get_driver_data() to get the driver data struct
>> - Removed "struct pci_device_id" definition and U_BOOT_PCI_DEVICE()
>>    as its not needed for the PCI based probing on Octeon TX2
>>
>>   drivers/spi/Kconfig      |   8 +
>>   drivers/spi/Makefile     |   1 +
>>   drivers/spi/octeon_spi.c | 647 +++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 656 insertions(+)
>>   create mode 100644 drivers/spi/octeon_spi.c
>>
>> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
>> index 30d808d7bb..3fc2d0674a 100644
>> --- a/drivers/spi/Kconfig
>> +++ b/drivers/spi/Kconfig
>> @@ -240,6 +240,14 @@ config NXP_FSPI
>>            Enable the NXP FlexSPI (FSPI) driver. This driver can be used to
>>            access the SPI NOR flash on platforms embedding this NXP IP core.
>>
>> +config OCTEON_SPI
>> +       bool "Octeon SPI driver"
>> +       depends on DM_PCI && (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2)
>> +       help
>> +         Enable the Octeon SPI driver. This driver can be used to
>> +         access the SPI NOR flash on Octeon II/III and OcteonTX/TX2
>> +         SoC platforms.
>> +
>>   config OMAP3_SPI
>>          bool "McSPI driver for OMAP"
>>          help
>> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
>> index 4e7461771f..b5c9ff1af8 100644
>> --- a/drivers/spi/Makefile
>> +++ b/drivers/spi/Makefile
>> @@ -43,6 +43,7 @@ obj-$(CONFIG_MXC_SPI) += mxc_spi.o
>>   obj-$(CONFIG_MXS_SPI) += mxs_spi.o
>>   obj-$(CONFIG_NXP_FSPI) += nxp_fspi.o
>>   obj-$(CONFIG_ATCSPI200_SPI) += atcspi200_spi.o
>> +obj-$(CONFIG_OCTEON_SPI) += octeon_spi.o
>>   obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
>>   obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
>>   obj-$(CONFIG_PL022_SPI) += pl022_spi.o
>> diff --git a/drivers/spi/octeon_spi.c b/drivers/spi/octeon_spi.c
>> new file mode 100644
>> index 0000000000..2fb39e444c
>> --- /dev/null
>> +++ b/drivers/spi/octeon_spi.c
>> @@ -0,0 +1,647 @@
>> +// SPDX-License-Identifier:    GPL-2.0
>> +/*
>> + * Copyright (C) 2018 Marvell International Ltd.
>> + *
>> + * https://spdx.org/licenses
>> + */
>> +
>> +#include <clk.h>
>> +#include <dm.h>
>> +#include <malloc.h>
>> +#include <spi.h>
>> +#include <spi-mem.h>
>> +#include <watchdog.h>
>> +#include <asm/io.h>
>> +#include <asm/unaligned.h>
>> +#include <linux/bitfield.h>
>> +#include <linux/compat.h>
>> +#include <linux/delay.h>
>> +
>> +#define OCTEON_SPI_MAX_BYTES   9
>> +#define OCTEON_SPI_MAX_CLOCK_HZ        50000000
>> +
>> +#define OCTEON_SPI_NUM_CS      4
>> +
>> +#define OCTEON_SPI_CS_VALID(cs)        ((cs) < OCTEON_SPI_NUM_CS)
>> +
>> +#define MPI_CFG                        0x0000
>> +#define MPI_STS                        0x0008
>> +#define MPI_TX                 0x0010
>> +#define MPI_XMIT               0x0018
>> +#define MPI_WIDE_DAT           0x0040
>> +#define MPI_IO_CTL             0x0048
>> +#define MPI_DAT(X)             (0x0080 + ((X) << 3))
>> +#define MPI_WIDE_BUF(X)                (0x0800 + ((X) << 3))
>> +#define MPI_CYA_CFG            0x1000
>> +#define MPI_CLKEN              0x1080
>> +
>> +#define MPI_CFG_ENABLE         BIT_ULL(0)
>> +#define MPI_CFG_IDLELO         BIT_ULL(1)
>> +#define MPI_CFG_CLK_CONT       BIT_ULL(2)
>> +#define MPI_CFG_WIREOR         BIT_ULL(3)
>> +#define MPI_CFG_LSBFIRST       BIT_ULL(4)
>> +#define MPI_CFG_CS_STICKY      BIT_ULL(5)
>> +#define MPI_CFG_CSHI           BIT_ULL(7)
>> +#define MPI_CFG_IDLECLKS       GENMASK_ULL(9, 8)
>> +#define MPI_CFG_TRITX          BIT_ULL(10)
>> +#define MPI_CFG_CSLATE         BIT_ULL(11)
>> +#define MPI_CFG_CSENA0         BIT_ULL(12)
>> +#define MPI_CFG_CSENA1         BIT_ULL(13)
>> +#define MPI_CFG_CSENA2         BIT_ULL(14)
>> +#define MPI_CFG_CSENA3         BIT_ULL(15)
>> +#define MPI_CFG_CLKDIV         GENMASK_ULL(28, 16)
>> +#define MPI_CFG_LEGACY_DIS     BIT_ULL(31)
>> +#define MPI_CFG_IOMODE         GENMASK_ULL(35, 34)
>> +#define MPI_CFG_TB100_EN       BIT_ULL(49)
>> +
>> +#define MPI_DAT_DATA           GENMASK_ULL(7, 0)
>> +
>> +#define MPI_STS_BUSY           BIT_ULL(0)
>> +#define MPI_STS_MPI_INTR       BIT_ULL(1)
>> +#define MPI_STS_RXNUM          GENMASK_ULL(12, 8)
>> +
>> +#define MPI_TX_TOTNUM          GENMASK_ULL(4, 0)
>> +#define MPI_TX_TXNUM           GENMASK_ULL(12, 8)
>> +#define MPI_TX_LEAVECS         BIT_ULL(16)
>> +#define MPI_TX_CSID            GENMASK_ULL(21, 20)
>> +
>> +#define MPI_XMIT_TOTNUM                GENMASK_ULL(10, 0)
>> +#define MPI_XMIT_TXNUM         GENMASK_ULL(30, 20)
>> +#define MPI_XMIT_BUF_SEL       BIT_ULL(59)
>> +#define MPI_XMIT_LEAVECS       BIT_ULL(60)
>> +#define MPI_XMIT_CSID          GENMASK_ULL(62, 61)
>> +
>> +enum {
>> +       PROBE_PCI = 0,          /* PCI based probing */
>> +       PROBE_DT,               /* DT based probing */
>> +};
>> +
>> +/* Used on Octeon TX2 */
>> +void board_acquire_flash_arb(bool acquire);
>> +
>> +struct octeon_spi_data {
>> +       int probe;
>> +       u32 reg_offs;
>> +};
>> +
>> +/* Local driver data structure */
>> +struct octeon_spi {
>> +       void __iomem *base;     /* Register base address */
>> +       struct clk clk;
>> +       u32 clkdiv;             /* Clock divisor for device speed */
>> +};
>> +
>> +static u64 octeon_spi_set_mpicfg(struct udevice *dev)
>> +{
>> +       struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
>> +       struct udevice *bus = dev_get_parent(dev);
>> +       struct octeon_spi *priv = dev_get_priv(bus);
>> +       u64 mpi_cfg;
>> +       uint max_speed = slave->max_hz;
>> +       bool cpha, cpol;
>> +
>> +       if (!max_speed)
>> +               max_speed = 12500000;
>> +       if (max_speed > OCTEON_SPI_MAX_CLOCK_HZ)
>> +               max_speed = OCTEON_SPI_MAX_CLOCK_HZ;
>> +
>> +       debug("\n slave params %d %d %d\n", slave->cs,
>> +             slave->max_hz, slave->mode);
>> +       cpha = !!(slave->mode & SPI_CPHA);
>> +       cpol = !!(slave->mode & SPI_CPOL);
>> +
>> +       mpi_cfg = FIELD_PREP(MPI_CFG_CLKDIV, priv->clkdiv & 0x1fff) |
>> +               FIELD_PREP(MPI_CFG_CSHI, !!(slave->mode & SPI_CS_HIGH)) |
>> +               FIELD_PREP(MPI_CFG_LSBFIRST, !!(slave->mode & SPI_LSB_FIRST)) |
>> +               FIELD_PREP(MPI_CFG_WIREOR, !!(slave->mode & SPI_3WIRE)) |
>> +               FIELD_PREP(MPI_CFG_IDLELO, cpha != cpol) |
>> +               FIELD_PREP(MPI_CFG_CSLATE, cpha) |
>> +               MPI_CFG_CSENA0 | MPI_CFG_CSENA1 |
>> +               MPI_CFG_CSENA2 | MPI_CFG_CSENA1 |
>> +               MPI_CFG_ENABLE;
>> +
>> +       debug("\n mpi_cfg %llx\n", mpi_cfg);
>> +       return mpi_cfg;
>> +}
>> +
>> +/**
>> + * Wait until the SPI bus is ready
>> + *
>> + * @param      dev     SPI device to wait for
>> + */
>> +static void octeon_spi_wait_ready(struct udevice *dev)
>> +{
>> +       struct udevice *bus = dev_get_parent(dev);
>> +       struct octeon_spi *priv = dev_get_priv(bus);
>> +       void *base = priv->base;
>> +       u64 mpi_sts;
>> +
>> +       do {
>> +               mpi_sts = readq(base + MPI_STS);
>> +               WATCHDOG_RESET();
>> +       } while (mpi_sts & MPI_STS_BUSY);
>> +
>> +       debug("%s(%s)\n", __func__, dev->name);
>> +}
>> +
>> +/**
>> + * Claim the bus for a slave device
>> + *
>> + * @param      dev     SPI bus
>> + *
>> + * @return     0 for success, -EINVAL if chip select is invalid
>> + */
>> +static int octeon_spi_claim_bus(struct udevice *dev)
>> +{
>> +       struct udevice *bus = dev_get_parent(dev);
>> +       struct octeon_spi *priv = dev_get_priv(bus);
>> +       void *base = priv->base;
>> +       u64 mpi_cfg;
>> +
>> +       debug("\n\n%s(%s)\n", __func__, dev->name);
>> +       if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
>> +               return -EINVAL;
>> +
>> +       if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
>> +               board_acquire_flash_arb(true);
>> +
>> +       mpi_cfg = readq(base + MPI_CFG);
>> +       mpi_cfg &= ~MPI_CFG_TRITX;
>> +       mpi_cfg |= MPI_CFG_ENABLE;
>> +       writeq(mpi_cfg, base + MPI_CFG);
>> +       mpi_cfg = readq(base + MPI_CFG);
>> +       udelay(5);      /** Wait for bus to settle */
>> +
>> +       return 0;
>> +}
>> +
>> +/**
>> + * Release the bus to a slave device
>> + *
>> + * @param      dev     SPI bus
>> + *
>> + * @return     0 for success, -EINVAL if chip select is invalid
>> + */
>> +static int octeon_spi_release_bus(struct udevice *dev)
>> +{
>> +       struct udevice *bus = dev_get_parent(dev);
>> +       struct octeon_spi *priv = dev_get_priv(bus);
>> +       void *base = priv->base;
>> +       u64 mpi_cfg;
>> +
>> +       debug("%s(%s)\n\n", __func__, dev->name);
>> +       if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
>> +               return -EINVAL;
>> +
>> +       if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
>> +               board_acquire_flash_arb(false);
> 
> Does this acquire flash depends on soc?

Yes. Only Octeon TX2 implements it - the other SoC's using this driver
(MIPS Octeon and ARM Octeon TX) do not.

> what exactly it does?

It controls the arbiter for the boot device between multiple internal
resources (SCP, MCP, AP Secure, AP Nonsecure).

> I
> believe it can go in separate uclass drivers and spi drivers get that
> functionality like other drivers. I'm not fond of calling an
> architecture or other places code in the driver. However if it local
> spi driver then attach driver_data on respective compatible and use it
> locally.

I could move this board_acquire_flash_arb() implementation into this
SPI driver, which might make sense. Its not called from other places
AFAICT.

Thanks,
Stefan

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon
  2020-07-30  6:23       ` Stefan Roese
@ 2020-07-30 11:00         ` Daniel Schwierzeck
  2020-07-30 11:42           ` Stefan Roese
  0 siblings, 1 reply; 22+ messages in thread
From: Daniel Schwierzeck @ 2020-07-30 11:00 UTC (permalink / raw)
  To: u-boot

Am Donnerstag, den 30.07.2020, 08:23 +0200 schrieb Stefan Roese:
> Hi Daniel,
> 
> On 30.07.20 07:49, Stefan Roese wrote:
> > Hi Daniel,
> > 
> > On 24.07.20 15:56, Daniel Schwierzeck wrote:
> > > Am Donnerstag, den 23.07.2020, 12:17 +0200 schrieb Stefan Roese:
> > > > From: Suneel Garapati <sgarapati@marvell.com>
> > > > 
> > > > Adds support for SPI controllers found on Octeon II/III and Octeon TX
> > > > TX2 SoC platforms.
> > > > 
> > > > Signed-off-by: Aaron Williams <awilliams@marvell.com>
> > > > Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
> > > > Signed-off-by: Stefan Roese <sr@denx.de>
> > > > Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
> > > > Cc: Aaron Williams <awilliams@marvell.com>
> > > > Cc: Chandrakala Chavva <cchavva@marvell.com>
> > > > Cc: Jagan Teki <jagan@amarulasolutions.com>
> > 
> > <snip>
> > 
> > > > +static const struct dm_spi_ops octeon_spi_ops = {
> > > > +    .claim_bus    = octeon_spi_claim_bus,
> > > > +    .release_bus    = octeon_spi_release_bus,
> > > > +    .set_speed    = octeon_spi_set_speed,
> > > > +    .set_mode    = octeon_spi_set_mode,
> > > > +    .xfer        = octeon_spi_xfer,
> > > > +};
> > > > +
> > > > +static const struct dm_spi_ops octeontx2_spi_ops = {
> > > > +    .claim_bus    = octeon_spi_claim_bus,
> > > > +    .release_bus    = octeon_spi_release_bus,
> > > > +    .set_speed    = octeon_spi_set_speed,
> > > > +    .set_mode    = octeon_spi_set_mode,
> > > > +    .xfer        = octeontx2_spi_xfer,
> > > > +    .mem_ops    = &octeontx2_spi_mem_ops,
> > > > +};
> > > > +
> > > > +static int octeon_spi_probe(struct udevice *dev)
> > > > +{
> > > > +    struct octeon_spi *priv = dev_get_priv(dev);
> > > > +    const struct octeon_spi_data *data;
> > > > +    int ret;
> > > > +
> > > > +    data = (const struct octeon_spi_data *)dev_get_driver_data(dev);
> > > > +    if (data->probe == PROBE_PCI) {
> > > > +        pci_dev_t bdf = dm_pci_get_bdf(dev);
> > > > +
> > > > +        debug("SPI PCI device: %x\n", bdf);
> > > > +        priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
> > > > +                        PCI_REGION_MEM);
> > > > +    } else {
> > > > +        priv->base = dev_remap_addr(dev);
> > > > +    }
> > > > +
> > > > +    priv->base += data->reg_offs;
> > > > +
> > > > +    /* Octeon TX2 needs a different ops struct */
> > > > +    if (device_is_compatible(dev, "cavium,thunderx-spi")) {
> > > > +        /*
> > > > +         * "ops" is const and can't be written directly. So we need
> > > > +         * to write the Octeon TX2 ops value using this trick
> > > > +         */
> > > > +        writeq((u64)&octeontx2_spi_ops, (void *)&dev->driver->ops);
> > > > +    }
> > > 
> > > can't you simply add a xfer() function pointer to "struct
> > > octeon_spi_data" and assign the according xfer function to it? Then
> > > in octeon_spi_xfer() you can simply call that function pointer. With
> > > this you only need one instance of "struct dm_spi_ops" and don't need
> > > this ugly hack ;)
> > 
> > Unfortuantely its not that easy, as the Octeon TX2 ops struct also has
> > a " mem_ops" member, which the driver does not support for the other
> > Octeon models. I could clear this "mem_ops" member in the non Octeon
> > TX2 case, which is a bit better than the 2nd ops struct. But its still
> > not really elegent.

I had a look and the struct dm_spi_ops don't have a strict requirement
to be const. It doesn't matter for "struct driver : const void *ops" if
it points to struct dm_spi_ops linked to .data or .rodata, it can only
be assigned once.

If struct dm_spi_ops is linked to .data, you can simply re-assign some
function pointers during probe. I would suggest the following change:

@@ -82,6 +82,7 @@ void board_acquire_flash_arb(bool acquire);
 struct octeon_spi_data {
        int probe;
        u32 reg_offs;
+       bool is_octeontx;
 };
 
 /* Local driver data structure */
@@ -559,7 +560,7 @@ static int octeon_spi_set_mode(struct udevice *bus,
uint mode)
        return 0;
 }
 
-static const struct dm_spi_ops octeon_spi_ops = {
+static struct dm_spi_ops octeon_spi_ops = {
        .claim_bus      = octeon_spi_claim_bus,
        .release_bus    = octeon_spi_release_bus,
        .set_speed      = octeon_spi_set_speed,
@@ -567,15 +568,6 @@ static const struct dm_spi_ops octeon_spi_ops = {
        .xfer           = octeon_spi_xfer,
 };
 
-static const struct dm_spi_ops octeontx2_spi_ops = {
-       .claim_bus      = octeon_spi_claim_bus,
-       .release_bus    = octeon_spi_release_bus,
-       .set_speed      = octeon_spi_set_speed,
-       .set_mode       = octeon_spi_set_mode,
-       .xfer           = octeontx2_spi_xfer,
-       .mem_ops        = &octeontx2_spi_mem_ops,
-};
-
 static int octeon_spi_probe(struct udevice *dev)
 {
        struct octeon_spi *priv = dev_get_priv(dev);
@@ -596,12 +588,9 @@ static int octeon_spi_probe(struct udevice *dev)
        priv->base += data->reg_offs;
 
        /* Octeon TX2 needs a different ops struct */
-       if (device_is_compatible(dev, "cavium,thunderx-spi")) {
-               /*
-                * "ops" is const and can't be written directly. So we
need
-                * to write the Octeon TX2 ops value using this trick
-                */
-               writeq((u64)&octeontx2_spi_ops, (void *)&dev->driver-
>ops);
+       if (data->is_octeontx) {
+               octeon_spi_ops.xfer = octeontx2_spi_xfer;
+               octeon_spi_ops.mem_ops = &octeontx2_spi_mem_ops;
        }
 
        ret = clk_get_by_index(dev, 0, &priv->clk);
@@ -620,11 +609,13 @@ static int octeon_spi_probe(struct udevice *dev)
 static const struct octeon_spi_data spi_octeon_data = {
        .probe = PROBE_DT,
        .reg_offs = 0x0000,
+       .is_octeontx = false,
 };
 
 static const struct octeon_spi_data spi_octeontx_data = {
        .probe = PROBE_PCI,
        .reg_offs = 0x1000,
+       .is_octeontx = true,
 };
 
> > 
> > Or do you have some other idea on how to implement this in a "better
> > way"?
> 
> BTW: If this SPI driver is the only patch in this series, that you feel
> is not ready, then I suggest to drop this one from this patch series
> and push the remaining ones to mainline (if they have no issues of
> course).
> 

if you are okay with my suggestion you could send a v3 of just the SPI
driver. I think I'll have time tomorrow to prepare another pull
request.

-- 
- Daniel

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon
  2020-07-30 11:00         ` Daniel Schwierzeck
@ 2020-07-30 11:42           ` Stefan Roese
  0 siblings, 0 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-30 11:42 UTC (permalink / raw)
  To: u-boot

Hi Daniel,

On 30.07.20 13:00, Daniel Schwierzeck wrote:
> Am Donnerstag, den 30.07.2020, 08:23 +0200 schrieb Stefan Roese:
>> Hi Daniel,
>>
>> On 30.07.20 07:49, Stefan Roese wrote:
>>> Hi Daniel,
>>>
>>> On 24.07.20 15:56, Daniel Schwierzeck wrote:
>>>> Am Donnerstag, den 23.07.2020, 12:17 +0200 schrieb Stefan Roese:
>>>>> From: Suneel Garapati <sgarapati@marvell.com>
>>>>>
>>>>> Adds support for SPI controllers found on Octeon II/III and Octeon TX
>>>>> TX2 SoC platforms.
>>>>>
>>>>> Signed-off-by: Aaron Williams <awilliams@marvell.com>
>>>>> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
>>>>> Signed-off-by: Stefan Roese <sr@denx.de>
>>>>> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
>>>>> Cc: Aaron Williams <awilliams@marvell.com>
>>>>> Cc: Chandrakala Chavva <cchavva@marvell.com>
>>>>> Cc: Jagan Teki <jagan@amarulasolutions.com>
>>>
>>> <snip>
>>>
>>>>> +static const struct dm_spi_ops octeon_spi_ops = {
>>>>> +    .claim_bus    = octeon_spi_claim_bus,
>>>>> +    .release_bus    = octeon_spi_release_bus,
>>>>> +    .set_speed    = octeon_spi_set_speed,
>>>>> +    .set_mode    = octeon_spi_set_mode,
>>>>> +    .xfer        = octeon_spi_xfer,
>>>>> +};
>>>>> +
>>>>> +static const struct dm_spi_ops octeontx2_spi_ops = {
>>>>> +    .claim_bus    = octeon_spi_claim_bus,
>>>>> +    .release_bus    = octeon_spi_release_bus,
>>>>> +    .set_speed    = octeon_spi_set_speed,
>>>>> +    .set_mode    = octeon_spi_set_mode,
>>>>> +    .xfer        = octeontx2_spi_xfer,
>>>>> +    .mem_ops    = &octeontx2_spi_mem_ops,
>>>>> +};
>>>>> +
>>>>> +static int octeon_spi_probe(struct udevice *dev)
>>>>> +{
>>>>> +    struct octeon_spi *priv = dev_get_priv(dev);
>>>>> +    const struct octeon_spi_data *data;
>>>>> +    int ret;
>>>>> +
>>>>> +    data = (const struct octeon_spi_data *)dev_get_driver_data(dev);
>>>>> +    if (data->probe == PROBE_PCI) {
>>>>> +        pci_dev_t bdf = dm_pci_get_bdf(dev);
>>>>> +
>>>>> +        debug("SPI PCI device: %x\n", bdf);
>>>>> +        priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
>>>>> +                        PCI_REGION_MEM);
>>>>> +    } else {
>>>>> +        priv->base = dev_remap_addr(dev);
>>>>> +    }
>>>>> +
>>>>> +    priv->base += data->reg_offs;
>>>>> +
>>>>> +    /* Octeon TX2 needs a different ops struct */
>>>>> +    if (device_is_compatible(dev, "cavium,thunderx-spi")) {
>>>>> +        /*
>>>>> +         * "ops" is const and can't be written directly. So we need
>>>>> +         * to write the Octeon TX2 ops value using this trick
>>>>> +         */
>>>>> +        writeq((u64)&octeontx2_spi_ops, (void *)&dev->driver->ops);
>>>>> +    }
>>>>
>>>> can't you simply add a xfer() function pointer to "struct
>>>> octeon_spi_data" and assign the according xfer function to it? Then
>>>> in octeon_spi_xfer() you can simply call that function pointer. With
>>>> this you only need one instance of "struct dm_spi_ops" and don't need
>>>> this ugly hack ;)
>>>
>>> Unfortuantely its not that easy, as the Octeon TX2 ops struct also has
>>> a " mem_ops" member, which the driver does not support for the other
>>> Octeon models. I could clear this "mem_ops" member in the non Octeon
>>> TX2 case, which is a bit better than the 2nd ops struct. But its still
>>> not really elegent.
> 
> I had a look and the struct dm_spi_ops don't have a strict requirement
> to be const. It doesn't matter for "struct driver : const void *ops" if
> it points to struct dm_spi_ops linked to .data or .rodata, it can only
> be assigned once.
> 
> If struct dm_spi_ops is linked to .data, you can simply re-assign some
> function pointers during probe.

A, nice. Thanks for looking into this.

> I would suggest the following change:
> 
> @@ -82,6 +82,7 @@ void board_acquire_flash_arb(bool acquire);
>   struct octeon_spi_data {
>          int probe;
>          u32 reg_offs;
> +       bool is_octeontx;
>   };
>   
>   /* Local driver data structure */
> @@ -559,7 +560,7 @@ static int octeon_spi_set_mode(struct udevice *bus,
> uint mode)
>          return 0;
>   }
>   
> -static const struct dm_spi_ops octeon_spi_ops = {
> +static struct dm_spi_ops octeon_spi_ops = {
>          .claim_bus      = octeon_spi_claim_bus,
>          .release_bus    = octeon_spi_release_bus,
>          .set_speed      = octeon_spi_set_speed,
> @@ -567,15 +568,6 @@ static const struct dm_spi_ops octeon_spi_ops = {
>          .xfer           = octeon_spi_xfer,
>   };
>   
> -static const struct dm_spi_ops octeontx2_spi_ops = {
> -       .claim_bus      = octeon_spi_claim_bus,
> -       .release_bus    = octeon_spi_release_bus,
> -       .set_speed      = octeon_spi_set_speed,
> -       .set_mode       = octeon_spi_set_mode,
> -       .xfer           = octeontx2_spi_xfer,
> -       .mem_ops        = &octeontx2_spi_mem_ops,
> -};
> -
>   static int octeon_spi_probe(struct udevice *dev)
>   {
>          struct octeon_spi *priv = dev_get_priv(dev);
> @@ -596,12 +588,9 @@ static int octeon_spi_probe(struct udevice *dev)
>          priv->base += data->reg_offs;
>   
>          /* Octeon TX2 needs a different ops struct */
> -       if (device_is_compatible(dev, "cavium,thunderx-spi")) {
> -               /*
> -                * "ops" is const and can't be written directly. So we
> need
> -                * to write the Octeon TX2 ops value using this trick
> -                */
> -               writeq((u64)&octeontx2_spi_ops, (void *)&dev->driver-
>> ops);
> +       if (data->is_octeontx) {
> +               octeon_spi_ops.xfer = octeontx2_spi_xfer;
> +               octeon_spi_ops.mem_ops = &octeontx2_spi_mem_ops;
>          }
>   
>          ret = clk_get_by_index(dev, 0, &priv->clk);
> @@ -620,11 +609,13 @@ static int octeon_spi_probe(struct udevice *dev)
>   static const struct octeon_spi_data spi_octeon_data = {
>          .probe = PROBE_DT,
>          .reg_offs = 0x0000,
> +       .is_octeontx = false,
>   };
>   
>   static const struct octeon_spi_data spi_octeontx_data = {
>          .probe = PROBE_PCI,
>          .reg_offs = 0x1000,
> +       .is_octeontx = true,
>   };

This selection via driver_data does not work, as Octeon TX and Octeon
TX2 have the same compatible for probing. I've changed this to a runtime
compatible detection (TX2 only) instead and it works just fine.

>>>
>>> Or do you have some other idea on how to implement this in a "better
>>> way"?
>>
>> BTW: If this SPI driver is the only patch in this series, that you feel
>> is not ready, then I suggest to drop this one from this patch series
>> and push the remaining ones to mainline (if they have no issues of
>> course).
>>
> 
> if you are okay with my suggestion you could send a v3 of just the SPI
> driver. I think I'll have time tomorrow to prepare another pull
> request.

Great. I'll make the necessary changes and will send out v3 shortly.

Thanks,
Stefan

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 10/10] mips: octeon: Update EBB7304 defconfig
  2020-07-30 11:56 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
@ 2020-07-30 11:56 ` Stefan Roese
  0 siblings, 0 replies; 22+ messages in thread
From: Stefan Roese @ 2020-07-30 11:56 UTC (permalink / raw)
  To: u-boot

This patch enables the following options for the Octeon EBB7304 EVB:

- PCI & DM_PCI
- DM_SPI_FLASH & SPI flash device support
- SPI & Octeon SPI driver
- GPIO cmd support
- I2C cmd support

Signed-off-by: Stefan Roese <sr@denx.de>

---

(no changes since v2)

Changes in v2:
- Kconfig and defconfig changes separated into 2 patches

 configs/octeon_ebb7304_defconfig | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/configs/octeon_ebb7304_defconfig b/configs/octeon_ebb7304_defconfig
index d810b1e45f..17fdc68c03 100644
--- a/configs/octeon_ebb7304_defconfig
+++ b/configs/octeon_ebb7304_defconfig
@@ -12,6 +12,8 @@ CONFIG_ARCH_OCTEON=y
 CONFIG_DEBUG_UART=y
 CONFIG_SYS_CONSOLE_INFO_QUIET=y
 CONFIG_HUSH_PARSER=y
+CONFIG_CMD_GPIO=y
+CONFIG_CMD_I2C=y
 CONFIG_CMD_MTD=y
 CONFIG_CMD_PCI=y
 CONFIG_CMD_DHCP=y
@@ -29,10 +31,18 @@ CONFIG_CFI_FLASH=y
 CONFIG_SYS_FLASH_USE_BUFFER_WRITE=y
 CONFIG_FLASH_CFI_MTD=y
 CONFIG_SYS_FLASH_CFI=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH_ATMEL=y
+CONFIG_SPI_FLASH_SPANSION=y
+CONFIG_SPI_FLASH_STMICRO=y
 # CONFIG_NETDEVICES is not set
+CONFIG_PCI=y
+CONFIG_DM_PCI=y
 CONFIG_DEBUG_UART_SHIFT=3
 CONFIG_DEBUG_UART_ANNOUNCE=y
 CONFIG_SYS_NS16550=y
+CONFIG_SPI=y
+CONFIG_OCTEON_SPI=y
 CONFIG_SYSRESET=y
 CONFIG_SYSRESET_OCTEON=y
 CONFIG_HEXDUMP=y
-- 
2.28.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2020-07-30 11:56 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-23 10:17 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
2020-07-23 10:17 ` [PATCH v2 01/10] gpio: octeon_gpio: Add GPIO controller driver for Octeon Stefan Roese
2020-07-23 10:17 ` [PATCH v2 02/10] mips: octeon: mrvl,cn73xx.dtsi: Add GPIO DT nodes Stefan Roese
2020-07-23 10:17 ` [PATCH v2 03/10] mips: octeon: dts: Add I2C " Stefan Roese
2020-07-23 10:17 ` [PATCH v2 04/10] clk: clk_octeon: Add simple MIPS Octeon clock driver Stefan Roese
2020-07-23 10:17 ` [PATCH v2 05/10] mips: octeon: dts: Add Octeon clock driver DT nodes Stefan Roese
2020-07-23 10:17 ` [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon Stefan Roese
2020-07-24 13:56   ` Daniel Schwierzeck
2020-07-24 14:27     ` Stefan Roese
2020-07-30  5:49     ` Stefan Roese
2020-07-30  6:23       ` Stefan Roese
2020-07-30 11:00         ` Daniel Schwierzeck
2020-07-30 11:42           ` Stefan Roese
2020-07-30  7:50     ` Jagan Teki
2020-07-30  7:53       ` Stefan Roese
2020-07-30  7:44   ` Jagan Teki
2020-07-30  8:06     ` Stefan Roese
2020-07-23 10:17 ` [PATCH v2 07/10] mips: octeon: mrvl,cn73xx.dtsi: Add SPI DT node Stefan Roese
2020-07-23 10:17 ` [PATCH v2 08/10] mips: octeon: mrvl, octeon-ebb7304.dts: Add SPI flash " Stefan Roese
2020-07-23 10:17 ` [PATCH v2 09/10] mips: octeon: Update Octeon Kconfig Stefan Roese
2020-07-23 10:17 ` [PATCH v2 10/10] mips: octeon: Update EBB7304 defconfig Stefan Roese
2020-07-30 11:56 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
2020-07-30 11:56 ` [PATCH v2 10/10] mips: octeon: Update EBB7304 defconfig Stefan Roese

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.