[U-Boot] [RFC PATCH 21/29] drivers: spi: add SPI controller driver for OcteonTX
Suneel Garapati
suneelglinux at gmail.com
Tue Oct 29 21:08:13 UTC 2019
From: Suneel Garapati <sgarapati at marvell.com>
Adds support for SPI controllers found on OcteonTX or
OcteonTX2 SoC platforms.
Signed-off-by: Aaron Williams <awilliams at marvell.com>
Signed-off-by: Suneel Garapati <sgarapati at 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
More information about the U-Boot
mailing list