
On 2017/5/19 22:59, Cyrille Pitchen wrote:
This patch adds support to the Atmel Quad SPI controller.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com
Acked-by Wenyou Yang wenyou.yang@atmel.com
Best Regards, Wenyou Yang
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 f3f7dbe0897b..73f2e5c26bfb 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 c090562c7732..af75fa41c82f 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 000000000000..6c265d0a4714 --- /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;
+}
+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 = bus->of_offset;
- 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,
- .of_match = atmel_qspi_ids,
- .ops = &atmel_qspi_ops,
- .ofdata_to_platdata = atmel_qspi_ofdata_to_platdata,
- .platdata_auto_alloc_size = sizeof(struct atmel_qspi_platdata),
- .priv_auto_alloc_size = sizeof(struct atmel_qspi_priv),
- .probe = atmel_qspi_probe,
+}; diff --git a/drivers/spi/atmel_qspi.h b/drivers/spi/atmel_qspi.h new file mode 100644 index 000000000000..ee1a14bd726d --- /dev/null +++ b/drivers/spi/atmel_qspi.h @@ -0,0 +1,169 @@ +/*
- Copyright (C) 2016
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __ATMEL_QSPI_H__ +#define __ATMEL_QSPI_H__
+/*
- Register Definitions
- */
+#define QSPI_CR 0x00 /* Control Register */ +#define QSPI_MR 0x04 /* Mode Register */ +#define QSPI_RDR 0x08 /* Receive Data Register */ +#define QSPI_TDR 0x0c /* Transmit Data Register */ +#define QSPI_SR 0x10 /* Status Register */ +#define QSPI_IER 0x14 /* Interrupt Enable Register */ +#define QSPI_IDR 0x18 /* Interrupt Disable Register */ +#define QSPI_IMR 0x1c /* Interrupt Mask Register */ +#define QSPI_SCR 0x20 /* Serial Clock Register */ +#define QSPI_IAR 0x30 /* Instruction Address Register */ +#define QSPI_ICR 0x34 /* Instruction Code Register */ +#define QSPI_IFR 0x38 /* Instruction Frame Register */ +/* 0x3c Reserved */ +#define QSPI_SMR 0x40 /* Scrambling Mode Register */ +#define QSPI_SKR 0x44 /* Scrambling Key Register */ +/* 0x48 ~ 0xe0 */ +#define QSPI_WPMR 0xe4 /* Write Protection Mode Register */ +#define QSPI_WPSR 0xe8 /* Write Protection Status Register */ +/* 0xec ~ 0xf8 Reserved */ +/* 0xfc Reserved */
+/*
- Register Field Definitions
- */
+/* QSPI_CR */ +#define QSPI_CR_QSPIEN BIT(0) /* QSPI Enable */ +#define QSPI_CR_QSPIDIS BIT(1) /* QSPI Disable */ +#define QSPI_CR_SWRST BIT(7) /* QSPI Software Reset */ +#define QSPI_CR_LASTXFER BIT(24) /* Last Transfer */
+/* QSPI_MR */ +#define QSPI_MR_SMM BIT(0) /* Serial Memort Mode */ +#define QSPI_MR_SMM_SPI 0 +#define QSPI_MR_SMM_MEMORY QSPI_MR_SMM +#define QSPI_MR_LLB BIT(1) /* Local Localback Enable */ +#define QSPI_MR_LLB_DISABLED 0 +#define QSPI_MR_LLB_ENABLED QSPI_MR_LLB +#define QSPI_MR_WDRBT BIT(2) /* Wait Data Read Before Transfer */ +#define QSPI_MR_WDRBT_DISABLED 0 +#define QSPI_MR_WDRBT_ENABLED QSPI_MR_WDRBT +#define QSPI_MR_SMRM BIT(3) /* Serial Memory Register Mode */ +#define QSPI_MR_SMRM_AHB 0 +#define QSPI_MR_SMRM_APB QSPI_MR_SMRM +#define QSPI_MR_CSMODE GENMASK(5, 4) /* Chip Select Mode */ +#define QSPI_MR_CSMODE_NOT_RELOADED (0x0u << 4) +#define QSPI_MR_CSMODE_LASTXFER (0x1u << 4) +#define QSPI_MR_CSMODE_SYSTEMATICALLY (0x2u << 4) +#define QSPI_MR_NBBITS GENMASK(11, 8) /*
* Number of Bits Per
* Transfer
*/
+#define QSPI_MR_NBBITS_8_BIT (0x0u << 8) +#define QSPI_MR_NBBITS_16_BIT (0x8u << 8) +#define QSPI_MR_DLYBCT GENMASK(23, 16) /*
* Delay Between Consecutive
* Transfers
*/
+#define QSPI_MR_DLYCS GENMASK(31, 24) /* Minimum Inactive QCS Delay */
+/* QSPI_SR */ +#define QSPI_SR_RDRF BIT(0) /* Receive Data Register Full */ +#define QSPI_SR_TDRE BIT(1) /* Transmit Data Register Empty */ +#define QSPI_SR_TXEMPTY BIT(2) /* Transmission Registers Empty */ +#define QSPI_SR_OVRES BIT(3) /* Overrun Error Status */ +#define QSPI_SR_CSR BIT(8) /* Chip Select Rise */ +#define QSPI_SR_CSS BIT(9) /* Chip Select Status */ +#define QSPI_SR_INSTRE BIT(10) /* Instruction End Status */ +#define QSPI_SR_QSPIENS BIT(24) /* QSPI Enable Status */
+/* QSPI_SCR */ +#define QSPI_SCR_CPOL BIT(0) /* Clock Polarity */ +#define QSPI_SCR_CPOL_(x) ((x) << 0) +#define QSPI_SCR_CPHA BIT(1) /* Clock Phase */ +#define QSPI_SCR_CPHA_(x) ((x) << 1) +#define QSPI_SCR_SCBR GENMASK(15, 8) /* Serial Clock Baud Rate */ +#define QSPI_SCR_SCBR_(x) (((x) << 8) & QSPI_SCR_SCBR) +#define QSPI_SCR_DLYBS GENMASK(23, 16) +#define QSPI_SCR_DLYBS_(x) (((x) << 16) & QSPI_SCR_DLYBS) /*
* Delay Before
* QSCK
*/
+/* QSPI_ICR */ +#define QSPI_ICR_INST GENMASK(7, 0) +#define QSPI_ICR_INST_(x) (((x) << 0) & QSPI_ICR_INST) /*
* Instruction
* Code
*/
+#define QSPI_ICR_OPT GENMASK(23, 16) +#define QSPI_ICR_OPT_(x) (((x) << 16) & QSPI_ICR_OPT) /*
* Option
* Code
*/
+/* QSPI_IFR */ +#define QSPI_IFR_WIDTH GENMASK(2, 0) /*
* Width of Instruction Code,
* Address, Option Code and Data
*/
+#define QSPI_IFR_WIDTH_SINGLE_BIT_SPI (0x0u << 0) +#define QSPI_IFR_WIDTH_DUAL_OUTPUT (0x1u << 0) +#define QSPI_IFR_WIDTH_QUAD_OUTPUT (0x2u << 0) +#define QSPI_IFR_WIDTH_DUAL_IO (0x3u << 0) +#define QSPI_IFR_WIDTH_QUAD_IO (0x4u << 0) +#define QSPI_IFR_WIDTH_DUAL_CMD (0x5u << 0) +#define QSPI_IFR_WIDTH_QUAD_CMD (0x6u << 0) +#define QSPI_IFR_WIDTH_(x) (((x) << 0) & QSPI_IFR_WIDTH) +#define QSPI_IFR_INSTEN BIT(4) /* Instruction Enable*/ +#define QSPI_IFR_ADDREN BIT(5) /* Address Enable*/ +#define QSPI_IFR_OPTEN BIT(6) /* Option Enable*/ +#define QSPI_IFR_DATAEN BIT(7) /* Data Enable*/ +#define QSPI_IFR_OPTL GENMASK(9, 8) /* Option Code Length */ +#define QSPI_IFR_OPTL_1BIT (0x0u << 8) +#define QSPI_IFR_OPTL_2BIT (0x1u << 8) +#define QSPI_IFR_OPTL_4BIT (0x2u << 8) +#define QSPI_IFR_OPTL_8BIT (0x3u << 8) +#define QSPI_IFR_ADDRL BIT(10) /* Address Length */ +#define QSPI_IFR_ADDRL_24_BIT 0 +#define QSPI_IFR_ADDRL_32_BIT QSPI_IFR_ADDRL +#define QSPI_IFR_TFRTYPE GENMASK(13, 12) /* Data Transfer Type */ +#define QSPI_IFR_TFRTYPE_READ (0x0u << 12) +#define QSPI_IFR_TFRTYPE_READ_MEMORY (0x1u << 12) +#define QSPI_IFR_TFRTYPE_WRITE (0x2u << 12) +#define QSPI_IFR_TFRTYPE_WRITE_MEMORY (0x3u << 12) +#define QSPI_IFR_TFRTYPE_(x) (((x) << 12) & QSPI_IFR_TFRTYPE) +#define QSPI_IFR_CRM BIT(14) /* Continuous Read Mode */ +#define QSPI_IFR_NBDUM GENMASK(20, 16) +#define QSPI_IFR_NBDUM_(x) (((x) << 16) & QSPI_IFR_NBDUM) /*
* Number Of
* Dummy Cycles
*/
+struct atmel_qspi_platdata {
- void *regbase;
- void *membase;
+};
+struct atmel_qspi_priv {
- ulong bus_clk_rate;
- void *regbase;
- void *membase;
+};
+#include <asm/io.h>
+static inline u32 qspi_readl(struct atmel_qspi_priv *aq, u32 reg) +{
- return readl(aq->regbase + reg);
+}
+static inline void qspi_writel(struct atmel_qspi_priv *aq, u32 reg, u32 value) +{
- writel(value, aq->regbase + reg);
+}
+#endif /* __ATMEL_QSPI_H__ */