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

Stefan Roese sr at denx.de
Thu Jul 30 10:06:46 CEST 2020


Hi Jagan,

On 30.07.20 09:44, Jagan Teki wrote:
> 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?

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


More information about the U-Boot mailing list