On Fri, Sep 23, 2016 at 5:47 PM, Stefan Roese <s...@denx.de> wrote: > The SPI IP core in the Marvell Armada 3700 is similar to the one in the > other Armada SoCs. But the differences are big enough that it makes > sense to introduce a new driver instead of cluttering the old > kirkwood driver with #ifdef's. > > Signed-off-by: Stefan Roese <s...@denx.de> > Cc: Nadav Haklai <nad...@marvell.com> > Cc: Kostya Porotchkin <kos...@marvell.com> > Cc: Wilson Ding <ding...@marvell.com> > Cc: Victor Gu <x...@marvell.com> > Cc: Hua Jing <jing...@marvell.com> > Cc: Terry Zhou <bjz...@marvell.com> > Cc: Hanna Hawa <han...@marvell.com> > Cc: Haim Boot <ha...@marvell.com> > Cc: Jagan Teki <jt...@openedev.com> > --- > v2: > - Evaluated hz and used values provided by the DT in mvebu_spi_set_speed() > as suggested by Jagan > > drivers/spi/Kconfig | 7 + > drivers/spi/Makefile | 1 + > drivers/spi/mvebu_a3700_spi.c | 289 > ++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 297 insertions(+) > create mode 100644 drivers/spi/mvebu_a3700_spi.c > > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig > index 5da66a6..8724f87 100644 > --- a/drivers/spi/Kconfig > +++ b/drivers/spi/Kconfig > @@ -68,6 +68,13 @@ config ICH_SPI > access the SPI NOR flash on platforms embedding this Intel > ICH IP core. > > +config MVEBU_A3700_SPI > + bool "Marvell Armada 3700 SPI driver" > + help > + Enable the Marvell Armada 3700 SPI driver. This driver can be > + used to access the SPI NOR flash on platforms embedding this > + Marvell IP core. > + > config PIC32_SPI > bool "Microchip PIC32 SPI driver" > depends on MACH_PIC32 > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile > index b1d9e20..247c5f6 100644 > --- a/drivers/spi/Makefile > +++ b/drivers/spi/Makefile > @@ -37,6 +37,7 @@ obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o > obj-$(CONFIG_LPC32XX_SSP) += lpc32xx_ssp.o > obj-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o > obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o > +obj-$(CONFIG_MVEBU_A3700_SPI) += mvebu_a3700_spi.o > obj-$(CONFIG_MXC_SPI) += mxc_spi.o > obj-$(CONFIG_MXS_SPI) += mxs_spi.o > obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o > diff --git a/drivers/spi/mvebu_a3700_spi.c b/drivers/spi/mvebu_a3700_spi.c > new file mode 100644 > index 0000000..b566e03 > --- /dev/null > +++ b/drivers/spi/mvebu_a3700_spi.c > @@ -0,0 +1,289 @@ > +/* > + * Copyright (C) 2015 Marvell International Ltd. > + * > + * Copyright (C) 2016 Stefan Roese <s...@denx.de> > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <common.h> > +#include <dm.h> > +#include <malloc.h> > +#include <spi.h> > +#include <wait_bit.h> > +#include <asm/io.h> > + > +DECLARE_GLOBAL_DATA_PTR; > + > +#define SPI_TIMEOUT 10000 > + > +#define MVEBU_SPI_A3700_XFER_RDY BIT(1) > +#define MVEBU_SPI_A3700_FIFO_FLUSH BIT(9) > +#define MVEBU_SPI_A3700_BYTE_LEN BIT(5) > +#define MVEBU_SPI_A3700_CLK_PHA BIT(6) > +#define MVEBU_SPI_A3700_CLK_POL BIT(7) > +#define MVEBU_SPI_A3700_FIFO_EN BIT(17) > +#define MVEBU_SPI_A3700_SPI_EN_0 BIT(16) > +#define MVEBU_SPI_A3700_CLK_PRESCALE_BIT 0 > +#define MVEBU_SPI_A3700_CLK_PRESCALE_MASK \ > + (0x1f << MVEBU_SPI_A3700_CLK_PRESCALE_BIT) > + > +/* SPI registers */ > +struct spi_reg { > + u32 ctrl; /* 0x10600 */ > + u32 cfg; /* 0x10604 */ > + u32 dout; /* 0x10608 */ > + u32 din; /* 0x1060c */ > +}; > + > +struct mvebu_spi_platdata { > + struct spi_reg *spireg; > + unsigned int frequency; > + unsigned int clock; > +}; > + > +static void spi_cs_activate(struct spi_reg *reg, int cs) > +{ > + setbits_le32(®->ctrl, MVEBU_SPI_A3700_SPI_EN_0 << cs); > +} > + > +static void spi_cs_deactivate(struct spi_reg *reg, int cs) > +{ > + clrbits_le32(®->ctrl, MVEBU_SPI_A3700_SPI_EN_0 << cs); > +} > + > +/** > + * spi_legacy_shift_byte() - triggers the real SPI transfer > + * @bytelen: Indicate how many bytes to transfer. > + * @dout: Buffer address of what to send. > + * @din: Buffer address of where to receive. > + * > + * This function triggers the real SPI transfer in legacy mode. It > + * will shift out char buffer from @dout, and shift in char buffer to > + * @din, if necessary. > + * > + * This function assumes that only one byte is shifted at one time. > + * However, it is not its responisbility to set the transfer type to > + * one-byte. Also, it does not guarantee that it will work if transfer > + * type becomes two-byte. See spi_set_legacy() for details. > + * > + * In legacy mode, simply write to the SPI_DOUT register will trigger > + * the transfer. > + * > + * If @dout == NULL, which means no actual data needs to be sent out, > + * then the function will shift out 0x00 in order to shift in data. > + * The XFER_RDY flag is checked every time before accessing SPI_DOUT > + * and SPI_DIN register. > + * > + * The number of transfers to be triggerred is decided by @bytelen. > + * > + * Return: 0 - cool > + * -ETIMEDOUT - XFER_RDY flag timeout > + */ > +static int spi_legacy_shift_byte(struct spi_reg *reg, unsigned int bytelen, > + const void *dout, void *din) > +{ > + const u8 *dout_8; > + u8 *din_8; > + int ret; > + > + /* Use 0x00 as dummy dout */ > + const u8 dummy_dout = 0x0; > + u32 pending_dout = 0x0; > + > + /* dout_8: pointer of current dout */ > + dout_8 = dout; > + /* din_8: pointer of current din */ > + din_8 = din; > + > + while (bytelen) { > + ret = wait_for_bit(__func__, ®->ctrl, > + MVEBU_SPI_A3700_XFER_RDY, true, 100, > false); > + if (ret) > + return ret; > + > + if (dout) > + pending_dout = (u32)*dout_8; > + else > + pending_dout = (u32)dummy_dout; > + > + /* Trigger the xfer */ > + writel(pending_dout, ®->dout); > + > + if (din) { > + ret = wait_for_bit(__func__, ®->ctrl, > + MVEBU_SPI_A3700_XFER_RDY, > + true, 100, false); > + if (ret) > + return ret; > + > + /* Read what is transferred in */ > + *din_8 = (u8)readl(®->din); > + } > + > + /* Don't increment the current pointer if NULL */ > + if (dout) > + dout_8++; > + if (din) > + din_8++; > + > + bytelen--; > + } > + > + return 0; > +} > + > +static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen, > + const void *dout, void *din, unsigned long flags) > +{ > + struct udevice *bus = dev->parent; > + struct mvebu_spi_platdata *plat = dev_get_platdata(bus); > + struct spi_reg *reg = plat->spireg; > + unsigned int bytelen; > + int ret; > + > + bytelen = bitlen / 8; > + > + if (dout && din) > + debug("This is a duplex transfer.\n"); > + > + /* Activate CS */ > + if (flags & SPI_XFER_BEGIN) { > + debug("SPI: activate cs.\n"); > + spi_cs_activate(reg, spi_chip_select(dev)); > + } > + > + /* Send and/or receive */ > + if (dout || din) { > + ret = spi_legacy_shift_byte(reg, bytelen, dout, din); > + if (ret) > + return ret; > + } > + > + /* Deactivate CS */ > + if (flags & SPI_XFER_END) { > + ret = wait_for_bit(__func__, ®->ctrl, > + MVEBU_SPI_A3700_XFER_RDY, true, 100, > false); > + if (ret) > + return ret; > + > + debug("SPI: deactivate cs.\n"); > + spi_cs_deactivate(reg, spi_chip_select(dev)); > + } > + > + return 0; > +} > + > +static int mvebu_spi_set_speed(struct udevice *bus, uint hz) > +{ > + struct mvebu_spi_platdata *plat = dev_get_platdata(bus); > + struct spi_reg *reg = plat->spireg; > + u32 data; > + > + data = readl(®->cfg); > + > + /* Set Prescaler */ > + data &= ~MVEBU_SPI_A3700_CLK_PRESCALE_MASK; > + > + /* Calculate Prescaler = (spi_input_freq / spi_max_freq) */ > + if (hz > plat->frequency) > + hz = plat->frequency; > + data |= plat->clock / plat->frequency;
Need to use the hz to compute here. > + > + writel(data, ®->cfg); > + > + return 0; > +} > + > +static int mvebu_spi_set_mode(struct udevice *bus, uint mode) > +{ > + struct mvebu_spi_platdata *plat = dev_get_platdata(bus); > + struct spi_reg *reg = plat->spireg; > + > + /* > + * Set SPI polarity > + * 0: Serial interface clock is low when inactive > + * 1: Serial interface clock is high when inactive > + */ > + if (mode & SPI_CPOL) > + setbits_le32(®->cfg, MVEBU_SPI_A3700_CLK_POL); > + else > + clrbits_le32(®->cfg, MVEBU_SPI_A3700_CLK_POL); > + if (mode & SPI_CPHA) > + setbits_le32(®->cfg, MVEBU_SPI_A3700_CLK_PHA); > + else > + clrbits_le32(®->cfg, MVEBU_SPI_A3700_CLK_PHA); > + > + return 0; > +} > + > +static int mvebu_spi_probe(struct udevice *bus) > +{ > + struct mvebu_spi_platdata *plat = dev_get_platdata(bus); > + struct spi_reg *reg = plat->spireg; > + u32 data; > + int ret; > + > + /* > + * Settings SPI controller to be working in legacy mode, which > + * means use only DO pin (I/O 1) for Data Out, and DI pin (I/O 0) > + * for Data In. > + */ > + > + /* Flush read/write FIFO */ > + data = readl(®->cfg); > + writel(data | MVEBU_SPI_A3700_FIFO_FLUSH, ®->cfg); > + ret = wait_for_bit(__func__, ®->cfg, MVEBU_SPI_A3700_FIFO_FLUSH, > + false, 1000, false); > + if (ret) > + return ret; > + > + /* Disable FIFO mode */ > + data &= ~MVEBU_SPI_A3700_FIFO_EN; > + > + /* Always shift 1 byte at a time */ > + data &= ~MVEBU_SPI_A3700_BYTE_LEN; > + > + writel(data, ®->cfg); > + > + return 0; > +} > + > +static int mvebu_spi_ofdata_to_platdata(struct udevice *bus) > +{ > + struct mvebu_spi_platdata *plat = dev_get_platdata(bus); > + > + plat->spireg = (struct spi_reg *)dev_get_addr(bus); > + > + plat->clock = fdtdec_get_int(gd->fdt_blob, bus->of_offset, > + "clock-frequency", 160000); I think this is not related to spi, global clock is it? usually we can't use clock-frequency we set the clock with the help of spi-max-frequency. thanks! -- Jagan Teki Free Software Engineer | www.openedev.com U-Boot, Linux | Upstream Maintainer Hyderabad, India. _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot