On Tue, Jul 25, 2017 at 12:31 PM, Wenyou Yang <wenyou.y...@microchip.com> wrote: > From: Cyrille Pitchen <cyrille.pitc...@atmel.com> > > This patch adds support to the Atmel Quad SPI controller.
So, this is SPI flash driver not Generic SPI - the problem with these were resides at drivers/spi is entire SPI layer becomes SPI-flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operation. > > Signed-off-by: Cyrille Pitchen <cyrille.pitc...@atmel.com> > Signed-off-by: Wenyou Yang <wenyou.y...@microchip.com> > --- > > Changes in v3: None > Changes in v2: > - Rebase on the latest u-boot/master(2710d54f5). > > drivers/spi/Kconfig | 7 + > drivers/spi/Makefile | 1 + > drivers/spi/atmel_qspi.c | 404 > +++++++++++++++++++++++++++++++++++++++++++++++ > drivers/spi/atmel_qspi.h | 169 ++++++++++++++++++++ > 4 files changed, 581 insertions(+) > create mode 100644 drivers/spi/atmel_qspi.c > create mode 100644 drivers/spi/atmel_qspi.h > > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig > index 8a8e8e480f..b3e8cb941e 100644 > --- a/drivers/spi/Kconfig > +++ b/drivers/spi/Kconfig > @@ -32,6 +32,13 @@ config ATH79_SPI > uses driver model and requires a device tree binding to operate. > please refer to doc/device-tree-bindings/spi/spi-ath79.txt. > > +config ATMEL_QSPI > + bool "Atmel QSPI driver" > + depends on ARCH_AT91 > + help > + Enable the Ateml Quad-SPI (QSPI) driver. This driver can only be > + used to access SPI NOR flashes. > + > config ATMEL_SPI > bool "Atmel SPI driver" > depends on ARCH_AT91 > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile > index 9f8b86de76..358db2b065 100644 > --- a/drivers/spi/Makefile > +++ b/drivers/spi/Makefile > @@ -18,6 +18,7 @@ endif > obj-$(CONFIG_ALTERA_SPI) += altera_spi.o > obj-$(CONFIG_ATH79_SPI) += ath79_spi.o > obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o > +obj-$(CONFIG_ATMEL_QSPI) += atmel_qspi.o > obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o > obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o > obj-$(CONFIG_CF_SPI) += cf_spi.o > diff --git a/drivers/spi/atmel_qspi.c b/drivers/spi/atmel_qspi.c > new file mode 100644 > index 0000000000..0af7a4dc0a > --- /dev/null > +++ b/drivers/spi/atmel_qspi.c > @@ -0,0 +1,404 @@ > +/* > + * Copyright (C) 2017 Atmel Corporation > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > +#include <common.h> > +#include <clk.h> > +#include <dm.h> > +#include <fdtdec.h> > +#include <errno.h> > +#include <spi.h> > +#include <asm/io.h> > +#include <mach/clk.h> > +#include "atmel_qspi.h" > + > +DECLARE_GLOBAL_DATA_PTR; > + > +static void atmel_qspi_memcpy_fromio(void *dst, unsigned long src, size_t > len) > +{ > + u8 *d = (u8 *)dst; > + > + while (len--) { > + *d++ = readb(src); > + src++; > + } > +} > + > +static void atmel_qspi_memcpy_toio(unsigned long dst, const void *src, > + size_t len) > +{ > + const u8 *s = (const u8 *)src; > + > + while (len--) { > + writeb(*s, dst); > + dst++; > + s++; > + } > +} > + > +static int atmel_qspi_set_ifr_tfrtype(u8 flags, u32 *ifr) > +{ > + u32 ifr_tfrtype; > + > + switch (flags & SPI_FCMD_TYPE) { > + case SPI_FCMD_READ: > + ifr_tfrtype = QSPI_IFR_TFRTYPE_READ_MEMORY; > + break; > + > + case SPI_FCMD_WRITE: > + ifr_tfrtype = QSPI_IFR_TFRTYPE_WRITE_MEMORY; > + break; > + > + case SPI_FCMD_ERASE: > + case SPI_FCMD_WRITE_REG: > + ifr_tfrtype = QSPI_IFR_TFRTYPE_WRITE; > + break; > + > + case SPI_FCMD_READ_REG: > + ifr_tfrtype = QSPI_IFR_TFRTYPE_READ; > + break; > + > + default: > + return -EINVAL; > + } > + > + *ifr = (*ifr & ~QSPI_IFR_TFRTYPE) | ifr_tfrtype; > + return 0; > +} > + > +static int atmel_qpsi_set_ifr_width(enum spi_flash_protocol proto, u32 *ifr) > +{ > + u32 ifr_width; > + > + switch (proto) { > + case SPI_FPROTO_1_1_1: > + ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI; > + break; > + > + case SPI_FPROTO_1_1_2: > + ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT; > + break; > + > + case SPI_FPROTO_1_2_2: > + ifr_width = QSPI_IFR_WIDTH_DUAL_IO; > + break; > + > + case SPI_FPROTO_2_2_2: > + ifr_width = QSPI_IFR_WIDTH_DUAL_CMD; > + break; > + > + case SPI_FPROTO_1_1_4: > + ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT; > + break; > + > + case SPI_FPROTO_1_4_4: > + ifr_width = QSPI_IFR_WIDTH_QUAD_IO; > + break; > + > + case SPI_FPROTO_4_4_4: > + ifr_width = QSPI_IFR_WIDTH_QUAD_CMD; > + break; > + > + default: > + return -EINVAL; > + } > + > + *ifr = (*ifr & ~QSPI_IFR_WIDTH) | ifr_width; > + return 0; > +} > + > +static int atmel_qspi_xfer(struct udevice *dev, unsigned int bitlen, > + const void *dout, void *din, unsigned long flags) > +{ > + /* This controller can only be used with SPI NOR flashes. */ > + return -EINVAL; > +} > + > +static int atmel_qspi_set_speed(struct udevice *bus, uint hz) > +{ > + struct atmel_qspi_priv *aq = dev_get_priv(bus); > + u32 scr, scbr, mask, new_value; > + > + /* Compute the QSPI baudrate */ > + scbr = DIV_ROUND_UP(aq->bus_clk_rate, hz); > + if (scbr > 0) > + scbr--; > + > + new_value = QSPI_SCR_SCBR_(scbr); > + mask = QSPI_SCR_SCBR; > + > + scr = qspi_readl(aq, QSPI_SCR); > + if ((scr & mask) == new_value) > + return 0; > + > + scr = (scr & ~mask) | new_value; > + qspi_writel(aq, QSPI_SCR, scr); > + > + return 0; > +} > + > +static int atmel_qspi_set_mode(struct udevice *bus, uint mode) > +{ > + struct atmel_qspi_priv *aq = dev_get_priv(bus); > + u32 scr, mask, new_value; > + > + new_value = (QSPI_SCR_CPOL_((mode & SPI_CPOL) != 0) | > + QSPI_SCR_CPHA_((mode & SPI_CPHA) != 0)); > + mask = (QSPI_SCR_CPOL | QSPI_SCR_CPHA); > + > + scr = qspi_readl(aq, QSPI_SCR); > + if ((scr & mask) == new_value) > + return 0; > + > + scr = (scr & ~mask) | new_value; > + qspi_writel(aq, QSPI_SCR, scr); > + > + return 0; > +} > + > +static bool > +atmel_qspi_is_flash_command_supported(struct udevice *dev, > + const struct spi_flash_command *cmd) > +{ > + return true; > +} > + > +static int atmel_qspi_exec_flash_command(struct udevice *dev, > + const struct spi_flash_command *cmd) > +{ > + struct udevice *bus = dev_get_parent(dev); > + struct atmel_qspi_priv *aq = dev_get_priv(bus); > + unsigned int iar, icr, ifr; > + unsigned int offset; > + unsigned int imr, sr; > + unsigned long memaddr; > + int err; > + > + iar = 0; > + icr = 0; > + ifr = 0; > + > + err = atmel_qspi_set_ifr_tfrtype(cmd->flags, &ifr); > + if (err) > + return err; > + > + err = atmel_qpsi_set_ifr_width(cmd->proto, &ifr); > + if (err) > + return err; > + > + /* Compute instruction parameters */ > + icr |= QSPI_ICR_INST_(cmd->inst); > + ifr |= QSPI_IFR_INSTEN; > + > + /* Compute address parameters. */ > + switch (cmd->addr_len) { > + case 4: > + ifr |= QSPI_IFR_ADDRL_32_BIT; > + /*break;*/ /* fall through the 24bit (3 byte) address case */ > + case 3: > + iar = cmd->data_len ? 0 : cmd->addr; > + ifr |= QSPI_IFR_ADDREN; > + offset = cmd->addr; > + break; > + case 0: > + offset = 0; > + break; > + default: > + return -EINVAL; > + } > + > + /* Compute option parameters. */ > + if (cmd->num_mode_cycles) { > + unsigned int mode_cycle_bits, mode_bits; > + > + icr |= QSPI_ICR_OPT_(cmd->mode); > + ifr |= QSPI_IFR_OPTEN; > + > + switch (ifr & QSPI_IFR_WIDTH) { > + case QSPI_IFR_WIDTH_SINGLE_BIT_SPI: > + case QSPI_IFR_WIDTH_DUAL_OUTPUT: > + case QSPI_IFR_WIDTH_QUAD_OUTPUT: > + mode_cycle_bits = 1; > + break; > + case QSPI_IFR_WIDTH_DUAL_IO: > + case QSPI_IFR_WIDTH_DUAL_CMD: > + mode_cycle_bits = 2; > + break; > + case QSPI_IFR_WIDTH_QUAD_IO: > + case QSPI_IFR_WIDTH_QUAD_CMD: > + mode_cycle_bits = 4; > + break; > + default: > + return -EINVAL; > + } > + > + mode_bits = cmd->num_mode_cycles * mode_cycle_bits; > + switch (mode_bits) { > + case 1: > + ifr |= QSPI_IFR_OPTL_1BIT; > + break; > + > + case 2: > + ifr |= QSPI_IFR_OPTL_2BIT; > + break; > + > + case 4: > + ifr |= QSPI_IFR_OPTL_4BIT; > + break; > + > + case 8: > + ifr |= QSPI_IFR_OPTL_8BIT; > + break; > + > + default: > + return -EINVAL; > + } > + } > + > + /* Set the number of dummy cycles. */ > + if (cmd->num_wait_states) > + ifr |= QSPI_IFR_NBDUM_(cmd->num_wait_states); > + > + /* Set data enable. */ > + if (cmd->data_len) > + ifr |= QSPI_IFR_DATAEN; > + > + /* Clear pending interrupts. */ > + (void)qspi_readl(aq, QSPI_SR); > + > + /* Set QSPI Instruction Frame registers. */ > + qspi_writel(aq, QSPI_IAR, iar); > + qspi_writel(aq, QSPI_ICR, icr); > + qspi_writel(aq, QSPI_IFR, ifr); > + > + /* Skip to the final steps if there is no data. */ > + if (!cmd->data_len) > + goto no_data; > + > + /* Dummy read of QSPI_IFR to synchronize APB and AHB accesses. */ > + (void)qspi_readl(aq, QSPI_IFR); > + > + /* Stop here for Continuous Read. */ > + memaddr = (unsigned long)(aq->membase + offset); > + if (cmd->tx_data) > + /* Write data. */ > + atmel_qspi_memcpy_toio(memaddr, cmd->tx_data, cmd->data_len); > + else if (cmd->rx_data) > + /* Read data. */ > + atmel_qspi_memcpy_fromio(cmd->rx_data, memaddr, > cmd->data_len); > + > + /* Release the chip-select. */ > + qspi_writel(aq, QSPI_CR, QSPI_CR_LASTXFER); > + > +no_data: > + /* Poll INSTruction End and Chip Select Rise flags. */ > + imr = QSPI_SR_INSTRE | QSPI_SR_CSR; > + sr = 0; > + while (sr != (QSPI_SR_INSTRE | QSPI_SR_CSR)) > + sr |= qspi_readl(aq, QSPI_SR) & imr; > + > + return 0; > +} From v3 1/8 comments, this can be local function where we each IO opeartions can initiate this run command. > + > + > +static const struct dm_spi_ops atmel_qspi_ops = { > + .xfer = atmel_qspi_xfer, > + .set_speed = atmel_qspi_set_speed, > + .set_mode = atmel_qspi_set_mode, > + .is_flash_command_supported = > atmel_qspi_is_flash_command_supported, > + .exec_flash_command = atmel_qspi_exec_flash_command, > +}; > + > +static int atmel_qspi_enable_clk(struct udevice *bus) > +{ > + struct atmel_qspi_priv *aq = dev_get_priv(bus); > + struct clk clk; > + ulong clk_rate; > + int ret; > + > + ret = clk_get_by_index(bus, 0, &clk); > + if (ret) > + return -EINVAL; > + > + ret = clk_enable(&clk); > + if (ret) > + goto free_clock; > + > + clk_rate = clk_get_rate(&clk); > + if (!clk_rate) { > + ret = -EINVAL; > + goto free_clock; > + } > + > + aq->bus_clk_rate = clk_rate; > + > +free_clock: > + clk_free(&clk); > + > + return ret; > +} > + > +static int atmel_qspi_probe(struct udevice *bus) > +{ > + const struct atmel_qspi_platdata *plat = dev_get_platdata(bus); > + struct atmel_qspi_priv *aq = dev_get_priv(bus); > + u32 mr; > + int ret; > + > + ret = atmel_qspi_enable_clk(bus); > + if (ret) > + return ret; > + > + aq->regbase = plat->regbase; > + aq->membase = plat->membase; > + > + /* Reset the QSPI controler */ > + qspi_writel(aq, QSPI_CR, QSPI_CR_SWRST); > + > + /* Set the QSPI controller in Serial Memory Mode */ > + mr = (QSPI_MR_NBBITS_8_BIT | > + QSPI_MR_SMM_MEMORY | > + QSPI_MR_CSMODE_LASTXFER); > + qspi_writel(aq, QSPI_MR, mr); > + > + /* Enable the QSPI controller */ > + qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIEN); > + > + return 0; > +} > + > +static int atmel_qspi_ofdata_to_platdata(struct udevice *bus) > +{ > + struct atmel_qspi_platdata *plat = dev_get_platdata(bus); > + const void *blob = gd->fdt_blob; > + int node = dev_of_offset(bus); > + u32 data[4]; > + int ret; > + > + ret = fdtdec_get_int_array(blob, node, "reg", data, ARRAY_SIZE(data)); > + if (ret) { > + printf("Error: Can't get base addresses (ret=%d)!\n", ret); > + return -ENODEV; > + } > + plat->regbase = (void *)data[0]; > + plat->membase = (void *)data[2]; > + > + return 0; > +} > + > +static const struct udevice_id atmel_qspi_ids[] = { > + { .compatible = "atmel,sama5d2-qspi" }, > + { } > +}; > + > +U_BOOT_DRIVER(atmel_qspi) = { > + .name = "atmel_qspi", > + .id = UCLASS_SPI, Summary, better to write UCLASS_SPI_FLASH driver. 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 https://lists.denx.de/listinfo/u-boot