[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