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
This commit is contained in:
parent
1ab71814c2
commit
73b655f3b2
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user