[PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon

Jagan Teki jagan at amarulasolutions.com
Thu Jul 30 09:44:58 CEST 2020


On Thu, Jul 23, 2020 at 3:47 PM Stefan Roese <sr at denx.de> wrote:
>
> From: Suneel Garapati <sgarapati at marvell.com>
>
> Adds support for SPI controllers found on Octeon II/III and Octeon TX
> TX2 SoC platforms.
>
> Signed-off-by: Aaron Williams <awilliams at marvell.com>
> Signed-off-by: Suneel Garapati <sgarapati at marvell.com>
> Signed-off-by: Stefan Roese <sr at denx.de>
> Cc: Daniel Schwierzeck <daniel.schwierzeck at gmail.com>
> Cc: Aaron Williams <awilliams at marvell.com>
> Cc: Chandrakala Chavva <cchavva at marvell.com>
> Cc: Jagan Teki <jagan at 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.


More information about the U-Boot mailing list