On Fri, Sep 23, 2016 at 6:54 PM, Stefan Roese <s...@denx.de> wrote: > On 23.09.2016 14:53, Jagan Teki wrote: >> >> 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. > > > Yes, hz should be used instead of plat->frequency here. Will fix > in v3. Thanks for noticing. > > >>> + >>> + 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. > > > This is the input clock to the SPI IP core in the SoC. And > in most drivers I've seen so far, this frequency is configured > via some macros / defines. E.g.: > > CONFIG_SYS_TCLK > CONFIG_CQSPI_REF_CLK > ... > > Best would be if this SPI input clock could be read via some > clock driver infrastructure. But this is something that we > don't have for MVEBU in U-Boot yet. So I can either switch back > to some define for this value here or leave it as the DT property > for now.
OK, use the dt and add FIXME with some proper comment. 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