From mboxrd@z Thu Jan 1 00:00:00 1970 From: Wenyou Yang Date: Tue, 25 Jul 2017 15:01:02 +0800 Subject: [U-Boot] [PATCH v3 8/8] sf: add driver for Atmel QSPI controller In-Reply-To: <20170725070102.1344-1-wenyou.yang@microchip.com> References: <20170725070102.1344-1-wenyou.yang@microchip.com> Message-ID: <20170725070102.1344-9-wenyou.yang@microchip.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de From: Cyrille Pitchen This patch adds support to the Atmel Quad SPI controller. Signed-off-by: Cyrille Pitchen Signed-off-by: Wenyou Yang --- 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 +#include +#include +#include +#include +#include +#include +#include +#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 = 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, + .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 0000000000..ee1a14bd72 --- /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 + +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__ */ -- 2.13.0