[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