On 21.09.2016 09:56, Jagan Teki wrote: > On Fri, Sep 16, 2016 at 6:39 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> >> --- >> drivers/spi/Kconfig | 7 ++ >> drivers/spi/Makefile | 1 + >> drivers/spi/mvebu_a3700_spi.c | 281 >> ++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 289 insertions(+) >> create mode 100644 drivers/spi/mvebu_a3700_spi.c >> >> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig >> index aca385d..bc5e716 100644 >> --- a/drivers/spi/Kconfig >> +++ b/drivers/spi/Kconfig >> @@ -75,6 +75,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..26f9a8f >> --- /dev/null >> +++ b/drivers/spi/mvebu_a3700_spi.c >> @@ -0,0 +1,281 @@ >> +/* >> + * 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; >> +}; >> + >> +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); > > Do we have any documentation in data sheet or something for this 100 ms?
No, not really. >> + 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) */ >> + /* ToDo: get real values for clock and frequency */ >> + data |= 160000 / 40000; > > Better to get these from dt atleast for now and also please set the > freq/baud value by evaluating using input 'hz' Okay. Updated for v2 - coming soon... Thanks, Stefan _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot