SAMV71 QSPI: Add support for non-DMA memory transfers

This commit is contained in:
Gregory Nutt 2015-08-28 10:13:46 -06:00
parent 8aefb9d139
commit 3e0affba86

View File

@ -174,6 +174,8 @@ struct sam_qspidev_s
#ifdef CONFIG_SAMV7_QSPI_DMA
bool candma; /* DMA is supported */
uint8_t rxintf; /* RX hardware interface number */
uint8_t txintf; /* TX hardware interface number */
sem_t dmawait; /* Used to wait for DMA completion */
WDOG_ID dmadog; /* Watchdog that handles DMA timeouts */
int result; /* DMA result */
@ -181,11 +183,6 @@ struct sam_qspidev_s
DMA_HANDLE txdma; /* QSPI TX DMA handle */
#endif
#ifdef CONFIG_SAMV7_QSPI_DMA
uint8_t rxintf; /* RX hardware interface number */
uint8_t txintf; /* TX hardware interface number */
#endif
/* Debug stuff */
#ifdef CONFIG_SAMV7_QSPI_DMADEBUG
@ -230,7 +227,6 @@ static inline void qspi_flush(struct sam_qspidev_s *priv);
/* DMA support */
#ifdef CONFIG_SAMV7_QSPI_DMA
#ifdef CONFIG_SAMV7_QSPI_DMADEBUG
# define qspi_rxdma_sample(s,i) sam_dmasample((s)->rxdma, &(s)->rxdmaregs[i])
# define qspi_txdma_sample(s,i) sam_dmasample((s)->txdma, &(s)->txdmaregs[i])
@ -251,6 +247,16 @@ static inline uintptr_t qspi_regaddr(struct sam_qspidev_s *priv,
unsigned int offset);
#endif
static int qspi_memory_enable(struct sam_qspidev_s *priv,
struct qspi_meminfo_s *meminfo);
#ifdef CONFIG_SAMV7_QSPI_DMA
static int qspi_memory_dma(struct sam_qspidev_s *priv,
struct qspi_meminfo_s *meminfo);
#endif
static int qspi_memory_nodma(struct sam_qspidev_s *priv,
struct qspi_meminfo_s *meminfo);
/* Interrupts */
static int qspi_interrupt(struct sam_qspidev_s *priv);
@ -265,7 +271,9 @@ 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,
struct qspi_xfrinfo_s *xfrinfo);
struct qspi_cmdinfo_s *cmdinfo);
static int qspi_memory(struct qspi_dev_s *dev,
struct qspi_meminfo_s *meminfo);
/* Initialization */
@ -285,6 +293,7 @@ static const struct qspi_ops_s g_qspi0ops =
.setmode = qspi_setmode,
.setbits = qspi_setbits,
.command = qspi_command,
.memory = qspi_memory,
};
/* This is the overall state of the QSPI0 controller */
@ -726,6 +735,184 @@ static inline uintptr_t qspi_regaddr(struct sam_qspidev_s *priv,
}
#endif
/****************************************************************************
* Name: qspi_memory_enable
*
* Description:
* Enable the QSPI memory transfer
*
* Input Parameters:
* priv - Device-specific state data
* meminfo - Describes the memory transfer to be performed.
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
*
****************************************************************************/
static int qspi_memory_enable(struct sam_qspidev_s *priv,
struct qspi_meminfo_s *meminfo)
{
uint32_t regval;
/* Write the Instruction code register:
*
* QSPI_ICR_INST(cmd) 8-bit command
* QSPI_ICR_OPT(0) No option
*/
regval = QSPI_ICR_INST(meminfo->cmd) | QSPI_ICR_OPT(0);
qspi_putreg(priv, regval, SAM_QSPI_ICR_OFFSET);
/* Is memory data scrambled? */
if (QSPIMEM_ISSCRAMBLE(meminfo->flags))
{
/* Yes.. set the scramble key */
qspi_putreg(priv, meminfo->key, SAM_QSPI_SKR_OFFSET);
/* Enable the scrambler and enable/disable the random value in the
* key.
*/
regval = QSPI_SMR_SCREN;
if (!QSPIMEM_ISRANDOM(meminfo->flags))
{
/* Disable random value in key */
regval |= QSPI_SMR_RVDIS;
}
qspi_putreg(priv, 0, SAM_QSPI_SMR_OFFSET);
}
else
{
/* Disable the scrambler */
qspi_putreg(priv, 0, SAM_QSPI_SKR_OFFSET);
qspi_putreg(priv, 0, SAM_QSPI_SMR_OFFSET);
}
/* Write Instruction Frame Register:
*
* QSPI_IFR_WIDTH_SINGLE Instruction=single bit
* QSPI_IFR_INSTEN=1 Instruction Enable
* QSPI_IFR_ADDREN=1 Address Enable
* QSPI_IFR_OPTEN=0 Option Disable
* QSPI_IFR_DATAEN=1 Data Enable
* QSPI_IFR_OPTL_* Not used (zero)
* QSPI_IFR_ADDRL=0/1 Depends on meminfo->addrlen;
* QSPI_IFR_TFRTYP_RD/WRMEM Depends on meminfo->flags
* QSPI_IFR_CRM=0 Not continuous read
* QSPI_IFR_NBDUM(0) No dummy cycles
*/
regval = QSPI_IFR_WIDTH_SINGLE | QSPI_IFR_INSTEN | QSPI_IFR_ADDREN |
QSPI_IFR_DATAEN;
if (QSPIMEM_ISWRITE(meminfo->flags))
{
regval |= QSPI_IFR_TFRTYP_WRMEM;
}
else
{
regval |= QSPI_IFR_TFRTYP_RDMEM;
}
if (meminfo->addrlen == 3)
{
regval |= QSPI_IFR_ADDRL_24BIT;
}
else if (meminfo->addrlen == 4)
{
regval |= QSPI_IFR_ADDRL_32BIT;
}
else
{
return -EINVAL;
}
/* Write the instruction frame value */
qspi_putreg(priv, regval, SAM_QSPI_IFR_OFFSET);
(void)qspi_getreg(priv, SAM_QSPI_IFR_OFFSET);
return OK;
}
/****************************************************************************
* Name: qspi_memory_dma
*
* Description:
* Perform one QSPI memory transfer using DMA
*
* Input Parameters:
* priv - Device-specific state data
* meminfo - Describes the memory transfer to be performed.
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
*
****************************************************************************/
#ifdef CONFIG_SAMV7_QSPI_DMA
static int qspi_memory_dma(struct sam_qspidev_s *priv,
struct qspi_meminfo_s *meminfo)
{
#warning Missing Logic
}
#endif
/****************************************************************************
* Name: qspi_memory_nodma
*
* Description:
* Perform one QSPI memory transfer without using DMA
*
* Input Parameters:
* priv - Device-specific state data
* meminfo - Describes the memory transfer to be performed.
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
*
****************************************************************************/
static int qspi_memory_nodma(struct sam_qspidev_s *priv,
struct qspi_meminfo_s *meminfo)
{
uintptr_t memaddr = SAM_QSPIMEM_BASE + meminfo->addr;
/* Enable the memory transfer */
qspi_memory_enable(priv, meminfo);
/* Transfer data to/from QSPI memory */
if (QSPICMD_ISWRITE(meminfo->flags))
{
memcpy((void *)memaddr, meminfo->buffer, meminfo->buflen);
}
else
{
memcpy(meminfo->buffer, (void *)memaddr, meminfo->buflen);
}
MEMORY_SYNC();
/* Indicate the end of the transfer as soon as the transmission
* registers are empty.
*/
while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_INT_TXEMPTY) == 0);
qspi_putreg(priv, QSPI_CR_LASTXFER, SAM_QSPI_CR_OFFSET);
/* Wait for the end of the transfer */
while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_INSTRE) == 0);
return OK;
}
/****************************************************************************
* Name: qspi_lock
*
@ -1003,7 +1190,7 @@ static void qspi_setbits(struct qspi_dev_s *dev, int nbits)
*
* Input Parameters:
* dev - Device-specific state data
* xfrinfo - Describes the transfer to be performed.
* cmdinfo - Describes the command transfer to be performed.
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
@ -1011,48 +1198,48 @@ static void qspi_setbits(struct qspi_dev_s *dev, int nbits)
****************************************************************************/
static int qspi_command(struct qspi_dev_s *dev,
struct qspi_xfrinfo_s *xfrinfo)
struct qspi_cmdinfo_s *cmdinfo)
{
struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev;
uint32_t regval;
uint32_t ifr;
DEBUGASSERT(priv != NULL && xfrinfo != NULL);
DEBUGASSERT(priv != NULL && cmdinfo != NULL);
#ifdef CONFIG_DEBUG_SPI
spivdbg("Transfer:\n");
spivdbg(" flags: %02x\n", xfrinfo->flags);
spivdbg(" cmd: %04x\n", xfrinfo->cmd);
if (QSPIXFR_ISADDRESS(xfrinfo->flags))
spivdbg(" flags: %02x\n", cmdinfo->flags);
spivdbg(" cmd: %04x\n", cmdinfo->cmd);
if (QSPICMD_ISADDRESS(cmdinfo->flags))
{
spivdbg(" address/length: %08lx %d\n",
(unsigned long)xfrinfo->addr, xfrinfo->addrlen);
(unsigned long)cmdinfo->addr, cmdinfo->addrlen);
}
if (QSPIXFR_ISDATA(xfrinfo->flags))
if (QSPICMD_ISDATA(cmdinfo->flags))
{
spivdbg(" %s Data:\n", QSPIXFR_ISWRITE(xfrinfo->flags) ? "Write" : "Read");
spivdbg(" buffer/length: %p %d\n", xfrinfo->buffer, xfrinfo->buflen);
spivdbg(" %s Data:\n", QSPICMD_ISWRITE(cmdinfo->flags) ? "Write" : "Read");
spivdbg(" buffer/length: %p %d\n", cmdinfo->buffer, cmdinfo->buflen);
}
#endif
DEBUGASSERT(xfrinfo->cmd < 256);
DEBUGASSERT(cmdinfo->cmd < 256);
/* Write the instruction address register */
ifr = 0;
if (QSPIXFR_ISADDRESS(xfrinfo->flags))
if (QSPICMD_ISADDRESS(cmdinfo->flags))
{
DEBUGASSERT(xfrinfo->addrlen == 3 || xfrinfo->addrlen == 4);
DEBUGASSERT(cmdinfo->addrlen == 3 || cmdinfo->addrlen == 4);
qspi_putreg(priv, xfrinfo->addr, SAM_QSPI_IFR_OFFSET);
qspi_putreg(priv, cmdinfo->addr, SAM_QSPI_IFR_OFFSET);
ifr |= QSPI_IFR_ADDREN;
if (xfrinfo->addrlen == 3)
if (cmdinfo->addrlen == 3)
{
ifr |= QSPI_IFR_ADDRL_24BIT;
}
else if (xfrinfo->addrlen == 4)
else if (cmdinfo->addrlen == 4)
{
ifr |= QSPI_IFR_ADDRL_32BIT;
}
@ -1068,19 +1255,21 @@ static int qspi_command(struct qspi_dev_s *dev,
* QSPI_ICR_OPT(0) No option
*/
regval = QSPI_ICR_INST(xfrinfo->cmd) | QSPI_ICR_OPT(0);
regval = QSPI_ICR_INST(cmdinfo->cmd) | QSPI_ICR_OPT(0);
qspi_putreg(priv, regval, SAM_QSPI_ICR_OFFSET);
if (QSPIXFR_ISDATA(xfrinfo->flags))
/* Does data accompany the command? */
if (QSPICMD_ISDATA(cmdinfo->flags))
{
uint16_t buflen;
DEBUGASSERT(xfrinfo->buffer != NULL && xfrinfo->buflen > 0);
DEBUGASSERT(cmdinfo->buffer != NULL && cmdinfo->buflen > 0);
/* Make sure that the length is an even multiple of 32-bit words. */
DEBUGASSERT((xfrinfo->buflen & 3) == 0);
buflen = (xfrinfo->buflen + 3) & ~3;
DEBUGASSERT((cmdinfo->buflen & 3) == 0);
buflen = (cmdinfo->buflen + 3) & ~3;
/* Write Instruction Frame Register:
*
@ -1100,7 +1289,7 @@ static int qspi_command(struct qspi_dev_s *dev,
ifr |= QSPI_IFR_WIDTH_SINGLE | QSPI_IFR_INSTEN | QSPI_IFR_DATAEN |
QSPI_IFR_NBDUM(0);
if (QSPIXFR_ISWRITE(xfrinfo->flags))
if (QSPICMD_ISWRITE(cmdinfo->flags))
{
ifr |= QSPI_IFR_TFRTYP_WRITE;
qspi_putreg(priv, ifr, SAM_QSPI_IFR_OFFSET);
@ -1109,7 +1298,7 @@ static int qspi_command(struct qspi_dev_s *dev,
/* Copy the data to write to QSPI_RAM */
memcpy((void *)SAM_QSPIMEM_BASE, xfrinfo->buffer, buflen);
memcpy((void *)SAM_QSPIMEM_BASE, cmdinfo->buffer, buflen);
}
else
{
@ -1120,7 +1309,7 @@ static int qspi_command(struct qspi_dev_s *dev,
/* Copy the data from QSPI memory into the user buffer */
memcpy(xfrinfo->buffer, (const void *)SAM_QSPIMEM_BASE, buflen);
memcpy(cmdinfo->buffer, (const void *)SAM_QSPIMEM_BASE, buflen);
}
MEMORY_SYNC();
@ -1163,6 +1352,42 @@ static int qspi_command(struct qspi_dev_s *dev,
return OK;
}
/****************************************************************************
* Name: qspi_memory
*
* Description:
* Perform one QSPI memory transfer
*
* Input Parameters:
* dev - Device-specific state data
* meminfo - Describes the memory transfer to be performed.
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
*
****************************************************************************/
static int qspi_memory(struct qspi_dev_s *dev,
struct qspi_meminfo_s *meminfo)
{
struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev;
DEBUGASSERT(priv != NULL && meminfo != NULL);
#ifdef CONFIG_SAMV7_QSPI_DMA
/* Can we perform DMA? Should we perform DMA? */
if (priv->candma && meminfo->buflen > CONFIG_SAMV7_QSPI_DMATHRESHOLD)
{
return qspi_memory_dma(priv, meminfo);
}
else
#endif
{
return qspi_memory_nodma(priv, meminfo);
}
}
/****************************************************************************
* Name: qspi_hw_initialize
*