All of lore.kernel.org
 help / color / mirror / Atom feed
From: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
To: u-boot@lists.denx.de
Subject: [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon
Date: Fri, 24 Jul 2020 15:56:50 +0200	[thread overview]
Message-ID: <af15c11a2657009f45e9d4cec4c8d8e58a6cb27c.camel@gmail.com> (raw)
In-Reply-To: <20200723101724.953325-7-sr@denx.de>

Am Donnerstag, den 23.07.2020, 12:17 +0200 schrieb Stefan Roese:
> From: Suneel Garapati <sgarapati@marvell.com>
> 
> Adds support for SPI controllers found on Octeon II/III and Octeon TX
> TX2 SoC platforms.
> 
> Signed-off-by: Aaron Williams <awilliams@marvell.com>
> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
> Signed-off-by: Stefan Roese <sr@denx.de>
> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
> Cc: Aaron Williams <awilliams@marvell.com>
> Cc: Chandrakala Chavva <cchavva@marvell.com>
> Cc: Jagan Teki <jagan@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);
> +
> +	mpi_cfg = readq(base + MPI_CFG);
> +	mpi_cfg &= ~MPI_CFG_ENABLE;
> +	writeq(mpi_cfg, base + MPI_CFG);
> +	mpi_cfg = readq(base + MPI_CFG);
> +	udelay(1);
> +
> +	return 0;
> +}
> +
> +static int octeon_spi_xfer(struct udevice *dev, unsigned int bitlen,
> +			   const void *dout, void *din, unsigned long flags)
> +{
> +	struct udevice *bus = dev_get_parent(dev);
> +	struct octeon_spi *priv = dev_get_priv(bus);
> +	void *base = priv->base;
> +	u64 mpi_tx;
> +	u64 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 (!OCTEON_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 = octeon_spi_set_mpicfg(dev);
> +	if (mpi_cfg != readq(base + MPI_CFG)) {
> +		writeq(mpi_cfg, base + MPI_CFG);
> +		mpi_cfg = readq(base + MPI_CFG);
> +		udelay(10);
> +	}
> +
> +	debug("\n mpi_cfg upd %llx\n", 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((u64 *)tx_data);
> +			debug("  tx: %016llx \t", (unsigned long long)wide_dat);
> +			tx_data += 8;
> +			writeq(wide_dat, base + MPI_WIDE_DAT);
> +		}
> +
> +		mpi_tx = FIELD_PREP(MPI_TX_CSID, cs) |
> +			FIELD_PREP(MPI_TX_LEAVECS, 1) |
> +			FIELD_PREP(MPI_TX_TXNUM, tx_data ? 8 : 0) |
> +			FIELD_PREP(MPI_TX_TOTNUM, 8);
> +		writeq(mpi_tx, base + MPI_TX);
> +
> +		octeon_spi_wait_ready(dev);
> +
> +		debug("\n ");
> +
> +		if (rx_data) {
> +			wide_dat = readq(base + 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++, base + MPI_DAT(i));
> +		}
> +	}
> +
> +	mpi_tx = FIELD_PREP(MPI_TX_CSID, cs) |
> +		FIELD_PREP(MPI_TX_LEAVECS, !(flags & SPI_XFER_END)) |
> +		FIELD_PREP(MPI_TX_TXNUM, tx_data ? len : 0) |
> +		FIELD_PREP(MPI_TX_TOTNUM, len);
> +	writeq(mpi_tx, base + MPI_TX);
> +
> +	octeon_spi_wait_ready(dev);
> +
> +	debug("\n ");
> +
> +	if (rx_data) {
> +		for (i = 0; i < len; i++) {
> +			*rx_data = readq(base + MPI_DAT(i)) & 0xff;
> +			debug("  rx: %02x\n", *rx_data);
> +			rx_data++;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int octeontx2_spi_xfer(struct udevice *dev, unsigned int bitlen,
> +			      const void *dout, void *din, unsigned long flags)
> +{
> +	struct udevice *bus = dev_get_parent(dev);
> +	struct octeon_spi *priv = dev_get_priv(bus);
> +	void *base = priv->base;
> +	u64 mpi_xmit;
> +	u64 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 (!OCTEON_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 = octeon_spi_set_mpicfg(dev);
> +
> +	mpi_cfg |= MPI_CFG_TRITX | MPI_CFG_LEGACY_DIS | MPI_CFG_CS_STICKY |
> +		MPI_CFG_TB100_EN;
> +
> +	mpi_cfg &= ~MPI_CFG_IOMODE;
> +	if (flags & (SPI_TX_DUAL | SPI_RX_DUAL))
> +		mpi_cfg |= FIELD_PREP(MPI_CFG_IOMODE, 2);
> +	if (flags & (SPI_TX_QUAD | SPI_RX_QUAD))
> +		mpi_cfg |= FIELD_PREP(MPI_CFG_IOMODE, 3);
> +
> +	if (mpi_cfg != readq(base + MPI_CFG)) {
> +		writeq(mpi_cfg, base + MPI_CFG);
> +		mpi_cfg = readq(base + MPI_CFG);
> +		udelay(10);
> +	}
> +
> +	debug("\n mpi_cfg upd %llx\n\n", mpi_cfg);
> +
> +	/* 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, base + MPI_WIDE_BUF(i));
> +			}
> +		}
> +
> +		mpi_xmit = FIELD_PREP(MPI_XMIT_CSID, cs) | MPI_XMIT_LEAVECS |
> +			FIELD_PREP(MPI_XMIT_TXNUM, tx_data ? 1024 : 0) |
> +			FIELD_PREP(MPI_XMIT_TOTNUM, 1024);
> +		writeq(mpi_xmit, base + MPI_XMIT);
> +
> +		octeon_spi_wait_ready(dev);
> +
> +		debug("\n ");
> +
> +		if (rx_data) {
> +			/* 8 bytes per iteration */
> +			for (i = 0; i < 128; i++) {
> +				wide_dat = readq(base + 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, base + MPI_WIDE_BUF(i));
> +		}
> +		if (rem) {
> +			memcpy(&wide_dat, tx_data, rem);
> +			debug("  rtx: %016llx\t", wide_dat);
> +			writeq(wide_dat, base + MPI_WIDE_BUF(i));
> +		}
> +	}
> +
> +	mpi_xmit = FIELD_PREP(MPI_XMIT_CSID, cs) |
> +		FIELD_PREP(MPI_XMIT_LEAVECS, !(flags & SPI_XFER_END)) |
> +		FIELD_PREP(MPI_XMIT_TXNUM, tx_data ? len : 0) |
> +		FIELD_PREP(MPI_XMIT_TOTNUM, len);
> +	writeq(mpi_xmit, base + MPI_XMIT);
> +
> +	octeon_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(base + 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(base + 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 octeon_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 octeon_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 opcode, *buf;
> +	u8 *addr;
> +	int i, temp, ret;
> +
> +	if (op->cmd.buswidth != 1)
> +		return -ENOTSUPP;
> +
> +	/* Send CMD */
> +	i = 0;
> +	opcode = op->cmd.opcode;
> +
> +	if (!op->data.nbytes && !op->addr.nbytes && !op->dummy.nbytes)
> +		flags |= SPI_XFER_END;
> +
> +	ret = octeontx2_spi_xfer(slave->dev, 8, (void *)&opcode, NULL, flags);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Send Address and dummy */
> +	if (op->addr.nbytes) {
> +		/* Alloc buffer for address+dummy */
> +		buf = (u8 *)calloc(1, op->addr.nbytes + op->dummy.nbytes);
> +		if (!buf) {
> +			printf("%s Out of memory\n", __func__);
> +			return -ENOMEM;
> +		}
> +		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 = octeontx2_spi_xfer(slave->dev, i * 8, (void *)buf, NULL,
> +					 flags);
> +		free(buf);
> +		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 = octeontx2_spi_xfer(slave->dev, (op->data.nbytes * 8), tx, rx,
> +				 flags);
> +	return ret;
> +}
> +
> +static const struct spi_controller_mem_ops octeontx2_spi_mem_ops = {
> +	.supports_op = octeon_spi_supports_op,
> +	.exec_op = octeon_spi_exec_op,
> +};
> +
> +/**
> + * Set the speed of the SPI bus
> + *
> + * @param	bus	bus to set
> + * @param	max_hz	maximum speed supported
> + */
> +static int octeon_spi_set_speed(struct udevice *bus, uint max_hz)
> +{
> +	struct octeon_spi *priv = dev_get_priv(bus);
> +	ulong clk_rate;
> +	u32 calc_hz;
> +
> +	if (max_hz > OCTEON_SPI_MAX_CLOCK_HZ)
> +		max_hz = OCTEON_SPI_MAX_CLOCK_HZ;
> +
> +	clk_rate = clk_get_rate(&priv->clk);
> +	if (IS_ERR_VALUE(clk_rate))
> +		return -EINVAL;
> +
> +	debug("%s(%s, %u, %lu)\n", __func__, bus->name, max_hz, clk_rate);
> +
> +	priv->clkdiv = clk_rate / (2 * max_hz);
> +	while (1) {
> +		calc_hz = clk_rate / (2 * priv->clkdiv);
> +		if (calc_hz <= max_hz)
> +			break;
> +		priv->clkdiv += 1;
> +	}
> +
> +	if (priv->clkdiv > 8191)
> +		return -EINVAL;
> +
> +	debug("%s: clkdiv=%d\n", __func__, priv->clkdiv);
> +
> +	return 0;
> +}
> +
> +static int octeon_spi_set_mode(struct udevice *bus, uint mode)
> +{
> +	/* We don't set it here */
> +	return 0;
> +}
> +
> +static const struct dm_spi_ops octeon_spi_ops = {
> +	.claim_bus	= octeon_spi_claim_bus,
> +	.release_bus	= octeon_spi_release_bus,
> +	.set_speed	= octeon_spi_set_speed,
> +	.set_mode	= octeon_spi_set_mode,
> +	.xfer		= octeon_spi_xfer,
> +};
> +
> +static const struct dm_spi_ops octeontx2_spi_ops = {
> +	.claim_bus	= octeon_spi_claim_bus,
> +	.release_bus	= octeon_spi_release_bus,
> +	.set_speed	= octeon_spi_set_speed,
> +	.set_mode	= octeon_spi_set_mode,
> +	.xfer		= octeontx2_spi_xfer,
> +	.mem_ops	= &octeontx2_spi_mem_ops,
> +};
> +
> +static int octeon_spi_probe(struct udevice *dev)
> +{
> +	struct octeon_spi *priv = dev_get_priv(dev);
> +	const struct octeon_spi_data *data;
> +	int ret;
> +
> +	data = (const struct octeon_spi_data *)dev_get_driver_data(dev);
> +	if (data->probe == PROBE_PCI) {
> +		pci_dev_t bdf = dm_pci_get_bdf(dev);
> +
> +		debug("SPI PCI device: %x\n", bdf);
> +		priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
> +					    PCI_REGION_MEM);
> +	} else {
> +		priv->base = dev_remap_addr(dev);
> +	}
> +
> +	priv->base += data->reg_offs;
> +
> +	/* Octeon TX2 needs a different ops struct */
> +	if (device_is_compatible(dev, "cavium,thunderx-spi")) {
> +		/*
> +		 * "ops" is const and can't be written directly. So we need
> +		 * to write the Octeon TX2 ops value using this trick
> +		 */
> +		writeq((u64)&octeontx2_spi_ops, (void *)&dev->driver->ops);
> +	}

can't you simply add a xfer() function pointer to "struct
octeon_spi_data" and assign the according xfer function to it? Then
in octeon_spi_xfer() you can simply call that function pointer. With
this you only need one instance of "struct dm_spi_ops" and don't need
this ugly hack ;) Maybe you can add some common code to
octeon_spi_xfer() itself and reduce the size of the SoC specific xfer
functions.

> +
> +	ret = clk_get_by_index(dev, 0, &priv->clk);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = clk_enable(&priv->clk);
> +	if (ret)
> +		return ret;
> +
> +	debug("SPI bus %s %d at %p\n", dev->name, dev->seq, priv->base);
> +
> +	return 0;
> +}
> +
> +static const struct octeon_spi_data spi_octeon_data = {
> +	.probe = PROBE_DT,
> +	.reg_offs = 0x0000,
> +};
> +
> +static const struct octeon_spi_data spi_octeontx_data = {
> +	.probe = PROBE_PCI,
> +	.reg_offs = 0x1000,
> +};
> +
> +static const struct udevice_id octeon_spi_ids[] = {
> +	/* MIPS Octeon */
> +	{ .compatible = "cavium,octeon-3010-spi",
> +	  .data = (ulong)&spi_octeon_data },
> +	/* ARM Octeon TX / TX2 */
> +	{ .compatible = "cavium,thunder-8190-spi",
> +	  .data = (ulong)&spi_octeontx_data },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(octeon_spi) = {
> +	.name			= "spi_octeon",
> +	.id			= UCLASS_SPI,
> +	.of_match		= octeon_spi_ids,
> +	.probe			= octeon_spi_probe,
> +	.priv_auto_alloc_size	= sizeof(struct octeon_spi),
> +	.ops			= &octeon_spi_ops,
> +};
-- 
- Daniel

  reply	other threads:[~2020-07-24 13:56 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-07-23 10:17 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
2020-07-23 10:17 ` [PATCH v2 01/10] gpio: octeon_gpio: Add GPIO controller driver for Octeon Stefan Roese
2020-07-23 10:17 ` [PATCH v2 02/10] mips: octeon: mrvl,cn73xx.dtsi: Add GPIO DT nodes Stefan Roese
2020-07-23 10:17 ` [PATCH v2 03/10] mips: octeon: dts: Add I2C " Stefan Roese
2020-07-23 10:17 ` [PATCH v2 04/10] clk: clk_octeon: Add simple MIPS Octeon clock driver Stefan Roese
2020-07-23 10:17 ` [PATCH v2 05/10] mips: octeon: dts: Add Octeon clock driver DT nodes Stefan Roese
2020-07-23 10:17 ` [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon Stefan Roese
2020-07-24 13:56   ` Daniel Schwierzeck [this message]
2020-07-24 14:27     ` Stefan Roese
2020-07-30  5:49     ` Stefan Roese
2020-07-30  6:23       ` Stefan Roese
2020-07-30 11:00         ` Daniel Schwierzeck
2020-07-30 11:42           ` Stefan Roese
2020-07-30  7:50     ` Jagan Teki
2020-07-30  7:53       ` Stefan Roese
2020-07-30  7:44   ` Jagan Teki
2020-07-30  8:06     ` Stefan Roese
2020-07-23 10:17 ` [PATCH v2 07/10] mips: octeon: mrvl,cn73xx.dtsi: Add SPI DT node Stefan Roese
2020-07-23 10:17 ` [PATCH v2 08/10] mips: octeon: mrvl, octeon-ebb7304.dts: Add SPI flash " Stefan Roese
2020-07-23 10:17 ` [PATCH v2 09/10] mips: octeon: Update Octeon Kconfig Stefan Roese
2020-07-23 10:17 ` [PATCH v2 10/10] mips: octeon: Update EBB7304 defconfig Stefan Roese
2020-07-30 11:56 [PATCH v2 00/10] mips: octeon: Misc Octeon drivers, DT and Kconfig / defconfig updates Stefan Roese
2020-07-30 11:56 ` [PATCH v2 06/10] drivers: spi: Add SPI controller driver for Octeon Stefan Roese
2020-08-03 19:09   ` Daniel Schwierzeck
2020-08-03 23:20   ` Suneel Garapati
2020-08-04 12:54     ` Stefan Roese

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=af15c11a2657009f45e9d4cec4c8d8e58a6cb27c.camel@gmail.com \
    --to=daniel.schwierzeck@gmail.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.