Signed-off-by: Tim Harvey <thar...@gateworks.com>
---
 configs/thunderx_81xx_defconfig |   4 +
 drivers/spi/Kconfig             |   6 +
 drivers/spi/Makefile            |   1 +
 drivers/spi/thunderx_spi.c      | 448 ++++++++++++++++++++++++++++++++
 4 files changed, 459 insertions(+)
 create mode 100755 drivers/spi/thunderx_spi.c

diff --git a/configs/thunderx_81xx_defconfig b/configs/thunderx_81xx_defconfig
index e43aa9750d..48f57ecf1b 100644
--- a/configs/thunderx_81xx_defconfig
+++ b/configs/thunderx_81xx_defconfig
@@ -24,6 +24,7 @@ CONFIG_SYS_PROMPT="ThunderX_81XX> "
 CONFIG_CMD_GPIO=y
 CONFIG_CMD_I2C=y
 CONFIG_CMD_PCI=y
+CONFIG_CMD_SPI=y
 # CONFIG_CMD_NET is not set
 CONFIG_DEFAULT_DEVICE_TREE="thunderx-81xx"
 CONFIG_DM=y
@@ -39,3 +40,6 @@ CONFIG_PCI_THUNDERX=y
 CONFIG_DM_SERIAL=y
 CONFIG_DEBUG_UART_PL011=y
 CONFIG_DEBUG_UART_SKIP_INIT=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_THUNDERX_SPI=y
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 516188ea88..d1d5463909 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -233,6 +233,12 @@ config TEGRA210_QSPI
          be used to access SPI chips on platforms embedding this
          NVIDIA Tegra210 IP core.
 
+config THUNDERX_SPI
+       bool "Cavium ThunderX SPI driver"
+       help
+         Enable the Cavium ThunderX SPI driver. This driver can be used to
+         access the SPI NOR flash on ThunderX SoC platforms.
+
 config XILINX_SPI
        bool "Xilinx SPI driver"
        help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 7242ea7e40..ea99775094 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o
 obj-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o
 obj-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o
 obj-$(CONFIG_TEGRA210_QSPI) += tegra210_qspi.o
+obj-$(CONFIG_THUNDERX_SPI) += thunderx_spi.o
 obj-$(CONFIG_TI_QSPI) += ti_qspi.o
 obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
 obj-$(CONFIG_ZYNQ_SPI) += zynq_spi.o
diff --git a/drivers/spi/thunderx_spi.c b/drivers/spi/thunderx_spi.c
new file mode 100755
index 0000000000..e165215524
--- /dev/null
+++ b/drivers/spi/thunderx_spi.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier:    GPL-2.0+
+/*
+ * Copyright (C) 2018, Cavium Inc.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <malloc.h>
+#include <spi.h>
+#include <watchdog.h>
+
+#include <asm/io.h>
+#include <asm/arch-thunderx/thunderx.h>
+#include <asm/unaligned.h>
+
+#define THUNDERX_SPI_MAX_BYTES         9
+#define THUNDERX_SPI_MAX_CLOCK_HZ      50000000
+
+#define THUNDERX_SPI_NUM_CS            4
+
+#define THUNDERX_SPI_CS_VALID(cs)      ((cs) < THUNDERX_SPI_NUM_CS)
+
+#define MPI_CFG                                0x1000
+#define MPI_STS                                0x1008
+#define MPI_TX                         0x1010
+#define MPI_WIDE_DAT                   0x1040
+#define MPI_DAT(X)                     (0x1080 + ((X) << 3))
+
+union mpi_cfg {
+       uint64_t u;
+       struct mpi_cfg_s {
+#if __BYTE_ORDER == __BIG_ENDIAN /* Word 0 - Big Endian */
+               uint64_t                :35;
+
+               uint64_t clkdiv         :13;    /** clock divisor */
+               uint64_t csena3         :1;     /** cs enable 3. */
+               uint64_t csena2         :1;     /** cs enable 2 */
+               uint64_t csena1         :1;     /** cs enable 1 */
+               uint64_t csena0         :1;     /** cs enable 0 */
+               /**
+                * 0 = SPI_CSn asserts 1/2 coprocessor-clock cycle before
+                *     transaction
+                * 1 = SPI_CSn asserts coincident with transaction
+                */
+               uint64_t cslate         :1;
+               /**
+                * Tristate TX.  Set to 1 to tristate SPI_DO when not
+                * transmitting.
+                */
+               uint64_t tritx          :1;
+               /**
+                * When set, guarantees idle coprocessor-clock cycles between
+                * commands.
+                */
+               uint64_t idleclks       :2;
+               /**
+                * SPI_CSn_L high.  1 = SPI_CSn_L is asserted high,
+                * 0 = SPI_CS_n asserted low.
+                */
+               uint64_t cshi           :1;
+               uint64_t                :2;     /** Reserved */
+               /** 0 = shift MSB first, 1 = shift LSB first */
+               uint64_t lsbfirst       :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.
+                */
+               uint64_t wireor         :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.
+                */
+               uint64_t clk_cont       :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.
+                */
+               uint64_t idlelo         :1;
+               /** MPI/SPI enable, 0 = pins are tristated, 1 = pins driven */
+               uint64_t enable         :1;
+#else /* Word 0 - Little Endian */
+               uint64_t enable         :1;
+               uint64_t idlelo         :1;
+               uint64_t clk_cont       :1;
+               uint64_t wireor         :1;
+               uint64_t lsbfirst       :1;
+               uint64_t                :2;
+               uint64_t cshi           :1;
+               uint64_t idleclks       :2;
+               uint64_t tritx          :1;
+               uint64_t cslate         :1;
+               uint64_t csena0         :1;
+               uint64_t csena1         :1;
+               uint64_t csena2         :1;
+               uint64_t csena3         :1;
+               uint64_t clkdiv         :13;
+               uint64_t                :35;    /** Reserved */
+#endif /* Word 0 - End */
+       } s;
+       /* struct mpi_cfg_s cn; */
+};
+
+/**
+ * Register (NCB) mpi_dat#
+ *
+ * MPI/SPI Data Registers
+ */
+union mpi_dat {
+       uint64_t u;
+       struct mpi_datx_s {
+#if __BYTE_ORDER == __BIG_ENDIAN /* Word 0 - Big Endian */
+               uint64_t reserved_8_63  :56;
+               /**< [  7:  0](R/W/H) Data to transmit/receive. */
+               uint64_t data           :8;
+#else /* Word 0 - Little Endian */
+               uint64_t data           :8;
+               uint64_t reserved_8_63  :56;
+#endif /* Word 0 - End */
+       } s;
+       /* struct mpi_datx_s cn; */
+};
+
+/**
+ * Register (NCB) mpi_sts
+ *
+ * MPI/SPI STS Register
+ */
+union mpi_sts {
+       uint64_t u;
+       struct mpi_sts_s {
+#if __BYTE_ORDER == __BIG_ENDIAN /* Word 0 - Big Endian */
+               uint64_t reserved_13_63 :51;
+               uint64_t rxnum          :5;     /** Number of bytes */
+               uint64_t reserved_2_7   :6;
+               uint64_t mpi_intr       :1;     /** Transaction done int */
+               uint64_t busy           :1;     /** SPI engine busy */
+#else /* Word 0 - Little Endian */
+               uint64_t busy           :1;
+               uint64_t mpi_intr       :1;
+               uint64_t reserved_2_7   :6;
+               uint64_t rxnum          :5;
+               uint64_t reserved_13_63 :51;
+#endif /* Word 0 - End */
+       } s;
+       /* struct mpi_sts_s cn; */
+};
+
+/**
+ * Register (NCB) mpi_tx
+ *
+ * MPI/SPI Transmit Register
+ */
+union mpi_tx {
+       uint64_t u;
+       struct mpi_tx_s {
+#if __BYTE_ORDER == __BIG_ENDIAN /* Word 0 - Big Endian */
+               uint64_t                :42;    /* Reserved */
+               uint64_t csid           :2;     /** Which CS to assert */
+               uint64_t                :3;     /* Reserved */
+               uint64_t leavecs        :1;     /** Leave CSn asserted */
+               uint64_t                :3;     /* Reserved */
+               uint64_t txnum          :5;     /** Number of words to tx */
+               uint64_t                :3;     /* Reserved */
+               uint64_t totnum         :5;     /** Total bytes to shift */
+#else /* Word 0 - Little Endian */
+               uint64_t totnum         :5;
+               uint64_t                :3;
+               uint64_t txnum          :5;
+               uint64_t                :3;
+               uint64_t leavecs        :1;
+               uint64_t                :3;
+               uint64_t csid           :2;
+               uint64_t                :42;
+#endif /* Word 0 - End */
+       } s;
+       /* struct mpi_tx_s cn; */
+};
+
+/** Local driver data structure */
+struct thunderx_spi {
+       void *baseaddr;         /** Register base address */
+       u32 clkdiv;             /** Clock divisor for device speed */
+};
+
+void *thunderx_spi_get_baseaddr(struct udevice *dev)
+{
+       struct udevice *bus = dev_get_parent(dev);
+       struct thunderx_spi *priv = dev_get_priv(bus);
+
+       return priv->baseaddr;
+}
+
+static union mpi_cfg thunderx_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 thunderx_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 > THUNDERX_SPI_MAX_CLOCK_HZ)
+               max_speed = THUNDERX_SPI_MAX_CLOCK_HZ;
+
+       dev_dbg(dev, "%s: CS%d Hz=%d Mode=%x\n", __func__,
+               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;
+       dev_dbg(dev, "%s: mpi_cfg=%llx\n", __func__, mpi_cfg.u);
+
+       return mpi_cfg;
+}
+
+/**
+ * Wait until the SPI bus is ready
+ *
+ * @param      dev     SPI device to wait for
+ */
+static void thunderx_spi_wait_ready(struct udevice *dev)
+{
+       void *baseaddr = thunderx_spi_get_baseaddr(dev);
+       union mpi_sts mpi_sts;
+
+       do {
+               mpi_sts.u = readq(baseaddr + MPI_STS);
+               WATCHDOG_RESET();
+       } while (mpi_sts.s.busy);
+}
+/**
+ * Claim the bus for a slave device
+ *
+ * @param      dev     SPI bus
+ *
+ * @return     0 for success, -EINVAL if chip select is invalid
+ */
+static int thunderx_spi_claim_bus(struct udevice *dev)
+{
+       void *baseaddr = thunderx_spi_get_baseaddr(dev);
+       union mpi_cfg mpi_cfg;
+
+       if (!THUNDERX_SPI_CS_VALID(spi_chip_select(dev)))
+               return -EINVAL;
+
+       mpi_cfg.u = readq(baseaddr + MPI_CFG);
+       mpi_cfg.s.tritx = 0;
+       mpi_cfg.s.enable = 1;
+       writeq(mpi_cfg.u, baseaddr + MPI_CFG);
+
+       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 thunderx_spi_release_bus(struct udevice *dev)
+{
+       void *baseaddr = thunderx_spi_get_baseaddr(dev);
+       union mpi_cfg mpi_cfg;
+
+       if (!THUNDERX_SPI_CS_VALID(spi_chip_select(dev)))
+               return -EINVAL;
+
+       mpi_cfg.u = readq(baseaddr + MPI_CFG);
+       mpi_cfg.s.enable = 0;
+       writeq(mpi_cfg.u, baseaddr + MPI_CFG);
+
+       return 0;
+}
+
+static int thunderx_spi_xfer(struct udevice *dev, unsigned int bitlen,
+                            const void *dout, void *din, unsigned long flags)
+{
+       void *baseaddr = thunderx_spi_get_baseaddr(dev);
+       union mpi_tx mpi_tx;
+       union mpi_cfg mpi_cfg;
+       uint64_t wide_dat = 0;
+       int len = bitlen / 8;
+       int i;
+       const uint8_t *tx_data = dout;
+       uint8_t *rx_data = din;
+       int cs = spi_chip_select(dev);
+
+       if (!THUNDERX_SPI_CS_VALID(cs))
+               return -EINVAL;
+
+       dev_dbg(dev, "%s bitlen=%u dout=%p din=%p flags=0x%lx CS%d\n", __func__,
+               bitlen, dout, din, flags, cs);
+
+       mpi_cfg = thunderx_spi_set_mpicfg(dev);
+
+       if (mpi_cfg.u != readq(baseaddr + MPI_CFG))
+               writeq(mpi_cfg.u, baseaddr + 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((uint64_t *)tx_data);
+                       debug("  tx: %016llx\n", (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);
+
+               thunderx_spi_wait_ready(dev);
+
+               if (rx_data) {
+                       wide_dat = readq(baseaddr + MPI_WIDE_DAT);
+                       debug("  rx: %016llx\n", (unsigned long long)wide_dat);
+                       *(uint64_t *)rx_data = wide_dat;
+                       rx_data += 8;
+               }
+               len -= 8;
+       }
+
+       /* 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);
+
+       thunderx_spi_wait_ready(dev);
+
+       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;
+}
+
+/**
+ * Set the speed of the SPI bus
+ *
+ * @param      bus     bus to set
+ * @param      max_hz  maximum speed supported
+ */
+static int thunderx_spi_set_speed(struct udevice *bus, uint max_hz)
+{
+       struct thunderx_spi *priv = dev_get_priv(bus);
+
+       dev_dbg(dev, "%s: max_hz=%u io=%llu", __func__, max_hz,
+               thunderx_get_io_clock());
+       if (max_hz > THUNDERX_SPI_MAX_CLOCK_HZ)
+               max_hz = THUNDERX_SPI_MAX_CLOCK_HZ;
+       priv->clkdiv = (thunderx_get_io_clock()) / (2 * max_hz);
+
+       return 0;
+}
+
+static int thunderx_spi_set_mode(struct udevice *bus, uint mode)
+{
+       /* We don't set it here */
+       return 0;
+}
+
+static int thunderx_pci_spi_probe(struct udevice *dev)
+{
+       struct thunderx_spi *priv = dev_get_priv(dev);
+       pci_dev_t bdf = dm_pci_get_bdf(dev);
+       size_t size;
+
+       dev->req_seq = PCI_FUNC(bdf);
+       priv->baseaddr = dm_pci_map_bar(dev, 0, &size, PCI_REGION_MEM);
+       dev_dbg(dev, "%s: SPI PCI device: bdf:%x base:%p\n", __func__,
+               bdf, priv->baseaddr);
+
+       return 0;
+}
+
+static const struct dm_spi_ops thunderx_spi_ops = {
+       .claim_bus      = thunderx_spi_claim_bus,
+       .release_bus    = thunderx_spi_release_bus,
+       .xfer           = thunderx_spi_xfer,
+       .set_speed      = thunderx_spi_set_speed,
+       .set_mode       = thunderx_spi_set_mode,
+};
+
+static const struct udevice_id thunderx_spi_ids[] = {
+       { .compatible   = "cavium,thunder-8890-spi" },
+       { .compatible   = "cavium,thunder-8190-spi" },
+       { }
+};
+
+U_BOOT_DRIVER(thunderx_pci_spi) = {
+       .name                   = "spi_thunderx",
+       .id                     = UCLASS_SPI,
+       .of_match               = thunderx_spi_ids,
+       .probe                  = thunderx_pci_spi_probe,
+       .priv_auto_alloc_size   = sizeof(struct thunderx_spi),
+       .ops                    = &thunderx_spi_ops,
+};
+
+static struct pci_device_id thunderx_pci_spi_supported[] = {
+       { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_THUNDERX_SPI) },
+       { },
+};
+
+U_BOOT_PCI_DEVICE(thunderx_pci_spi, thunderx_pci_spi_supported);
+
-- 
2.17.1

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

Reply via email to