From 73b655f3b2cee52e3fc2227ec18cac9755828f4b Mon Sep 17 00:00:00 2001 From: Andrey Zabolotnyi Date: Thu, 19 Mar 2020 15:59:18 +0300 Subject: [PATCH] stm32h7_qspi: support for custom clock (not just HCLK) and support for DUAL/QUAD commands (#582) * stm32h7_qspi: Board.h now may define the BOARD_QSPI_CLK macro to select one of RCC_D1CCIPR_QSPISEL_{HCLK,PLL1,PLL2,PER} clocks to use with QUADSPI peripherial. Defaults to HCLK for backward compatibility. New macros in qspi.h: QSPICMD_IDUAL and QSPICMD_IQUAD for selecting the bit width for instruction code (1,2 or 4 bits) of a qspi_cmdinfo_s, and QSPIMEM_IDUAL and QSPIMEM_IQUAD for selecting the bit width of a qspi_meminfo_s. * NX style fixes --- arch/arm/src/stm32h7/stm32_qspi.c | 125 ++++++++++++++++++++++-------- include/nuttx/spi/qspi.h | 11 ++- 2 files changed, 102 insertions(+), 34 deletions(-) diff --git a/arch/arm/src/stm32h7/stm32_qspi.c b/arch/arm/src/stm32h7/stm32_qspi.c index 8b828d41d1..05c1be566e 100644 --- a/arch/arm/src/stm32h7/stm32_qspi.c +++ b/arch/arm/src/stm32h7/stm32_qspi.c @@ -159,11 +159,35 @@ /* Clocking *****************************************************************/ -/* The QSPI bit rate clock is generated by dividing the peripheral clock by - * a value between 1 and 255 +/* The board.h file may choose a different clock source for QUADSPI + * peripherial by defining the BOARD_QSPI_CLK macro to one of the + * RCC_D1CCIPR_QSPISEL_XXX values (XXX = HCLK, PLL1, PLL2, PER). + * QUADSPI clock defaults to HCLK. */ -#define STL32F7_QSPI_CLOCK STM32_SYSCLK_FREQUENCY /* Frequency of the QSPI clock */ +#ifndef BOARD_QSPI_CLK +/* Clock QUADSPI from HCLK by default */ + +# define BOARD_QSPI_CLK RCC_D1CCIPR_QSPISEL_HCLK +#endif + +/* The QSPI bit rate clock is generated by dividing the peripheral clock by + * a value between 1 and 255. + * + * Find out the frequency of the QSPI clock. + */ + +#if BOARD_QSPI_CLK == RCC_D1CCIPR_QSPISEL_HCLK +# define QSPI_CLK_FREQUENCY STM32_HCLK_FREQUENCY +#elif BOARD_QSPI_CLK == RCC_D1CCIPR_QSPISEL_PLL1 +# define QSPI_CLK_FREQUENCY STM32_PLL1Q_FREQUENCY +#elif BOARD_QSPI_CLK == RCC_D1CCIPR_QSPISEL_PLL2 +# define QSPI_CLK_FREQUENCY STM32_PLL2R_FREQUENCY +#elif BOARD_QSPI_CLK == RCC_D1CCIPR_QSPISEL_PER +# define QSPI_CLK_FREQUENCY STM32_PER_FREQUENCY +#else +# error "BOARD_QSPI_CLK has unknown value!" +#endif /**************************************************************************** * Private Types @@ -268,8 +292,8 @@ static bool qspi_checkreg(struct stm32h7_qspidev_s *priv, bool wr, static inline uint32_t qspi_getreg(struct stm32h7_qspidev_s *priv, unsigned int offset); -static inline void qspi_putreg(struct stm32h7_qspidev_s *priv, uint32_t value, - unsigned int offset); +static inline void qspi_putreg(struct stm32h7_qspidev_s *priv, + uint32_t value, unsigned int offset); #ifdef CONFIG_DEBUG_SPI_INFO static void qspi_dumpregs(struct stm32h7_qspidev_s *priv, @@ -314,7 +338,8 @@ static void qspi_dma_sampledone(struct stm32h7_qspidev_s *priv); /* QSPI methods */ static int qspi_lock(struct qspi_dev_s *dev, bool lock); -static uint32_t qspi_setfrequency(struct qspi_dev_s *dev, uint32_t frequency); +static uint32_t qspi_setfrequency(struct qspi_dev_s *dev, + uint32_t frequency); static void qspi_setmode(struct qspi_dev_s *dev, enum qspi_mode_e mode); static void qspi_setbits(struct qspi_dev_s *dev, int nbits); static int qspi_command(struct qspi_dev_s *dev, @@ -455,8 +480,8 @@ static inline uint32_t qspi_getreg(struct stm32h7_qspidev_s *priv, * ****************************************************************************/ -static inline void qspi_putreg(struct stm32h7_qspidev_s *priv, uint32_t value, - unsigned int offset) +static inline void qspi_putreg(struct stm32h7_qspidev_s *priv, + uint32_t value, unsigned int offset) { uint32_t address = priv->base + offset; @@ -493,7 +518,7 @@ static void qspi_dumpregs(struct stm32h7_qspidev_s *priv, const char *msg) #if 0 /* this extra verbose output may be helpful in some cases; you'll need - * to make sure your syslog is large enough to accommodate the extra output. + * to make sure your syslog is large enough to accommodate extra output. */ regval = getreg32(priv->base + STM32_QUADSPI_CR_OFFSET); /* Control Register */ @@ -773,7 +798,19 @@ static int qspi_setupxctnfromcmd(struct qspi_xctnspec_s *xctn, /* XXX III instruction mode, single dual quad option bits */ - xctn->instrmode = CCR_IMODE_SINGLE; + if (QSPICMD_ISIQUAD(cmdinfo->flags)) + { + xctn->instrmode = CCR_IMODE_QUAD; + } + else if (QSPICMD_ISIDUAL(cmdinfo->flags)) + { + xctn->instrmode = CCR_IMODE_DUAL; + } + else + { + xctn->instrmode = CCR_IMODE_SINGLE; + } + xctn->instr = cmdinfo->cmd; /* XXX III option bits for 'send instruction only once' */ @@ -881,7 +918,8 @@ static int qspi_setupxctnfrommem(struct qspi_xctnspec_s *xctn, spiinfo(" cmd: %04x\n", meminfo->cmd); spiinfo(" address/length: %08lx/%d\n", (unsigned long)meminfo->addr, meminfo->addrlen); - spiinfo(" %s Data:\n", QSPIMEM_ISWRITE(meminfo->flags) ? "Write" : "Read"); + spiinfo(" %s Data:\n", QSPIMEM_ISWRITE(meminfo->flags) ? + "Write" : "Read"); spiinfo(" buffer/length: %p/%d\n", meminfo->buffer, meminfo->buflen); #endif @@ -891,7 +929,19 @@ static int qspi_setupxctnfrommem(struct qspi_xctnspec_s *xctn, /* XXX III instruction mode, single dual quad option bits */ - xctn->instrmode = CCR_IMODE_SINGLE; + if (QSPIMEM_ISIQUAD(meminfo->flags)) + { + xctn->instrmode = CCR_IMODE_QUAD; + } + else if (QSPIMEM_ISIDUAL(meminfo->flags)) + { + xctn->instrmode = CCR_IMODE_DUAL; + } + else + { + xctn->instrmode = CCR_IMODE_SINGLE; + } + xctn->instr = meminfo->cmd; /* XXX III option bits for 'send instruction only once' */ @@ -1008,11 +1058,13 @@ static void qspi_waitstatusflags(struct stm32h7_qspidev_s *priv, if (polarity) { - while (!((regval = qspi_getreg(priv, STM32_QUADSPI_SR_OFFSET)) & mask)); + while (!((regval = qspi_getreg(priv, STM32_QUADSPI_SR_OFFSET)) & mask)) + ; } else { - while (((regval = qspi_getreg(priv, STM32_QUADSPI_SR_OFFSET)) & mask)); + while (((regval = qspi_getreg(priv, STM32_QUADSPI_SR_OFFSET)) & mask)) + ; } } @@ -1137,13 +1189,14 @@ static int qspi0_interrupt(int irq, void *context, FAR void *arg) { /* Write data until we have no more or have no place to put it */ - while (((regval = qspi_getreg(&g_qspi0dev, STM32_QUADSPI_SR_OFFSET)) & - QSPI_SR_FTF) != 0) + while (((regval = qspi_getreg( + &g_qspi0dev, STM32_QUADSPI_SR_OFFSET)) & QSPI_SR_FTF) != 0) { if (g_qspi0dev.xctn->idxnow < g_qspi0dev.xctn->datasize) { *(volatile uint8_t *)datareg = - ((uint8_t *)g_qspi0dev.xctn->buffer)[g_qspi0dev.xctn->idxnow]; + ((uint8_t *)g_qspi0dev.xctn->buffer) + [g_qspi0dev.xctn->idxnow]; ++g_qspi0dev.xctn->idxnow; } else @@ -1158,13 +1211,13 @@ static int qspi0_interrupt(int irq, void *context, FAR void *arg) { /* Read data until we have no more or have no place to put it */ - while (((regval = qspi_getreg(&g_qspi0dev, STM32_QUADSPI_SR_OFFSET)) & - QSPI_SR_FTF) != 0) + while (((regval = qspi_getreg( + &g_qspi0dev, STM32_QUADSPI_SR_OFFSET)) & QSPI_SR_FTF) != 0) { if (g_qspi0dev.xctn->idxnow < g_qspi0dev.xctn->datasize) { - ((uint8_t *)g_qspi0dev.xctn->buffer)[g_qspi0dev.xctn->idxnow] = - *(volatile uint8_t *)datareg; + ((uint8_t *)g_qspi0dev.xctn->buffer) + [g_qspi0dev.xctn->idxnow] = *(volatile uint8_t *)datareg; ++g_qspi0dev.xctn->idxnow; } else @@ -1202,13 +1255,14 @@ static int qspi0_interrupt(int irq, void *context, FAR void *arg) /* Read any remaining data */ - while (((regval = qspi_getreg(&g_qspi0dev, STM32_QUADSPI_SR_OFFSET)) & + while (((regval = qspi_getreg( + &g_qspi0dev, STM32_QUADSPI_SR_OFFSET)) & QSPI_SR_FLEVEL_MASK) != 0) { if (g_qspi0dev.xctn->idxnow < g_qspi0dev.xctn->datasize) { - ((uint8_t *)g_qspi0dev.xctn->buffer)[g_qspi0dev.xctn->idxnow] = - *(volatile uint8_t *)datareg; + ((uint8_t *)g_qspi0dev.xctn->buffer) + [g_qspi0dev.xctn->idxnow] = *(volatile uint8_t *)datareg; ++g_qspi0dev.xctn->idxnow; } else @@ -1831,8 +1885,8 @@ static uint32_t qspi_setfrequency(struct qspi_dev_s *dev, uint32_t frequency) /* Configure QSPI to a frequency as close as possible to the requested * frequency. * - * QSCK frequency = STL32F7_QSPI_CLOCK / prescaler, or - * prescaler = STL32F7_QSPI_CLOCK / frequency + * QSCK frequency = QSPI_CLK_FREQUENCY / prescaler, or + * prescaler = QSPI_CLK_FREQUENCY / frequency * * Where prescaler can have the range 1 to 256 and the * STM32_QUADSPI_CR_OFFSET register field holds prescaler - 1. @@ -1840,7 +1894,7 @@ static uint32_t qspi_setfrequency(struct qspi_dev_s *dev, uint32_t frequency) * 'frequency' is treated as a not-to-exceed value. */ - prescaler = (frequency + STL32F7_QSPI_CLOCK - 1) / frequency; + prescaler = (frequency + QSPI_CLK_FREQUENCY - 1) / frequency; /* Make sure that the divider is within range */ @@ -1862,7 +1916,7 @@ static uint32_t qspi_setfrequency(struct qspi_dev_s *dev, uint32_t frequency) /* Calculate the new actual frequency */ - actual = STL32F7_QSPI_CLOCK / prescaler; + actual = QSPI_CLK_FREQUENCY / prescaler; spiinfo("prescaler=%d actual=%d\n", prescaler, actual); /* Save the frequency setting */ @@ -2049,8 +2103,8 @@ static int qspi_command(struct qspi_dev_s *dev, qspi_ccrconfig(priv, &xctn, CCR_FMODE_INDWR); - /* Enable 'Transfer Error' 'FIFO Threshhold' and 'Transfer Complete' - * interrupts. + /* Enable 'Transfer Error' 'FIFO Threshhold' and + * 'Transfer Complete' interrupts. */ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET); @@ -2074,8 +2128,8 @@ static int qspi_command(struct qspi_dev_s *dev, qspi_putreg(priv, addrval, STM32_QUADSPI_AR_OFFSET); - /* Enable 'Transfer Error' 'FIFO Threshhold' and 'Transfer Complete' - * interrupts + /* Enable 'Transfer Error' 'FIFO Threshhold' and + * 'Transfer Complete' interrupts */ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET); @@ -2444,7 +2498,8 @@ static int qspi_hw_initialize(struct stm32h7_qspidev_s *priv) /* Configure QSPI FIFO Threshold */ regval &= ~(QSPI_CR_FTHRES_MASK); - regval |= ((CONFIG_STM32H7_QSPI_FIFO_THESHOLD - 1) << QSPI_CR_FTHRES_SHIFT); + regval |= ((CONFIG_STM32H7_QSPI_FIFO_THESHOLD - 1) << + QSPI_CR_FTHRES_SHIFT); qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET); /* Wait till BUSY flag reset */ @@ -2538,6 +2593,10 @@ struct qspi_dev_s *stm32h7_qspi_initialize(int intf) priv = &g_qspi0dev; + /* Select QSPI clock source */ + + modreg32 (BOARD_QSPI_CLK, RCC_D1CCIPR_QSPISEL_MASK, STM32_RCC_D1CCIPR); + /* Enable clocking to the QSPI peripheral */ regval = getreg32(STM32_RCC_AHB3ENR); diff --git a/include/nuttx/spi/qspi.h b/include/nuttx/spi/qspi.h index 9cc86dded4..f6a081d435 100644 --- a/include/nuttx/spi/qspi.h +++ b/include/nuttx/spi/qspi.h @@ -49,6 +49,7 @@ /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ + /* Access macros ************************************************************/ /**************************************************************************** @@ -149,11 +150,15 @@ #define QSPICMD_ADDRESS (1 << 0) /* Bit 0: Enable address transfer */ #define QSPICMD_READDATA (1 << 1) /* Bit 1: Enable read data transfer */ #define QSPICMD_WRITEDATA (1 << 2) /* Bit 2: Enable write data transfer */ +#define QSPICMD_IDUAL (1 << 3) /* Bit 3: Instruction on two lines */ +#define QSPICMD_IQUAD (1 << 4) /* Bit 4: Instruction on four lines */ #define QSPICMD_ISADDRESS(f) (((f) & QSPICMD_ADDRESS) != 0) #define QSPICMD_ISDATA(f) (((f) & (QSPICMD_READDATA | QSPICMD_WRITEDATA)) != 0) #define QSPICMD_ISREAD(f) (((f) & QSPICMD_READDATA) != 0) #define QSPICMD_ISWRITE(f) (((f) & QSPICMD_WRITEDATA) != 0) +#define QSPICMD_ISIDUAL(f) (((f) & QSPICMD_IDUAL) != 0) +#define QSPICMD_ISIQUAD(f) (((f) & QSPICMD_IQUAD) != 0) /**************************************************************************** * Name: QSPI_MEMORY @@ -180,12 +185,16 @@ #define QSPIMEM_QUADIO (1 << 4) /* Bit 4: Use Quad I/O (READ only) */ #define QSPIMEM_SCRAMBLE (1 << 5) /* Bit 5: Scramble data */ #define QSPIMEM_RANDOM (1 << 6) /* Bit 6: Use random key in scrambler */ +#define QSPIMEM_IDUAL (1 << 7) /* Bit 7: Instruction on two lines */ +#define QSPIMEM_IQUAD (1 << 0) /* Bit 0: Instruction on four lines */ #define QSPIMEM_ISREAD(f) (((f) & QSPIMEM_WRITE) == 0) #define QSPIMEM_ISWRITE(f) (((f) & QSPIMEM_WRITE) != 0) #define QSPIMEM_ISDUALIO(f) (((f) & QSPIMEM_DUALIO) != 0) #define QSPIMEM_ISQUADIO(f) (((f) & QSPIMEM_QUADIO) != 0) #define QSPIMEM_ISSCRAMBLE(f) (((f) & QSPIMEM_SCRAMBLE) != 0) +#define QSPIMEM_ISIDUAL(f) (((f) & QSPIMEM_IDUAL) != 0) +#define QSPIMEM_ISIQUAD(f) (((f) & QSPIMEM_IQUAD) != 0) #define QSPIMEM_ISRANDOM(f) \ (((f) & (QSPIMEM_SCRAMBLE|QSPIMEM_RANDOM)) == \ @@ -309,7 +318,7 @@ extern "C" #endif /**************************************************************************** - * Public Functions + * Public Function Prototypes ****************************************************************************/ #undef EXTERN