SAMV71 QSPI: Flesh out most of the initialization logic
This commit is contained in:
parent
768aba20ad
commit
45a6f79eeb
@ -131,10 +131,10 @@
|
||||
#define QSPI_MR_LLB (1 << 1) /* Bit 1: Local Loopback Enable */
|
||||
#define QSPI_MR_WDRBT (1 << 2) /* Bit 2: Wait Data Read Before Transfer */
|
||||
#define QSPI_MR_CSMODE_SHIFT (4) /* Bits 4-5: Chip Select Mode */
|
||||
#define QSPI_MR_CSMODE_MASK (3 << QSPI_MR_PCS_SHIFT)
|
||||
# define QSPI_MR_CSMODE_NRELOAD (0 << QSPI_MR_PCS_SHIFT) /* CS deasserted if TD not reloaded */
|
||||
# define QSPI_MR_CSMODE_LASTXFER (1 << QSPI_MR_PCS_SHIFT) /* CS deasserted when LASTXFER transferred */
|
||||
# define QSPI_MR_CSMODE_SYSTEM (2 << QSPI_MR_PCS_SHIFT) /* CS deasserted after each transfer */
|
||||
#define QSPI_MR_CSMODE_MASK (3 << QSPI_MR_CSMODE_SHIFT)
|
||||
# define QSPI_MR_CSMODE_NRELOAD (0 << QSPI_MR_CSMODE_SHIFT) /* CS deasserted if TD not reloaded */
|
||||
# define QSPI_MR_CSMODE_LASTXFER (1 << QSPI_MR_CSMODE_SHIFT) /* CS deasserted when LASTXFER transferred */
|
||||
# define QSPI_MR_CSMODE_SYSTEM (2 << QSPI_MR_CSMODE_SHIFT) /* CS deasserted after each transfer */
|
||||
#define QSPI_MR_NBBITS_SHIFT (8) /* Bits 8-11: Number Of Bits Per Transfer */
|
||||
#define QSPI_MR_NBBITS_MASK (15 << QSPI_MR_NBBITS_SHIFT)
|
||||
# define QSPI_MR_NBBITS(n) ((uint32_t)((n)-SAM_QSPI_MINBITS) << QSPI_MR_NBBITS_SHIFT)
|
||||
|
@ -152,18 +152,23 @@
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
/* The state of the QSPI controller */
|
||||
/* The state of the QSPI controller.
|
||||
*
|
||||
* NOTE: Currently the SAMV7 supports only a single QSPI peripheral. Logic
|
||||
* here is designed to support multiple QSPI peripherals.
|
||||
*/
|
||||
|
||||
struct sam_qspidev_s
|
||||
{
|
||||
struct qspi_dev_s qspi; /* Externally visible part of the QSPI interface */
|
||||
|
||||
xcpt_t handler; /* Interrupt handler */
|
||||
uint32_t base; /* QSPI controller register base address */
|
||||
uint32_t frequency; /* Requested clock frequency */
|
||||
uint32_t actual; /* Actual clock frequency */
|
||||
uint8_t mode; /* Mode 0,1,2,3 */
|
||||
uint8_t nbits; /* Width of word in bits (8 to 16) */
|
||||
uint8_t intf; /* QSPI controller number (0) */
|
||||
uint8_t irq; /* Interrupt number */
|
||||
bool initialized; /* TRUE: Controller has been initialized */
|
||||
sem_t exclsem; /* Assures mutually exclusive access to QSPI */
|
||||
|
||||
@ -246,6 +251,13 @@ static inline uintptr_t qspi_regaddr(struct sam_qspidev_s *priv,
|
||||
unsigned int offset);
|
||||
#endif
|
||||
|
||||
/* Interrupts */
|
||||
|
||||
static int qspi_interrupt(struct sam_qspidev_s *priv);
|
||||
#ifdef CONFIG_SAMV7_QSPI
|
||||
static int qspi0_interrupt(int irq, void *context);
|
||||
#endif
|
||||
|
||||
/* QSPI methods */
|
||||
|
||||
static int qspi_lock(struct qspi_dev_s *dev, bool lock);
|
||||
@ -258,6 +270,10 @@ static int qspi_command_write(struct qspi_dev_s *dev, uint16_t cmd,
|
||||
static int qspi_command_read(struct qspi_dev_s *dev, uint16_t cmd,
|
||||
void *buffer, size_t buflen);
|
||||
|
||||
/* Initialization */
|
||||
|
||||
static int qspi_hw_initialize(struct sam_qspidev_s *priv);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
@ -285,9 +301,11 @@ static struct sam_qspidev_s g_qspi0dev =
|
||||
.ops = &g_qspi0ops,
|
||||
},
|
||||
.base = SAM_QSPI_BASE,
|
||||
.handler = qspi0_interrupt,
|
||||
.intf = 0,
|
||||
.irq = SAM_IRQ_QSPI,
|
||||
#ifdef CONFIG_SAMV7_QSPI_DMA
|
||||
.candma = SAMV7_QSPI_DMA,
|
||||
.candma = SAMV7_QSPI0_DMA,
|
||||
.rxintf = XDMACH_QSPI_RX,
|
||||
.txintf = XDMACH_QSPI_TX,
|
||||
#endif
|
||||
@ -1209,6 +1227,82 @@ static int qspi_command_read(struct qspi_dev_s *dev, uint16_t cmd,
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: qspi_hw_initialize
|
||||
*
|
||||
* Description:
|
||||
* Initialize the QSPI peripheral from hardware reset.
|
||||
*
|
||||
* Input Parameters:
|
||||
* priv - Device state structure.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) on SUCCESS, a negated errno on value of failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int qspi_hw_initialize(struct sam_qspidev_s *priv)
|
||||
{
|
||||
uint32_t regval;
|
||||
|
||||
/* Disable the QSPI */
|
||||
|
||||
qspi_putreg(priv, QSPI_CR_QSPIDIS, SAM_QSPI_CR_OFFSET);
|
||||
while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_QSPIENS) != 0);
|
||||
|
||||
/* Reset the QSPI (twice) */
|
||||
|
||||
qspi_putreg(priv, QSPI_CR_SWRST, SAM_QSPI_CR_OFFSET);
|
||||
qspi_putreg(priv, QSPI_CR_SWRST, SAM_QSPI_CR_OFFSET);
|
||||
|
||||
/* Configure the QSPI
|
||||
*
|
||||
* QSPI_MR_SMM - Serial Memory Mode
|
||||
* QSPI_MR_CSMODE_LASTXFER - CS de-asserted when LASTXFER transferred
|
||||
*/
|
||||
|
||||
regval = QSPI_MR_SMM;
|
||||
qspi_putreg(priv, regval, SAM_QSPI_MR_OFFSET);
|
||||
|
||||
regval |= QSPI_MR_CSMODE_LASTXFER;
|
||||
qspi_putreg(priv, regval, SAM_QSPI_MR_OFFSET);
|
||||
|
||||
/* Set up the initial QSPI clock mode:
|
||||
*
|
||||
* Mode 0: CPOL=0; NCPHA=1
|
||||
*/
|
||||
|
||||
regval = qspi_getreg(priv, SAM_QSPI_SCR_OFFSET);
|
||||
regval &= ~QSPI_SCR_CPOL;
|
||||
regval |= QSPI_SCR_NCPHA;
|
||||
qspi_putreg(priv, regval, SAM_QSPI_SCR_OFFSET);
|
||||
|
||||
regval |= QSPI_SCR_SCBR(1);
|
||||
qspi_putreg(priv, regval, SAM_QSPI_SCR_OFFSET);
|
||||
|
||||
/* 8-bit mode */
|
||||
|
||||
regval = qspi_getreg(priv, SAM_QSPI_MR_OFFSET);
|
||||
regval &= ~QSPI_MR_NBBITS_MASK;
|
||||
regval |= QSPI_MR_NBBITS_8BIT;
|
||||
qspi_putreg(priv, regval, SAM_QSPI_MR_OFFSET);
|
||||
|
||||
priv->nbits = 8;
|
||||
|
||||
/* Enable QSPI */
|
||||
|
||||
qspi_putreg(priv, QSPI_CR_QSPIEN, SAM_QSPI_CR_OFFSET);
|
||||
while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_QSPIENS) == 0);
|
||||
|
||||
/* Flush any pending transfers */
|
||||
|
||||
(void)qspi_getreg(priv, SAM_QSPI_SR_OFFSET);
|
||||
(void)qspi_getreg(priv, SAM_QSPI_RDR_OFFSET);
|
||||
|
||||
qspi_dumpregs(priv, "After initialization");
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
@ -1230,8 +1324,7 @@ static int qspi_command_read(struct qspi_dev_s *dev, uint16_t cmd,
|
||||
FAR struct qspi_dev_s *sam_qspi_initialize(int intf)
|
||||
{
|
||||
FAR struct sam_qspidev_s *priv;
|
||||
irqstate_t flags;
|
||||
uint32_t regval;
|
||||
int ret;
|
||||
|
||||
/* The support SAM parts have only a single QSPI port */
|
||||
|
||||
@ -1243,6 +1336,10 @@ FAR struct qspi_dev_s *sam_qspi_initialize(int intf)
|
||||
#ifdef CONFIG_SAMV7_QSPI
|
||||
if (intf == 0)
|
||||
{
|
||||
/* If this function is called multiple times, the following operatinos
|
||||
* will be performed multiple times.
|
||||
*/
|
||||
|
||||
/* Select QSPI0 */
|
||||
|
||||
priv = &g_qspi0dev;
|
||||
@ -1267,76 +1364,42 @@ FAR struct qspi_dev_s *sam_qspi_initialize(int intf)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SAMV7_QSPI_DMA
|
||||
/* Pre-allocate DMA channels. These allocations exploit that fact that
|
||||
* QSPI0 is managed by DMAC0 and QSPI1 is managed by DMAC1. Hence,
|
||||
* the QSPI number (intf) is the same as the DMAC number.
|
||||
*/
|
||||
|
||||
if (priv->candma)
|
||||
{
|
||||
priv->rxdma = sam_dmachannel(0);
|
||||
if (!priv->rxdma)
|
||||
{
|
||||
spidbg("ERROR: Failed to allocate the RX DMA channel\n");
|
||||
priv->candma = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->candma)
|
||||
{
|
||||
priv->txdma = sam_dmachannel(0);
|
||||
if (!priv->txdma)
|
||||
{
|
||||
spidbg("ERROR: Failed to allocate the TX DMA channel\n");
|
||||
sam_dmafree(priv->rxdma);
|
||||
priv->rxdma = NULL;
|
||||
priv->candma = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Has the QSPI hardware been initialized? */
|
||||
|
||||
if (!priv->initialized)
|
||||
{
|
||||
/* Enable clocking to the QSPI block */
|
||||
|
||||
flags = irqsave();
|
||||
#if defined(CONFIG_SAMV7_QSPI) && defined(CONFIG_SAMV7_QSPI1_MASTER)
|
||||
if (intf == 0)
|
||||
#endif
|
||||
/* Disable QSPI clocking */
|
||||
|
||||
qspi_putreg(priv, QSPI_CR_QSPIDIS, SAM_QSPI_CR_OFFSET);
|
||||
|
||||
/* Execute a software reset of the QSPI (twice) */
|
||||
|
||||
qspi_putreg(priv, QSPI_CR_SWRST, SAM_QSPI_CR_OFFSET);
|
||||
qspi_putreg(priv, QSPI_CR_SWRST, SAM_QSPI_CR_OFFSET);
|
||||
irqrestore(flags);
|
||||
|
||||
/* Configure the QSPI mode register */
|
||||
#warning Missing Logic
|
||||
|
||||
/* And enable the QSPI */
|
||||
|
||||
qspi_putreg(priv, QSPI_CR_QSPIEN, SAM_QSPI_CR_OFFSET);
|
||||
up_mdelay(20);
|
||||
|
||||
/* Flush any pending transfers */
|
||||
|
||||
(void)qspi_getreg(priv, SAM_QSPI_SR_OFFSET);
|
||||
(void)qspi_getreg(priv, SAM_QSPI_RDR_OFFSET);
|
||||
|
||||
/* No perform one time initialization */
|
||||
/* Initialize the QSPI semaphore that enforces mutually exclusive
|
||||
* access to the QSPI registers.
|
||||
*/
|
||||
|
||||
sem_init(&priv->exclsem, 0, 1);
|
||||
priv->initialized = true;
|
||||
|
||||
#ifdef CONFIG_SAMV7_QSPI_DMA
|
||||
/* Pre-allocate DMA channels. */
|
||||
|
||||
if (priv->candma)
|
||||
{
|
||||
priv->rxdma = sam_dmachannel(0);
|
||||
if (!priv->rxdma)
|
||||
{
|
||||
spidbg("ERROR: Failed to allocate the RX DMA channel\n");
|
||||
priv->candma = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->candma)
|
||||
{
|
||||
priv->txdma = sam_dmachannel(0);
|
||||
if (!priv->txdma)
|
||||
{
|
||||
spidbg("ERROR: Failed to allocate the TX DMA channel\n");
|
||||
sam_dmafree(priv->rxdma);
|
||||
priv->rxdma = NULL;
|
||||
priv->candma = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the QSPI semaphore that is used to wake up the waiting
|
||||
* thread when the DMA transfer completes.
|
||||
*/
|
||||
@ -1346,29 +1409,65 @@ FAR struct qspi_dev_s *sam_qspi_initialize(int intf)
|
||||
/* Create a watchdog time to catch DMA timeouts */
|
||||
|
||||
priv->dmadog = wd_create();
|
||||
DEBUGASSERT(priv->dmadog);
|
||||
if (priv->dmadog == NULL)
|
||||
{
|
||||
spidbg("ERROR: Failed to create wdog\n");
|
||||
goto errout_with_dmahandles;
|
||||
}
|
||||
#endif
|
||||
|
||||
qspi_dumpregs(priv, "After initialization");
|
||||
/* Attach the interrupt handler */
|
||||
|
||||
ret = irq_attach(priv->irq, priv->handler);
|
||||
if (ret < 0)
|
||||
{
|
||||
spidbg("ERROR: Failed to attach irq %d\n", priv->irq);
|
||||
goto errout_with_dmadog;
|
||||
}
|
||||
|
||||
/* Perform hardware initialization. Puts the QSPI into an active
|
||||
* state.
|
||||
*/
|
||||
|
||||
ret = qspi_hw_initialize(priv);
|
||||
if (ret < 0)
|
||||
{
|
||||
spidbg("ERROR: Failed to initialize QSPI hardware\n");
|
||||
goto errout_with_irq;
|
||||
}
|
||||
|
||||
/* Enable interrupts at the NVIC */
|
||||
|
||||
priv->initialized = true;
|
||||
up_enable_irq(priv->irq);
|
||||
}
|
||||
|
||||
/* Set to mode=0 and nbits=8 and impossible frequency in order to
|
||||
* force an update.
|
||||
*/
|
||||
|
||||
regval = qspi_getreg(priv, SAM_QSPI_SCR_OFFSET);
|
||||
regval &= ~QSPI_SCR_CPOL;
|
||||
regval |= QSPI_SCR_NCPHA;
|
||||
qspi_putreg(priv, regval, SAM_QSPI_SCR_OFFSET);
|
||||
|
||||
regval = qspi_getreg(priv, SAM_QSPI_MR_OFFSET);
|
||||
regval &= ~QSPI_MR_NBBITS_MASK;
|
||||
regval |= QSPI_MR_NBBITS_8BIT;
|
||||
qspi_putreg(priv, regval, SAM_QSPI_MR_OFFSET);
|
||||
|
||||
priv->nbits = 8;
|
||||
spivdbg("SCR=%08x\n", regval);
|
||||
|
||||
return &priv->qspi;
|
||||
|
||||
errout_with_irq:
|
||||
irq_detach(priv->irq);
|
||||
|
||||
errout_with_dmadog:
|
||||
#ifdef CONFIG_SAMV7_QSPI_DMA
|
||||
wd_delete(priv->dmadog);
|
||||
|
||||
errout_with_dmahandles:
|
||||
sem_destroy(&priv->dmawait);
|
||||
|
||||
if (priv->rxdma)
|
||||
{
|
||||
sam_dmafree(priv->rxdma);
|
||||
priv->rxdma = NULL;
|
||||
}
|
||||
|
||||
if (priv->txdma)
|
||||
{
|
||||
sam_dmafree(priv->txdma);
|
||||
priv->txdma = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
sem_destroy(&priv->exclsem);
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_SAMV7_QSPI */
|
||||
|
Loading…
Reference in New Issue
Block a user