From: Suneel Garapati <sgarap...@marvell.com>

Adds support for SPI controllers found on OcteonTX or
OcteonTX2 SoC platforms.

Signed-off-by: Aaron Williams <awilli...@marvell.com>
Signed-off-by: Suneel Garapati <sgarap...@marvell.com>
---
 drivers/spi/Kconfig        |   6 +
 drivers/spi/Makefile       |   1 +
 drivers/spi/octeontx_spi.c | 750 +++++++++++++++++++++++++++++++++++++
 3 files changed, 757 insertions(+)
 create mode 100644 drivers/spi/octeontx_spi.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b8ca2bdedd..e521f10015 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -175,6 +175,12 @@ config MVEBU_A3700_SPI
          used to access the SPI NOR flash on platforms embedding this
          Marvell IP core.
 
+config OCTEONTX_SPI
+       bool "OcteonTX SPI driver"
+       help
+         Enable the OcteonTX SPI driver. This driver can be used to
+         access the SPI NOR flash on OcteonTX or OcteonTX2 SoC platforms.
+
 config PIC32_SPI
        bool "Microchip PIC32 SPI driver"
        depends on MACH_PIC32
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index ae4f2958f8..296eb11da8 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_BCM63XX_HSSPI) += bcm63xx_hsspi.o
 obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o
 obj-$(CONFIG_BCMSTB_SPI) += bcmstb_spi.o
 obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
+obj-$(CONFIG_OCTEONTX_SPI) += octeontx_spi.o
 obj-$(CONFIG_CF_SPI) += cf_spi.o
 obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
 obj-$(CONFIG_DESIGNWARE_SPI) += designware_spi.o
diff --git a/drivers/spi/octeontx_spi.c b/drivers/spi/octeontx_spi.c
new file mode 100644
index 0000000000..1fb4989349
--- /dev/null
+++ b/drivers/spi/octeontx_spi.c
@@ -0,0 +1,750 @@
+// SPDX-License-Identifier:    GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ *
+ * https://spdx.org/licenses
+ */
+
+#include <common.h>
+#include <spi.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <dm.h>
+#include <asm/arch/clock.h>
+#include <asm/unaligned.h>
+#include <watchdog.h>
+
+#if defined(CONFIG_ARCH_OCTEONTX2)
+#include <asm/arch/board.h>
+#include <spi-mem.h>
+#define USE_TBI_CLK
+#endif
+
+#define OCTEONTX_SPI_MAX_BYTES         9
+#define OCTEONTX_SPI_MAX_CLOCK_HZ      50000000
+
+#define OCTEONTX2_TBI_CLK              100000000
+
+#define OCTEONTX_SPI_NUM_CS            4
+
+#define OCTEONTX_SPI_CS_VALID(cs)      ((cs) < OCTEONTX_SPI_NUM_CS)
+
+#define MPI_CFG                                0x1000
+#define MPI_STS                                0x1008
+#define MPI_TX                         0x1010
+#define MPI_XMIT                       0x1018
+#define MPI_WIDE_DAT                   0x1040
+#define MPI_IO_CTL                     0x1048
+#define MPI_DAT(X)                     (0x1080 + ((X) << 3))
+#define MPI_WIDE_BUF(X)                        (0x1800 + ((X) << 3))
+#define MPI_CYA_CFG                    0x2000
+#define MPI_CLKEN                      0x2080
+
+union mpi_cfg {
+       u64 u;
+       struct mpi_cfg_s {
+               /** MPI/SPI enable, 0 = pins are tristated, 1 = pins driven */
+               u64 enable      :1;
+               /**
+                * Clock idle low/clock invert
+                * 0 = SPI_CLK idles high, first transition is high-to-low.
+                *     This correspondes to SPI Block Guide options CPOL = 1,
+                *     CPHA = 0.
+                * 1 = SPI_CLK idles low, first transition is low-to-high.  This
+                *     corresponds to SPI Block Guide options CPOL = 0,
+                *     CPHA = 0.
+                */
+               u64 idlelo      :1;
+               /**
+                * Clock control.
+                * 0 = Clock idles to value given by IDLELO after completion of
+                *     MPI/SPI transaction.
+                * 1 = Clock never idles, requires SPI_CSn_L
+                *     deassertion/assertion between commands.
+                */
+               u64 clk_cont    :1;
+               /**
+                * Wire-or DO and DI.
+                * 0 = SPI_DO and SPI_DI are separate wires (SPI).  SPI_DO pin
+                *     is always driven.
+                * 1 = SPI_DO/DI is all from SPI_DO pin (MPI).  SPI_DO pin is
+                *     tristated when not transmitting.  If WIREOR = 1, SPI_DI
+                *     pin is not used by the MPI/SPI engine.
+                */
+               u64 wireor      :1;
+               /** 0 = shift MSB first, 1 = shift LSB first */
+               u64 lsbfirst    :1;
+               u64 cs_sticky   :1;     /** cs sticky bit */
+               u64 rsvd        :1;     /** Reserved */
+               /**
+                * SPI_CSn_L high.  1 = SPI_CSn_L is asserted high,
+                * 0 = SPI_CS_n asserted low.
+                */
+               u64 cshi        :1;
+               /**
+                * When set, guarantees idle coprocessor-clock cycles between
+                * commands.
+                */
+               u64 idleclks    :2;
+               /**
+                * Tristate TX.  Set to 1 to tristate SPI_DO when not
+                * transmitting.
+                */
+               u64 tritx       :1;
+               /**
+                * 0 = SPI_CSn asserts 1/2 coprocessor-clock cycle before
+                *     transaction
+                * 1 = SPI_CSn asserts coincident with transaction
+                */
+               u64 cslate      :1;
+               u64 csena0      :1;     /** cs enable 0 */
+               u64 csena1      :1;     /** cs enable 1 */
+               u64 csena2      :1;     /** cs enable 2 */
+               u64 csena3      :1;     /** cs enable 3 */
+               u64 clkdiv      :13;    /** clock divisor */
+               u64 rsvd1       :2;
+               u64 legacy_dis  :1;     /** Disable legacy mode */
+               u64 rsvd2       :2;
+               /**
+                * I/O Mode (legacy_dis must be 1):
+                *   0x0        One-lane unidirectional mode.
+                *   0x1        One-lane bidirectional mode.
+                *   0x2        Two-lane bidirectional mode.
+                *   0x3        Four-lane bidirectional mode.
+                */
+               u64 iomode      :2;
+               u64 rsvd3       :8;
+               /**
+                * Enable ESPI mode per slave.  Each bit corresponds to each
+                * of the four possible CS's.
+                * If 0, CRC hardware is disabled, turn-around time is the
+                * default for SPI and no special parsing in hardware.
+                * If 1, CRC hardware is enabled and the hardware will
+                * automatically calculate the CRC for one transaction and then
+                * apply it to the end of the transaction and then check the
+                * CRC on the response and if there is an error the
+                * MPI(0..1)_STS[CRC_ERR] bit will be set. The turn around
+                * time (TAR in the ESPI spec) is set to two cicles and parsing
+                * for special state is enabled.
+                */
+               u64 cs_espi_en  :4;
+               u64 rsvd4       :1;
+               /**
+                * SPI 100MHz clock enable.
+                *
+                * 0    Use the system clock (sclk) as the base frequency.
+                *      This provides higher granularity but may require
+                *      changing clkdiv if the system clock is changed.
+                * 1    Use a 100MHz clock as the base frequency.  This is
+                *      the reset value to enable the boot frequency to be
+                *      sclk agnostic.
+                */
+               u64 tb100_en    :1;
+               u64 rsvd5       :14;
+       } s;
+       /* struct mpi_cfg_s cn; */
+};
+
+/**
+ * Register (NCB) mpi_dat#
+ *
+ * MPI/SPI Data Registers
+ */
+union mpi_dat {
+       u64 u;
+       struct mpi_datx_s {
+               u64 data                :8;     /** Data to transmit/receive. */
+               u64 reserved_8_63       :56;
+       } s;
+       /* struct mpi_datx_s cn; */
+};
+
+/**
+ * Register (NCB) mpi_sts
+ *
+ * MPI/SPI STS Register
+ */
+union mpi_sts {
+       u64 u;
+       struct mpi_sts_s {
+               u64 busy        :1;     /** SPI engine busy */
+               u64 mpi_intr    :1;     /** Transaction done int */
+               u64 reserved_2_7:6;
+               u64 rxnum       :5;     /** ESPI number of rx bytes */
+               u64 rsvd        :6;
+               u64 crc_err     :1;     /** CRC error from ESPI */
+               u64 rsvd1       :5;
+               u64 crc         :8;     /** ESPI CRC received */
+               u64 reserved_40_63      :24;
+       } s;
+       /* struct mpi_sts_s cn; */
+};
+
+/**
+ * Register (NCB) mpi_tx
+ *
+ * MPI/SPI Transmit Register
+ */
+union mpi_tx {
+       u64 u;
+       struct mpi_tx_s {
+               u64 totnum      :5;     /** Total bytes to shift */
+               u64 rsvd        :3;
+               u64 txnum       :5;     /** Number of words to tx */
+               u64 rsvd1       :3;
+               u64 leavecs     :1;     /** Leave CS asserted */
+               u64 rsvd2       :3;
+               u64 csid        :2;     /** Which CS to assert */
+               u64 rsvd3       :42;
+       } s;
+       /* struct mpi_tx_s cn; */
+};
+
+#if !defined(CONFIG_ARCH_OCTEONTX)
+/**
+ * Register (NCB) mpi#_xmit
+ *
+ * MPI/SPI Transmit Register
+ */
+union mpi_xmit {
+       u64 u;
+       struct mpi_xmit_s {
+               /** Total number of bytes for transmit and receive. */
+               u64 totnum              : 11;
+               u64 reserved_11_19      : 9;
+               /** Number of bytes to transmit (max 1152) */
+               u64 txnum               : 11;
+               u64 reserved_31_59      : 29;
+               u64 leavecs             : 1;    /** Leave SPI_CSn_L asserted */
+               u64 csid                : 2;    /** Which CS to assert */
+               u64 reserved_63         : 1;
+       } s;
+};
+#endif
+
+/** Local driver data structure */
+struct octeontx_spi {
+       void *baseaddr;         /** Register base address */
+       u32 clkdiv;             /** Clock divisor for device speed */
+       bool is_otx2;           /** Gen 2 SoC */
+};
+
+static union mpi_cfg octeontx_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 octeontx_spi *priv = dev_get_priv(bus);
+       union mpi_cfg mpi_cfg;
+       uint max_speed = slave->max_hz;
+       bool cpha, cpol;
+
+       if (!max_speed)
+               max_speed = 12500000;
+       if (max_speed > OCTEONTX_SPI_MAX_CLOCK_HZ)
+               max_speed = OCTEONTX_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.u = 0;
+       mpi_cfg.s.clkdiv = priv->clkdiv & 0x1fff;
+       mpi_cfg.s.cshi = !!(slave->mode & SPI_CS_HIGH);
+       mpi_cfg.s.lsbfirst = !!(slave->mode & SPI_LSB_FIRST);
+       mpi_cfg.s.wireor = !!(slave->mode & SPI_3WIRE);
+       mpi_cfg.s.idlelo = cpha != cpol;
+       mpi_cfg.s.cslate = cpha;
+       mpi_cfg.s.enable = 1;
+       mpi_cfg.s.csena0 = 1;
+       mpi_cfg.s.csena1 = 1;
+       mpi_cfg.s.csena2 = 1;
+       mpi_cfg.s.csena3 = 1;
+
+       debug("\n mpi_cfg %llx\n", mpi_cfg.u);
+       return mpi_cfg;
+}
+
+/**
+ * Wait until the SPI bus is ready
+ *
+ * @param      dev     SPI device to wait for
+ */
+static void octeontx_spi_wait_ready(struct udevice *dev)
+{
+       struct udevice *bus = dev_get_parent(dev);
+       struct octeontx_spi *priv = dev_get_priv(bus);
+       void *baseaddr = priv->baseaddr;
+       union mpi_sts mpi_sts;
+
+       do {
+               mpi_sts.u = readq(baseaddr + MPI_STS);
+               WATCHDOG_RESET();
+       } while (mpi_sts.s.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 octeontx_spi_claim_bus(struct udevice *dev)
+{
+       struct udevice *bus = dev_get_parent(dev);
+       struct octeontx_spi *priv = dev_get_priv(bus);
+       void *baseaddr = priv->baseaddr;
+       union mpi_cfg mpi_cfg;
+
+       debug("\n\n%s(%s)\n", __func__, dev->name);
+       if (!OCTEONTX_SPI_CS_VALID(spi_chip_select(dev)))
+               return -EINVAL;
+
+#if !defined(CONFIG_ARCH_OCTEONTX)
+       acquire_flash_arb(true);
+#endif
+
+       mpi_cfg.u = readq(baseaddr + MPI_CFG);
+       mpi_cfg.s.tritx = 0;
+       mpi_cfg.s.enable = 1;
+       writeq(mpi_cfg.u, baseaddr + 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 octeontx_spi_release_bus(struct udevice *dev)
+{
+       struct udevice *bus = dev_get_parent(dev);
+       struct octeontx_spi *priv = dev_get_priv(bus);
+       void *baseaddr = priv->baseaddr;
+       union mpi_cfg mpi_cfg;
+
+       debug("%s(%s)\n\n", __func__, dev->name);
+       if (!OCTEONTX_SPI_CS_VALID(spi_chip_select(dev)))
+               return -EINVAL;
+
+#if !defined(CONFIG_ARCH_OCTEONTX)
+       acquire_flash_arb(false);
+#endif
+
+       mpi_cfg.u = readq(baseaddr + MPI_CFG);
+       mpi_cfg.s.enable = 0;
+       writeq(mpi_cfg.u, baseaddr + MPI_CFG);
+       udelay(1);
+
+       return 0;
+}
+
+#if defined(CONFIG_ARCH_OCTEONTX)
+static int octeontx_spi_xfer(struct udevice *dev, unsigned int bitlen,
+                            const void *dout, void *din, unsigned long flags)
+{
+       struct udevice *bus = dev_get_parent(dev);
+       struct octeontx_spi *priv = dev_get_priv(bus);
+       void *baseaddr = priv->baseaddr;
+       union mpi_tx mpi_tx;
+       union mpi_cfg 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 (!OCTEONTX_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 = octeontx_spi_set_mpicfg(dev);
+
+       if (mpi_cfg.u != readq(baseaddr + MPI_CFG)) {
+               writeq(mpi_cfg.u, baseaddr + MPI_CFG);
+               udelay(10);
+       }
+
+       debug("\n mpi_cfg upd %llx\n", mpi_cfg.u);
+
+       /*
+        * 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, baseaddr + MPI_WIDE_DAT);
+               }
+
+               mpi_tx.u = 0;
+               mpi_tx.s.csid = cs;
+               mpi_tx.s.leavecs = 1;
+               mpi_tx.s.txnum = tx_data ? 8 : 0;
+               mpi_tx.s.totnum = 8;
+               writeq(mpi_tx.u, baseaddr + MPI_TX);
+
+               octeontx_spi_wait_ready(dev);
+
+               debug("\n ");
+
+               if (rx_data) {
+                       wide_dat = readq(baseaddr + 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++, baseaddr + MPI_DAT(i));
+               }
+       }
+       mpi_tx.u = 0;
+       mpi_tx.s.csid = cs;
+       mpi_tx.s.leavecs = !(flags & SPI_XFER_END);
+       mpi_tx.s.txnum = tx_data ? len : 0;
+       mpi_tx.s.totnum = len;
+       writeq(mpi_tx.u, baseaddr + MPI_TX);
+
+       octeontx_spi_wait_ready(dev);
+
+       debug("\n ");
+
+       if (rx_data) {
+               for (i = 0; i < len; i++) {
+                       *rx_data = readq(baseaddr + MPI_DAT(i)) & 0xff;
+                       debug("  rx: %02x\n", *rx_data);
+                       rx_data++;
+               }
+       }
+
+       return 0;
+}
+
+#else
+
+static int octeontx_spi_xfer(struct udevice *dev, unsigned int bitlen,
+                            const void *dout, void *din, unsigned long flags)
+{
+       struct udevice *bus = dev_get_parent(dev);
+       struct octeontx_spi *priv = dev_get_priv(bus);
+       void *baseaddr = priv->baseaddr;
+       union mpi_xmit mpi_xmit;
+       union mpi_cfg 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 (!OCTEONTX_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 = octeontx_spi_set_mpicfg(dev);
+
+       mpi_cfg.s.legacy_dis = 1;
+       mpi_cfg.s.cs_sticky = 1;
+#ifdef USE_TBI_CLK
+       mpi_cfg.s.tb100_en = 1;
+#endif
+       mpi_cfg.s.iomode = 0;
+       if (flags & (SPI_TX_DUAL | SPI_RX_DUAL))
+               mpi_cfg.s.iomode = 2;
+       if (flags & (SPI_TX_QUAD | SPI_RX_QUAD))
+               mpi_cfg.s.iomode = 3;
+
+       if (mpi_cfg.u != readq(baseaddr + MPI_CFG)) {
+               writeq(mpi_cfg.u, baseaddr + MPI_CFG);
+               udelay(10);
+       }
+
+       debug("\n mpi_cfg upd %llx\n\n", mpi_cfg.u);
+
+       /* 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, baseaddr + MPI_WIDE_BUF(i));
+                       }
+               }
+
+               mpi_xmit.u = 0;
+               mpi_xmit.s.csid = cs;
+               mpi_xmit.s.leavecs = 1;
+               mpi_xmit.s.txnum = tx_data ? 1024 : 0;
+               mpi_xmit.s.totnum = 1024;
+               writeq(mpi_xmit.u, baseaddr + MPI_XMIT);
+
+               octeontx_spi_wait_ready(dev);
+
+               debug("\n ");
+
+               if (rx_data) {
+                       /* 8 bytes per iteration */
+                       for (i = 0; i < 128; i++) {
+                               wide_dat = readq(baseaddr + 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, baseaddr + MPI_WIDE_BUF(i));
+               }
+               if (rem) {
+                       memcpy(&wide_dat, tx_data, rem);
+                       debug("  rtx: %016llx\t", wide_dat);
+                       writeq(wide_dat, baseaddr + MPI_WIDE_BUF(i));
+               }
+       }
+
+       mpi_xmit.u = 0;
+       mpi_xmit.s.csid = cs;
+       mpi_xmit.s.leavecs = !(flags & SPI_XFER_END);
+       mpi_xmit.s.txnum = tx_data ? len : 0;
+       mpi_xmit.s.totnum = len;
+       writeq(mpi_xmit.u, baseaddr + MPI_XMIT);
+
+       octeontx_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(baseaddr + 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(baseaddr + 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 octeontx_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 octeontx_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 buf[8];
+       u8 *addr;
+       int i, temp, ret;
+
+       if (op->cmd.buswidth != 1)
+               return -ENOTSUPP;
+
+       /* Send CMD */
+       i = 0;
+       buf[i++] = op->cmd.opcode;
+
+       if (!op->data.nbytes && !op->addr.nbytes && !op->dummy.nbytes)
+               flags |= SPI_XFER_END;
+
+       ret = octeontx_spi_xfer(slave->dev, (i * 8), (void *)&buf, NULL,
+                               flags);
+       if (ret < 0)
+               return ret;
+       /* Send Address and dummy */
+       if (op->addr.nbytes) {
+               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 = octeontx_spi_xfer(slave->dev, (i - 1) * 8,
+                                       (void *)&buf[1], NULL,
+                                       flags);
+               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 = octeontx_spi_xfer(slave->dev, (op->data.nbytes * 8), tx, rx,
+                               flags);
+       return ret;
+}
+
+static const struct spi_controller_mem_ops octeontx_spi_mem_ops = {
+       .supports_op = octeontx_spi_supports_op,
+       .exec_op = octeontx_spi_exec_op,
+};
+
+#endif
+
+/**
+ * Set the speed of the SPI bus
+ *
+ * @param      bus     bus to set
+ * @param      max_hz  maximum speed supported
+ */
+static int octeontx_spi_set_speed(struct udevice *bus, uint max_hz)
+{
+       struct octeontx_spi *priv = dev_get_priv(bus);
+       u64 refclk = octeontx_get_io_clock();
+       u32 calc_hz;
+
+       debug("%s(%s, %u, %llu)\n", __func__, bus->name, max_hz, refclk);
+
+       if (max_hz > OCTEONTX_SPI_MAX_CLOCK_HZ)
+               max_hz = OCTEONTX_SPI_MAX_CLOCK_HZ;
+
+#ifdef USE_TBI_CLK
+       refclk = OCTEONTX2_TBI_CLK;
+#endif
+       priv->clkdiv = refclk / (2 * max_hz);
+       while (1) {
+               calc_hz = refclk / (2 * priv->clkdiv);
+               if (calc_hz <= max_hz)
+                       break;
+               priv->clkdiv += 1;
+       }
+       if (priv->clkdiv > 8191)
+               return -1;
+
+       debug("%s %d\n", __func__, priv->clkdiv);
+
+       return 0;
+}
+
+static int octeontx_spi_set_mode(struct udevice *bus, uint mode)
+{
+       /* We don't set it here */
+       return 0;
+}
+
+static int octeontx_pci_spi_probe(struct udevice *dev)
+{
+       struct octeontx_spi *priv = dev_get_priv(dev);
+       pci_dev_t bdf = dm_pci_get_bdf(dev);
+
+       debug("SPI PCI device: %x\n", bdf);
+       dev->req_seq = PCI_FUNC(bdf);
+       priv->baseaddr = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
+                                       PCI_REGION_MEM);
+
+       debug("SPI bus %s %d at %p\n", dev->name, dev->seq, priv->baseaddr);
+
+       return 0;
+}
+
+static const struct dm_spi_ops octeontx_spi_ops = {
+       .claim_bus      = octeontx_spi_claim_bus,
+       .release_bus    = octeontx_spi_release_bus,
+       .xfer           = octeontx_spi_xfer,
+       .set_speed      = octeontx_spi_set_speed,
+       .set_mode       = octeontx_spi_set_mode,
+#if !defined(CONFIG_ARCH_OCTEONTX)
+       .mem_ops        = &octeontx_spi_mem_ops,
+#endif
+};
+
+static const struct udevice_id octeontx_spi_ids[] = {
+       { .compatible   = "cavium,thunder-8890-spi" },
+       { .compatible   = "cavium,thunder-8190-spi" },
+       { }
+};
+
+U_BOOT_DRIVER(octeontx_pci_spi) = {
+       .name                   = "spi_octeontx",
+       .id                     = UCLASS_SPI,
+       .of_match               = octeontx_spi_ids,
+       .probe                  = octeontx_pci_spi_probe,
+       .priv_auto_alloc_size   = sizeof(struct octeontx_spi),
+       .ops                    = &octeontx_spi_ops,
+};
+
+static struct pci_device_id octeontx_spi_supported[] = {
+       { PCI_VDEVICE(CAVIUM, 0xa00b) },
+       { },
+};
+
+U_BOOT_PCI_DEVICE(octeontx_pci_spi, octeontx_spi_supported);
-- 
2.23.0

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to