[PATCH] spi: atmel-quadspi: Add support for SAMA7G5 QSPI
Eugen.Hristev at microchip.com
Eugen.Hristev at microchip.com
Mon Dec 6 17:51:40 CET 2021
Hello Jagan,
Do you wish to review this patch before I take it through the at91 tree ?
I am skimming through the patches for the next cycle merge window.
Thanks,
Eugen
On 11/3/21 6:47 PM, Tudor Ambarus wrote:
> sama7g5 QSPI has:
> 1/ One Octal Serial Peripheral Interfaces (QSPI0) Supporting Up to
> 200 MHz DDR. Octal, TwinQuad, Hyperflash and OctaFlash Protocols Supported
> 2/ One Quad Serial Peripheral Interfaces (QSPI1) Supporting Up to
> 90 MHz DDR/133 MHz SDR
>
> The QSPI controller of SAMA7G5 uses different clock domains, hence extra
> synchronization operations must be performed before accessing some
> registers. Differentiate between the versions of the IP using has_gclk.
> Differentiate between QSPI0 and QSPI1 with has_octal.
>
> Signed-off-by: Tudor Ambarus <tudor.ambarus at microchip.com>
> ---
> drivers/spi/atmel-quadspi.c | 595 ++++++++++++++++++++++++++++++++++--
> 1 file changed, 571 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c
> index c8a68e6447..098298336d 100644
> --- a/drivers/spi/atmel-quadspi.c
> +++ b/drivers/spi/atmel-quadspi.c
> @@ -17,6 +17,7 @@
> #include <errno.h>
> #include <fdtdec.h>
> #include <dm/device_compat.h>
> +#include <linux/bitfield.h>
> #include <linux/bitops.h>
> #include <linux/err.h>
> #include <linux/io.h>
> @@ -32,6 +33,7 @@
> #define QSPI_RD 0x0008 /* Receive Data Register */
> #define QSPI_TD 0x000c /* Transmit Data Register */
> #define QSPI_SR 0x0010 /* Status Register */
> +#define QSPI_SR2 0x0024 /* SAMA7G5 Status Register */
> #define QSPI_IER 0x0014 /* Interrupt Enable Register */
> #define QSPI_IDR 0x0018 /* Interrupt Disable Register */
> #define QSPI_IMR 0x001c /* Interrupt Mask Register */
> @@ -46,6 +48,13 @@
> #define QSPI_SMR 0x0040 /* Scrambling Mode Register */
> #define QSPI_SKR 0x0044 /* Scrambling Key Register */
>
> +#define QSPI_REFRESH 0x0050 /* Refresh Register */
> +#define QSPI_WRACNT 0x0054 /* Write Access Counter Register */
> +#define QSPI_DLLCFG 0x0058 /* DLL Configuration Register */
> +#define QSPI_PCALCFG 0x005C /* Pad Calibration Configuration Register */
> +#define QSPI_PCALBP 0x0060 /* Pad Calibration Bypass Register */
> +#define QSPI_TOUT 0x0064 /* Timeout Register */
> +
> #define QSPI_WPMR 0x00E4 /* Write Protection Mode Register */
> #define QSPI_WPSR 0x00E8 /* Write Protection Status Register */
>
> @@ -54,7 +63,14 @@
> /* Bitfields in QSPI_CR (Control Register) */
> #define QSPI_CR_QSPIEN BIT(0)
> #define QSPI_CR_QSPIDIS BIT(1)
> +#define QSPI_CR_DLLON BIT(2)
> +#define QSPI_CR_DLLOFF BIT(3)
> +#define QSPI_CR_STPCAL BIT(4)
> +#define QSPI_CR_SRFRSH BIT(5)
> #define QSPI_CR_SWRST BIT(7)
> +#define QSPI_CR_UPDCFG BIT(8)
> +#define QSPI_CR_STTFR BIT(9)
> +#define QSPI_CR_RTOUT BIT(10)
> #define QSPI_CR_LASTXFER BIT(24)
>
> /* Bitfields in QSPI_MR (Mode Register) */
> @@ -62,12 +78,15 @@
> #define QSPI_MR_LLB BIT(1)
> #define QSPI_MR_WDRBT BIT(2)
> #define QSPI_MR_SMRM BIT(3)
> +#define QSPI_MR_DQSDLYEN BIT(3)
> +
> #define QSPI_MR_CSMODE_MASK GENMASK(5, 4)
> #define QSPI_MR_CSMODE_NOT_RELOADED (0 << 4)
> #define QSPI_MR_CSMODE_LASTXFER (1 << 4)
> #define QSPI_MR_CSMODE_SYSTEMATICALLY (2 << 4)
> #define QSPI_MR_NBBITS_MASK GENMASK(11, 8)
> #define QSPI_MR_NBBITS(n) ((((n) - 8) << 8) & QSPI_MR_NBBITS_MASK)
> +#define QSPI_MR_OENSD BIT(15)
> #define QSPI_MR_DLYBCT_MASK GENMASK(23, 16)
> #define QSPI_MR_DLYBCT(n) (((n) << 16) & QSPI_MR_DLYBCT_MASK)
> #define QSPI_MR_DLYCS_MASK GENMASK(31, 24)
> @@ -81,6 +100,13 @@
> #define QSPI_SR_CSR BIT(8)
> #define QSPI_SR_CSS BIT(9)
> #define QSPI_SR_INSTRE BIT(10)
> +#define QSPI_SR_LWRA BIT(11)
> +#define QSPI_SR_QITF BIT(12)
> +#define QSPI_SR_QITR BIT(13)
> +#define QSPI_SR_CSFA BIT(14)
> +#define QSPI_SR_CSRA BIT(15)
> +#define QSPI_SR_RFRSHD BIT(16)
> +#define QSPI_SR_TOUT BIT(17)
> #define QSPI_SR_QSPIENS BIT(24)
>
> #define QSPI_SR_CMD_COMPLETED (QSPI_SR_INSTRE | QSPI_SR_CSR)
> @@ -93,9 +119,22 @@
> #define QSPI_SCR_DLYBS_MASK GENMASK(23, 16)
> #define QSPI_SCR_DLYBS(n) (((n) << 16) & QSPI_SCR_DLYBS_MASK)
>
> +/* Bitfields in QSPI_SR2 (SAMA7G5 Status Register) */
> +#define QSPI_SR2_SYNCBSY BIT(0)
> +#define QSPI_SR2_QSPIENS BIT(1)
> +#define QSPI_SR2_CSS BIT(2)
> +#define QSPI_SR2_RBUSY BIT(3)
> +#define QSPI_SR2_HIDLE BIT(4)
> +#define QSPI_SR2_DLOCK BIT(5)
> +#define QSPI_SR2_CALBSY BIT(6)
> +
> +/* Bitfields in QSPI_IAR (Instruction Address Register) */
> +#define QSPI_IAR_ADDR GENMASK(31, 0)
> +
> /* Bitfields in QSPI_ICR (Read/Write Instruction Code Register) */
> #define QSPI_ICR_INST_MASK GENMASK(7, 0)
> #define QSPI_ICR_INST(inst) (((inst) << 0) & QSPI_ICR_INST_MASK)
> +#define QSPI_ICR_INST_MASK_SAMA7G5 GENMASK(15, 0)
> #define QSPI_ICR_OPT_MASK GENMASK(23, 16)
> #define QSPI_ICR_OPT(opt) (((opt) << 16) & QSPI_ICR_OPT_MASK)
>
> @@ -108,6 +147,9 @@
> #define QSPI_IFR_WIDTH_QUAD_IO (4 << 0)
> #define QSPI_IFR_WIDTH_DUAL_CMD (5 << 0)
> #define QSPI_IFR_WIDTH_QUAD_CMD (6 << 0)
> +#define QSPI_IFR_WIDTH_OCT_OUTPUT (7 << 0)
> +#define QSPI_IFR_WIDTH_OCT_IO (8 << 0)
> +#define QSPI_IFR_WIDTH_OCT_CMD (9 << 0)
> #define QSPI_IFR_INSTEN BIT(4)
> #define QSPI_IFR_ADDREN BIT(5)
> #define QSPI_IFR_OPTEN BIT(6)
> @@ -118,19 +160,60 @@
> #define QSPI_IFR_OPTL_4BIT (2 << 8)
> #define QSPI_IFR_OPTL_8BIT (3 << 8)
> #define QSPI_IFR_ADDRL BIT(10)
> +#define QSPI_IFR_ADDRL_SAMA7G5 GENMASK(11, 10)
> #define QSPI_IFR_TFRTYP_MEM BIT(12)
> #define QSPI_IFR_SAMA5D2_WRITE_TRSFR BIT(13)
> #define QSPI_IFR_CRM BIT(14)
> +#define QSPI_IFR_DDREN BIT(15)
> #define QSPI_IFR_NBDUM_MASK GENMASK(20, 16)
> #define QSPI_IFR_NBDUM(n) (((n) << 16) & QSPI_IFR_NBDUM_MASK)
> +#define QSPI_IFR_END BIT(22)
> +#define QSPI_IFR_SMRM BIT(23)
> #define QSPI_IFR_APBTFRTYP_READ BIT(24) /* Defined in SAM9X60 */
> +#define QSPI_IFR_DQSEN BIT(25)
> +#define QSPI_IFR_DDRCMDEN BIT(26)
> +#define QSPI_IFR_HFWBEN BIT(27)
> +#define QSPI_IFR_PROTTYP GENMASK(29, 28)
> +#define QSPI_IFR_PROTTYP_STD_SPI 0
> +#define QSPI_IFR_PROTTYP_TWIN_QUAD 1
> +#define QSPI_IFR_PROTTYP_OCTAFLASH 2
> +#define QSPI_IFR_PROTTYP_HYPERFLASH 3
>
> /* Bitfields in QSPI_SMR (Scrambling Mode Register) */
> #define QSPI_SMR_SCREN BIT(0)
> #define QSPI_SMR_RVDIS BIT(1)
> +#define QSPI_SMR_SCRKL BIT(2)
> +
> +/* Bitfields in QSPI_REFRESH (Refresh Register) */
> +#define QSPI_REFRESH_DELAY_COUNTER GENMASK(31, 0)
> +
> +/* Bitfields in QSPI_WRACNT (Write Access Counter Register) */
> +#define QSPI_WRACNT_NBWRA GENMASK(31, 0)
> +
> +/* Bitfields in QSPI_DLLCFG (DLL Configuration Register) */
> +#define QSPI_DLLCFG_RANGE BIT(0)
> +
> +/* Bitfields in QSPI_PCALCFG (DLL Pad Calibration Configuration Register) */
> +#define QSPI_PCALCFG_AAON BIT(0)
> +#define QSPI_PCALCFG_DAPCAL BIT(1)
> +#define QSPI_PCALCFG_DIFFPM BIT(2)
> +#define QSPI_PCALCFG_CLKDIV GENMASK(6, 4)
> +#define QSPI_PCALCFG_CALCNT GENMASK(16, 8)
> +#define QSPI_PCALCFG_CALP GENMASK(27, 24)
> +#define QSPI_PCALCFG_CALN GENMASK(31, 28)
> +
> +/* Bitfields in QSPI_PCALBP (DLL Pad Calibration Bypass Register) */
> +#define QSPI_PCALBP_BPEN BIT(0)
> +#define QSPI_PCALBP_CALPBP GENMASK(11, 8)
> +#define QSPI_PCALBP_CALNBP GENMASK(19, 16)
> +
> +/* Bitfields in QSPI_TOUT (Timeout Register) */
> +#define QSPI_TOUT_TCNTM GENMASK(15, 0)
>
> /* Bitfields in QSPI_WPMR (Write Protection Mode Register) */
> #define QSPI_WPMR_WPEN BIT(0)
> +#define QSPI_WPMR_WPITEN BIT(1)
> +#define QSPI_WPMR_WPCREN BIT(2)
> #define QSPI_WPMR_WPKEY_MASK GENMASK(31, 8)
> #define QSPI_WPMR_WPKEY(wpkey) (((wpkey) << 8) & QSPI_WPMR_WPKEY_MASK)
>
> @@ -139,21 +222,61 @@
> #define QSPI_WPSR_WPVSRC_MASK GENMASK(15, 8)
> #define QSPI_WPSR_WPVSRC(src) (((src) << 8) & QSPI_WPSR_WPVSRC)
>
> +#define ATMEL_QSPI_TIMEOUT 1000000 /* us */
> +#define ATMEL_QSPI_SYNC_TIMEOUT 300000 /* us */
> +#define QSPI_DLLCFG_THRESHOLD_FREQ 90000000U
> +#define QSPI_TOUT_MAX 0xffff
> +
> +/**
> + * struct atmel_qspi_pcal - Pad Calibration Clock Division
> + * @pclk_rate: peripheral clock rate.
> + * @pclkdiv: calibration clock division. The clock applied to the calibration
> + * cell is divided by pclkdiv + 1.
> + */
> +struct atmel_qspi_pcal {
> + u32 pclk_rate;
> + u8 pclk_div;
> +};
> +
> +#define ATMEL_QSPI_PCAL_ARRAY_SIZE 8
> +static const struct atmel_qspi_pcal pcal[ATMEL_QSPI_PCAL_ARRAY_SIZE] = {
> + {25000000, 0},
> + {50000000, 1},
> + {75000000, 2},
> + {100000000, 3},
> + {125000000, 4},
> + {150000000, 5},
> + {175000000, 6},
> + {200000000, 7},
> +};
> +
> struct atmel_qspi_caps {
> bool has_qspick;
> + bool has_gclk;
> bool has_ricr;
> + bool octal;
> };
>
> +struct atmel_qspi_priv_ops;
> +
> struct atmel_qspi {
> void __iomem *regs;
> void __iomem *mem;
> resource_size_t mmap_size;
> const struct atmel_qspi_caps *caps;
> + const struct atmel_qspi_priv_ops *ops;
> struct udevice *dev;
> ulong bus_clk_rate;
> u32 mr;
> };
>
> +struct atmel_qspi_priv_ops {
> + int (*set_cfg)(struct atmel_qspi *aq, const struct spi_mem_op *op,
> + u32 *offset);
> + int (*transfer)(struct atmel_qspi *aq, const struct spi_mem_op *op,
> + u32 offset);
> +};
> +
> struct atmel_qspi_mode {
> u8 cmd_buswidth;
> u8 addr_buswidth;
> @@ -171,6 +294,19 @@ static const struct atmel_qspi_mode atmel_qspi_modes[] = {
> { 4, 4, 4, QSPI_IFR_WIDTH_QUAD_CMD },
> };
>
> +static const struct atmel_qspi_mode atmel_qspi_sama7g5_modes[] = {
> + { 1, 1, 1, QSPI_IFR_WIDTH_SINGLE_BIT_SPI },
> + { 1, 1, 2, QSPI_IFR_WIDTH_DUAL_OUTPUT },
> + { 1, 1, 4, QSPI_IFR_WIDTH_QUAD_OUTPUT },
> + { 1, 2, 2, QSPI_IFR_WIDTH_DUAL_IO },
> + { 1, 4, 4, QSPI_IFR_WIDTH_QUAD_IO },
> + { 2, 2, 2, QSPI_IFR_WIDTH_DUAL_CMD },
> + { 4, 4, 4, QSPI_IFR_WIDTH_QUAD_CMD },
> + { 1, 1, 8, QSPI_IFR_WIDTH_OCT_OUTPUT },
> + { 1, 8, 8, QSPI_IFR_WIDTH_OCT_IO },
> + { 8, 8, 8, QSPI_IFR_WIDTH_OCT_CMD },
> +};
> +
> #ifdef VERBOSE_DEBUG
> static const char *atmel_qspi_reg_name(u32 offset, char *tmp, size_t sz)
> {
> @@ -193,6 +329,8 @@ static const char *atmel_qspi_reg_name(u32 offset, char *tmp, size_t sz)
> return "IMR";
> case QSPI_SCR:
> return "SCR";
> + case QSPI_SR2:
> + return "SR2";
> case QSPI_IAR:
> return "IAR";
> case QSPI_ICR:
> @@ -205,6 +343,18 @@ static const char *atmel_qspi_reg_name(u32 offset, char *tmp, size_t sz)
> return "SMR";
> case QSPI_SKR:
> return "SKR";
> + case QSPI_REFRESH:
> + return "REFRESH";
> + case QSPI_WRACNT:
> + return "WRACNT";
> + case QSPI_DLLCFG:
> + return "DLLCFG";
> + case QSPI_PCALCFG:
> + return "PCALCFG";
> + case QSPI_PCALBP:
> + return "PCALBP";
> + case QSPI_TOUT:
> + return "TOUT";
> case QSPI_WPMR:
> return "WPMR";
> case QSPI_WPSR:
> @@ -272,9 +422,29 @@ static int atmel_qspi_find_mode(const struct spi_mem_op *op)
> return -ENOTSUPP;
> }
>
> +static int atmel_qspi_sama7g5_find_mode(const struct spi_mem_op *op)
> +{
> + u32 i;
> +
> + for (i = 0; i < ARRAY_SIZE(atmel_qspi_sama7g5_modes); i++)
> + if (atmel_qspi_is_compatible(op, &atmel_qspi_sama7g5_modes[i]))
> + return i;
> +
> + return -EOPNOTSUPP;
> +}
> +
> static bool atmel_qspi_supports_op(struct spi_slave *slave,
> const struct spi_mem_op *op)
> {
> + struct atmel_qspi *aq = dev_get_priv(slave->dev->parent);
> +
> + if (aq->caps->octal) {
> + if (atmel_qspi_sama7g5_find_mode(op) < 0)
> + return false;
> + else
> + return true;
> + }
> +
> if (atmel_qspi_find_mode(op) < 0)
> return false;
>
> @@ -397,24 +567,10 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq,
> return 0;
> }
>
> -static int atmel_qspi_exec_op(struct spi_slave *slave,
> - const struct spi_mem_op *op)
> +static int atmel_qspi_transfer(struct atmel_qspi *aq,
> + const struct spi_mem_op *op, u32 offset)
> {
> - struct atmel_qspi *aq = dev_get_priv(slave->dev->parent);
> - u32 sr, imr, offset;
> - int err;
> -
> - /*
> - * Check if the address exceeds the MMIO window size. An improvement
> - * would be to add support for regular SPI mode and fall back to it
> - * when the flash memories overrun the controller's memory space.
> - */
> - if (op->addr.val + op->data.nbytes > aq->mmap_size)
> - return -ENOTSUPP;
> -
> - err = atmel_qspi_set_cfg(aq, op, &offset);
> - if (err)
> - return err;
> + u32 sr, imr;
>
> /* Skip to the final steps if there is no data */
> if (op->data.nbytes) {
> @@ -436,7 +592,341 @@ static int atmel_qspi_exec_op(struct spi_slave *slave,
> /* Poll INSTruction End and Chip Select Rise flags. */
> imr = QSPI_SR_INSTRE | QSPI_SR_CSR;
> return readl_poll_timeout(aq->regs + QSPI_SR, sr, (sr & imr) == imr,
> - 1000000);
> + ATMEL_QSPI_TIMEOUT);
> +}
> +
> +static int atmel_qspi_reg_sync(struct atmel_qspi *aq)
> +{
> + u32 val;
> +
> + return readl_poll_timeout(aq->regs + QSPI_SR2, val,
> + !(val & QSPI_SR2_SYNCBSY),
> + ATMEL_QSPI_SYNC_TIMEOUT);
> +}
> +
> +static int atmel_qspi_update_config(struct atmel_qspi *aq)
> +{
> + int ret;
> +
> + ret = atmel_qspi_reg_sync(aq);
> + if (ret)
> + return ret;
> + atmel_qspi_write(QSPI_CR_UPDCFG, aq, QSPI_CR);
> + return atmel_qspi_reg_sync(aq);
> +}
> +
> +static int atmel_qspi_sama7g5_set_cfg(struct atmel_qspi *aq,
> + const struct spi_mem_op *op, u32 *offset)
> +{
> + u32 iar, icr, ifr;
> + int mode, ret;
> +
> + iar = 0;
> + icr = FIELD_PREP(QSPI_ICR_INST_MASK_SAMA7G5, op->cmd.opcode);
> + ifr = QSPI_IFR_INSTEN;
> +
> + mode = atmel_qspi_sama7g5_find_mode(op);
> + if (mode < 0)
> + return mode;
> + ifr |= atmel_qspi_sama7g5_modes[mode].config;
> +
> + if (op->dummy.buswidth && op->dummy.nbytes) {
> + if (op->addr.dtr && op->dummy.dtr && op->data.dtr)
> + ifr |= QSPI_IFR_NBDUM(op->dummy.nbytes * 8 /
> + (2 * op->dummy.buswidth));
> + else
> + ifr |= QSPI_IFR_NBDUM(op->dummy.nbytes * 8 /
> + op->dummy.buswidth);
> + }
> +
> + if (op->addr.buswidth && op->addr.nbytes) {
> + ifr |= FIELD_PREP(QSPI_IFR_ADDRL_SAMA7G5, op->addr.nbytes - 1) |
> + QSPI_IFR_ADDREN;
> + iar = FIELD_PREP(QSPI_IAR_ADDR, op->addr.val);
> + }
> +
> + if (op->addr.dtr && op->dummy.dtr && op->data.dtr) {
> + ifr |= QSPI_IFR_DDREN;
> + if (op->cmd.dtr)
> + ifr |= QSPI_IFR_DDRCMDEN;
> + ifr |= QSPI_IFR_DQSEN;
> + }
> +
> + if (op->cmd.buswidth == 8 || op->addr.buswidth == 8 ||
> + op->data.buswidth == 8)
> + ifr |= FIELD_PREP(QSPI_IFR_PROTTYP, QSPI_IFR_PROTTYP_OCTAFLASH);
> +
> + /* offset of the data access in the QSPI memory space */
> + *offset = iar;
> +
> + /* Set data enable */
> + if (op->data.nbytes) {
> + ifr |= QSPI_IFR_DATAEN;
> + if (op->addr.nbytes)
> + ifr |= QSPI_IFR_TFRTYP_MEM;
> + }
> +
> + /*
> + * If the QSPI controller is set in regular SPI mode, set it in
> + * Serial Memory Mode (SMM).
> + */
> + if (aq->mr != QSPI_MR_SMM) {
> + atmel_qspi_write(QSPI_MR_SMM | QSPI_MR_DQSDLYEN, aq, QSPI_MR);
> + ret = atmel_qspi_update_config(aq);
> + if (ret)
> + return ret;
> + aq->mr = QSPI_MR_SMM;
> + }
> +
> + /* Clear pending interrupts */
> + (void)atmel_qspi_read(aq, QSPI_SR);
> +
> + /* Set QSPI Instruction Frame registers */
> + if (op->addr.nbytes && !op->data.nbytes)
> + atmel_qspi_write(iar, aq, QSPI_IAR);
> +
> + if (op->data.dir == SPI_MEM_DATA_IN) {
> + atmel_qspi_write(icr, aq, QSPI_RICR);
> + } else {
> + atmel_qspi_write(icr, aq, QSPI_WICR);
> + if (op->data.nbytes)
> + atmel_qspi_write(FIELD_PREP(QSPI_WRACNT_NBWRA,
> + op->data.nbytes),
> + aq, QSPI_WRACNT);
> + }
> +
> + atmel_qspi_write(ifr, aq, QSPI_IFR);
> +
> + return atmel_qspi_update_config(aq);
> +}
> +
> +static int atmel_qspi_sama7g5_transfer(struct atmel_qspi *aq,
> + const struct spi_mem_op *op, u32 offset)
> +{
> + int err;
> + u32 val;
> +
> + if (!op->data.nbytes) {
> + /* Start the transfer. */
> + err = atmel_qspi_reg_sync(aq);
> + if (err)
> + return err;
> + atmel_qspi_write(QSPI_CR_STTFR, aq, QSPI_CR);
> +
> + return readl_poll_timeout(aq->regs + QSPI_SR, val,
> + val & QSPI_SR_CSRA,
> + ATMEL_QSPI_TIMEOUT);
> + }
> +
> + /* Send/Receive data. */
> + if (op->data.dir == SPI_MEM_DATA_IN) {
> + memcpy_fromio(op->data.buf.in, aq->mem + offset,
> + op->data.nbytes);
> +
> + if (op->addr.nbytes) {
> + err = readl_poll_timeout(aq->regs + QSPI_SR2, val,
> + !(val & QSPI_SR2_RBUSY),
> + ATMEL_QSPI_SYNC_TIMEOUT);
> + if (err)
> + return err;
> + }
> + } else {
> + memcpy_toio(aq->mem + offset, op->data.buf.out,
> + op->data.nbytes);
> +
> + err = readl_poll_timeout(aq->regs + QSPI_SR, val,
> + val & QSPI_SR_LWRA,
> + ATMEL_QSPI_TIMEOUT);
> + if (err)
> + return err;
> + }
> +
> + /* Release the chip-select. */
> + err = atmel_qspi_reg_sync(aq);
> + if (err)
> + return err;
> + atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR);
> +
> + return readl_poll_timeout(aq->regs + QSPI_SR, val, val & QSPI_SR_CSRA,
> + ATMEL_QSPI_TIMEOUT);
> +}
> +
> +static int atmel_qspi_exec_op(struct spi_slave *slave,
> + const struct spi_mem_op *op)
> +{
> + struct atmel_qspi *aq = dev_get_priv(slave->dev->parent);
> + u32 offset;
> + int err;
> +
> + /*
> + * Check if the address exceeds the MMIO window size. An improvement
> + * would be to add support for regular SPI mode and fall back to it
> + * when the flash memories overrun the controller's memory space.
> + */
> + if (op->addr.val + op->data.nbytes > aq->mmap_size)
> + return -ENOTSUPP;
> +
> + if (op->addr.nbytes > 4)
> + return -EOPNOTSUPP;
> +
> + err = aq->ops->set_cfg(aq, op, &offset);
> + if (err)
> + return err;
> +
> + return aq->ops->transfer(aq, op, offset);
> +}
> +
> +static int atmel_qspi_set_pad_calibration(struct udevice *bus, uint hz)
> +{
> + struct atmel_qspi *aq = dev_get_priv(bus);
> + u32 status, val;
> + int i, ret;
> + u8 pclk_div = 0;
> +
> + for (i = 0; i < ATMEL_QSPI_PCAL_ARRAY_SIZE; i++) {
> + if (aq->bus_clk_rate <= pcal[i].pclk_rate) {
> + pclk_div = pcal[i].pclk_div;
> + break;
> + }
> + }
> +
> + /*
> + * Use the biggest divider in case the peripheral clock exceeds
> + * 200MHZ.
> + */
> + if (aq->bus_clk_rate > pcal[ATMEL_QSPI_PCAL_ARRAY_SIZE - 1].pclk_rate)
> + pclk_div = pcal[ATMEL_QSPI_PCAL_ARRAY_SIZE - 1].pclk_div;
> +
> + /* Disable QSPI while configuring the pad calibration. */
> + status = atmel_qspi_read(aq, QSPI_SR2);
> + if (status & QSPI_SR2_QSPIENS) {
> + ret = atmel_qspi_reg_sync(aq);
> + if (ret)
> + return ret;
> + atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR);
> + }
> +
> + /*
> + * The analog circuitry is not shut down at the end of the calibration
> + * and the start-up time is only required for the first calibration
> + * sequence, thus increasing performance. Set the delay between the Pad
> + * calibration analog circuitry and the calibration request to 2us.
> + */
> + atmel_qspi_write(QSPI_PCALCFG_AAON |
> + FIELD_PREP(QSPI_PCALCFG_CLKDIV, pclk_div) |
> + FIELD_PREP(QSPI_PCALCFG_CALCNT,
> + 2 * (aq->bus_clk_rate / 1000000)),
> + aq, QSPI_PCALCFG);
> +
> + /* DLL On + start calibration. */
> + atmel_qspi_write(QSPI_CR_DLLON | QSPI_CR_STPCAL, aq, QSPI_CR);
> + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
> + (val & QSPI_SR2_DLOCK) &&
> + !(val & QSPI_SR2_CALBSY),
> + ATMEL_QSPI_TIMEOUT);
> +
> + /* Refresh analogic blocks every 1 ms.*/
> + atmel_qspi_write(FIELD_PREP(QSPI_REFRESH_DELAY_COUNTER, hz / 1000),
> + aq, QSPI_REFRESH);
> +
> + return ret;
> +}
> +
> +static int atmel_qspi_set_gclk(struct udevice *bus, uint hz)
> +{
> + struct atmel_qspi *aq = dev_get_priv(bus);
> + struct clk gclk;
> + u32 status, val;
> + int ret;
> +
> + /* Disable DLL before setting GCLK */
> + status = atmel_qspi_read(aq, QSPI_SR2);
> + if (status & QSPI_SR2_DLOCK) {
> + atmel_qspi_write(QSPI_CR_DLLOFF, aq, QSPI_CR);
> + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
> + !(val & QSPI_SR2_DLOCK),
> + ATMEL_QSPI_TIMEOUT);
> + if (ret)
> + return ret;
> + }
> +
> + if (hz > QSPI_DLLCFG_THRESHOLD_FREQ)
> + atmel_qspi_write(QSPI_DLLCFG_RANGE, aq, QSPI_DLLCFG);
> + else
> + atmel_qspi_write(0, aq, QSPI_DLLCFG);
> +
> + ret = clk_get_by_name(bus, "gclk", &gclk);
> + if (ret) {
> + dev_err(bus, "Missing QSPI generic clock\n");
> + return ret;
> + }
> +
> + ret = clk_disable(&gclk);
> + if (ret)
> + dev_err(bus, "Failed to disable QSPI generic clock\n");
> +
> + ret = clk_set_rate(&gclk, hz);
> + if (ret < 0) {
> + dev_err(bus, "Failed to set generic clock rate.\n");
> + return ret;
> + }
> +
> + ret = clk_enable(&gclk);
> + if (ret)
> + dev_err(bus, "Failed to enable QSPI generic clock\n");
> + clk_free(&gclk);
> +
> + return ret;
> +}
> +
> +static int atmel_qspi_sama7g5_set_speed(struct udevice *bus, uint hz)
> +{
> + struct atmel_qspi *aq = dev_get_priv(bus);
> + u32 val;
> + int ret;
> +
> + ret = atmel_qspi_set_gclk(bus, hz);
> + if (ret)
> + return ret;
> +
> + if (aq->caps->octal) {
> + ret = atmel_qspi_set_pad_calibration(bus, hz);
> + if (ret)
> + return ret;
> + } else {
> + atmel_qspi_write(QSPI_CR_DLLON, aq, QSPI_CR);
> + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
> + val & QSPI_SR2_DLOCK,
> + ATMEL_QSPI_TIMEOUT);
> + }
> +
> + /* Set the QSPI controller by default in Serial Memory Mode */
> + atmel_qspi_write(QSPI_MR_SMM | QSPI_MR_DQSDLYEN, aq, QSPI_MR);
> + ret = atmel_qspi_update_config(aq);
> + if (ret)
> + return ret;
> + aq->mr = QSPI_MR_SMM;
> +
> + /* Enable the QSPI controller. */
> + ret = atmel_qspi_reg_sync(aq);
> + if (ret)
> + return ret;
> + atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR);
> + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
> + val & QSPI_SR2_QSPIENS,
> + ATMEL_QSPI_SYNC_TIMEOUT);
> + if (ret)
> + return ret;
> +
> + if (aq->caps->octal)
> + ret = readl_poll_timeout(aq->regs + QSPI_SR, val,
> + val & QSPI_SR_RFRSHD,
> + ATMEL_QSPI_TIMEOUT);
> +
> + atmel_qspi_write(FIELD_PREP(QSPI_TOUT_TCNTM, QSPI_TOUT_MAX),
> + aq, QSPI_TOUT);
> +
> + return ret;
> }
>
> static int atmel_qspi_set_speed(struct udevice *bus, uint hz)
> @@ -444,6 +934,9 @@ static int atmel_qspi_set_speed(struct udevice *bus, uint hz)
> struct atmel_qspi *aq = dev_get_priv(bus);
> u32 scr, scbr, mask, new_value;
>
> + if (aq->caps->has_gclk)
> + return atmel_qspi_sama7g5_set_speed(bus, hz);
> +
> /* Compute the QSPI baudrate */
> scbr = DIV_ROUND_UP(aq->bus_clk_rate, hz);
> if (scbr > 0)
> @@ -480,6 +973,8 @@ static int atmel_qspi_set_mode(struct udevice *bus, uint mode)
>
> scr = (scr & ~mask) | new_value;
> atmel_qspi_write(scr, aq, QSPI_SCR);
> + if (aq->caps->has_gclk)
> + return atmel_qspi_update_config(aq);
>
> return 0;
> }
> @@ -487,7 +982,7 @@ static int atmel_qspi_set_mode(struct udevice *bus, uint mode)
> static int atmel_qspi_enable_clk(struct udevice *dev)
> {
> struct atmel_qspi *aq = dev_get_priv(dev);
> - struct clk pclk, qspick;
> + struct clk pclk, qspick, gclk;
> int ret;
>
> ret = clk_get_by_name(dev, "pclk", &pclk);
> @@ -517,6 +1012,17 @@ static int atmel_qspi_enable_clk(struct udevice *dev)
> if (ret)
> dev_err(dev, "Failed to enable QSPI system clock\n");
> clk_free(&qspick);
> + } else if (aq->caps->has_gclk) {
> + ret = clk_get_by_name(dev, "gclk", &gclk);
> + if (ret) {
> + dev_err(dev, "Missing QSPI generic clock\n");
> + goto free_pclk;
> + }
> +
> + ret = clk_enable(&gclk);
> + if (ret)
> + dev_err(dev, "Failed to enable QSPI system clock\n");
> + clk_free(&gclk);
> }
>
> aq->bus_clk_rate = clk_get_rate(&pclk);
> @@ -529,8 +1035,18 @@ free_pclk:
> return ret;
> }
>
> -static void atmel_qspi_init(struct atmel_qspi *aq)
> +static int atmel_qspi_init(struct atmel_qspi *aq)
> {
> + int ret;
> +
> + if (aq->caps->has_gclk) {
> + ret = atmel_qspi_reg_sync(aq);
> + if (ret)
> + return ret;
> + atmel_qspi_write(QSPI_CR_SWRST, aq, QSPI_CR);
> + return 0;
> + }
> +
> /* Reset the QSPI controller */
> atmel_qspi_write(QSPI_CR_SWRST, aq, QSPI_CR);
>
> @@ -540,8 +1056,20 @@ static void atmel_qspi_init(struct atmel_qspi *aq)
>
> /* Enable the QSPI controller */
> atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR);
> +
> + return 0;
> }
>
> +static const struct atmel_qspi_priv_ops atmel_qspi_priv_ops = {
> + .set_cfg = atmel_qspi_set_cfg,
> + .transfer = atmel_qspi_transfer,
> +};
> +
> +static const struct atmel_qspi_priv_ops atmel_qspi_sama7g5_priv_ops = {
> + .set_cfg = atmel_qspi_sama7g5_set_cfg,
> + .transfer = atmel_qspi_sama7g5_transfer,
> +};
> +
> static int atmel_qspi_probe(struct udevice *dev)
> {
> struct atmel_qspi *aq = dev_get_priv(dev);
> @@ -554,6 +1082,11 @@ static int atmel_qspi_probe(struct udevice *dev)
> return -EINVAL;
> };
>
> + if (aq->caps->has_gclk)
> + aq->ops = &atmel_qspi_sama7g5_priv_ops;
> + else
> + aq->ops = &atmel_qspi_priv_ops;
> +
> /* Map the registers */
> ret = dev_read_resource_byname(dev, "qspi_base", &res);
> if (ret) {
> @@ -583,10 +1116,7 @@ static int atmel_qspi_probe(struct udevice *dev)
> return ret;
>
> aq->dev = dev;
> -
> - atmel_qspi_init(aq);
> -
> - return 0;
> + return atmel_qspi_init(aq);
> }
>
> static const struct spi_controller_mem_ops atmel_qspi_mem_ops = {
> @@ -607,6 +1137,15 @@ static const struct atmel_qspi_caps atmel_sam9x60_qspi_caps = {
> .has_ricr = true,
> };
>
> +static const struct atmel_qspi_caps atmel_sama7g5_ospi_caps = {
> + .has_gclk = true,
> + .octal = true,
> +};
> +
> +static const struct atmel_qspi_caps atmel_sama7g5_qspi_caps = {
> + .has_gclk = true,
> +};
> +
> static const struct udevice_id atmel_qspi_ids[] = {
> {
> .compatible = "atmel,sama5d2-qspi",
> @@ -616,6 +1155,14 @@ static const struct udevice_id atmel_qspi_ids[] = {
> .compatible = "microchip,sam9x60-qspi",
> .data = (ulong)&atmel_sam9x60_qspi_caps,
> },
> + {
> + .compatible = "microchip,sama7g5-ospi",
> + .data = (ulong)&atmel_sama7g5_ospi_caps,
> + },
> + {
> + .compatible = "microchip,sama7g5-qspi",
> + .data = (ulong)&atmel_sama7g5_qspi_caps,
> + },
> { /* sentinel */ }
> };
>
>
More information about the U-Boot
mailing list