SAMD20: Some progress debugging SPI. Currently hangs

This commit is contained in:
Gregory Nutt 2014-02-20 15:20:42 -06:00
parent 8f469d70ed
commit 33508df77f
5 changed files with 120 additions and 83 deletions

View File

@ -371,8 +371,8 @@ config SAMD_HAVE_SPI
if SAMD_HAVE_SPI
config SAMA5_SPI_REGDEBUG
bool "SPI egister-Level Debug"
config SAMD_SPI_REGDEBUG
bool "SPI register-Level Debug"
default n
depends on DEBUG_SPI
---help---

View File

@ -161,9 +161,6 @@
#define SPI_CTRLA_FORM_MASK (7 << SPI_CTRLA_FORM_SHIFT)
# define SPI_CTRLA_FORM_SPI (0 << SPI_CTRLA_FORM_SHIFT) /* SPI frame (no address) */
# define SPI_CTRLA_FORM_ADDR (2 << SPI_CTRLA_FORM_SHIFT) /* SPI frame (w/address) */
#define SPI_CTRLA_CMODE (1 << 28) /* Bit 28: Communication mode */
# define SPI_CTRLA_ASYNCH (0)
# define SPI_CTRLA_SYNCH SPI_CTRLA_CMODE
#define SPI_CTRLA_CPHA (1 << 28) /* Bit 28: Clock phase */
#define SPI_CTRLA_CPOL (1 << 29) /* Bit 29: Clock polarity */
#define SPI_CTRLA_DORD (1 << 30) /* Bit 30: Data order */

View File

@ -79,7 +79,7 @@
* Configure the SERCOM core source clock.
*
* Two generic clocks are used by the SERCOM: GCLK_SERCOMx_CORE and
* GCLK_SERCOMx_SLOW. The core clock (GCLK_SERCOMx_CORE) is required to
* GCLK_SERCOM_SLOW. The core clock (GCLK_SERCOMx_CORE) is required to
* clock the SERCOM while operating as a master, while the slow clock
* (GCLK_SERCOM_SLOW) is only required for certain functions. SERCOM
* modules must share the same slow GCLK channel ID.
@ -136,7 +136,7 @@ void sercom_coreclk_configure(int sercom, int gclkgen, bool wrlock)
* Configure the SERCOM slow source clock.
*
* Two generic clocks are used by the SERCOM: GCLK_SERCOMx_CORE and
* GCLK_SERCOMx_SLOW. The core clock (GCLK_SERCOMx_CORE) is required to
* GCLK_SERCOM_SLOW. The core clock (GCLK_SERCOMx_CORE) is required to
* clock the SERCOM while operating as a master, while the slow clock
* (GCLK_SERCOM_SLOW) is only required for certain functions. SERCOM
* modules must share the same slow GCLK channel ID.
@ -145,38 +145,50 @@ void sercom_coreclk_configure(int sercom, int gclkgen, bool wrlock)
void sercom_slowclk_configure(int gclkgen)
{
static bool configured = false;
uint16_t regval;
/* Set up the SERCOM_GCLK_ID_SLOW clock */
regval = (SERCOM_GCLK_ID_SLOW << GCLK_CLKCTRL_ID_SHIFT);
/* Select and disable the SERCOM_GCLK_ID_SLOW generic clock */
putreg16(regval, SAM_GCLK_CLKCTRL);
/* Wait for clock to become disabled */
while ((getreg16(SAM_GCLK_CLKCTRL) & GCLK_CLKCTRL_CLKEN) != 0);
/* Select the SERCOM_GCLK_ID_SLOW clock source generator */
regval |= (uint16_t)gclkgen << GCLK_CLKCTRL_GEN_SHIFT;
/* Write the new configuration */
putreg16(regval, SAM_GCLK_CLKCTRL);
/* Enable the GCLK_SERCOM_SLOW generic clock and lock further
* writes to this GCLK. When this bit is written, it will lock
* further writes to the generic clock pointed by the CLKCTRL.ID. The
* generic clock generator pointed by CLKCTRL.GEN and the GENDIV.DIV
* will also be locked.
*
* We lock the SERCOM slow clock because it is common to all SERCOM modules
* and, once set, should not be changed again.
/* Since GCLK_SERCOM_SLOW is shard amongst all SERCOM modules, it should
* only be configured one time.
*/
regval |= (GCLK_CLKCTRL_WRTLOCK | GCLK_CLKCTRL_CLKEN);
putreg16(regval, SAM_GCLK_CLKCTRL);
if (!configured)
{
/* Set up the SERCOM_GCLK_ID_SLOW clock */
regval = (SERCOM_GCLK_ID_SLOW << GCLK_CLKCTRL_ID_SHIFT);
/* Select and disable the SERCOM_GCLK_ID_SLOW generic clock */
putreg16(regval, SAM_GCLK_CLKCTRL);
/* Wait for clock to become disabled */
while ((getreg16(SAM_GCLK_CLKCTRL) & GCLK_CLKCTRL_CLKEN) != 0);
/* Select the SERCOM_GCLK_ID_SLOW clock source generator */
regval |= (uint16_t)gclkgen << GCLK_CLKCTRL_GEN_SHIFT;
/* Write the new configuration */
putreg16(regval, SAM_GCLK_CLKCTRL);
/* Enable the GCLK_SERCOM_SLOW generic clock and lock further
* writes to this GCLK. When this bit is written, it will lock
* further writes to the generic clock pointed by the CLKCTRL.ID. The
* generic clock generator pointed by CLKCTRL.GEN and the GENDIV.DIV
* will also be locked.
*
* We lock the SERCOM slow clock because it is common to all SERCOM modules
* and, once set, should not be changed again.
*/
regval |= (/* GCLK_CLKCTRL_WRTLOCK | */ GCLK_CLKCTRL_CLKEN);
putreg16(regval, SAM_GCLK_CLKCTRL);
/* Now we are configured */
configured = true;
}
}

View File

@ -145,9 +145,9 @@ struct sam_spidev_s
/* Debug stuff */
#ifdef CONFIG_SAMD_SPI_REGDEBUG
bool wrlast; /* Last was a write */
uint32_t addresslast; /* Last address */
uint32_t valuelast; /* Last value */
bool wr; /* Last was a write */
uint32_t regaddr; /* Last address */
uint32_t regval; /* Last value */
int ntimes; /* Number of times */
#endif
};
@ -165,10 +165,8 @@ static bool spi_checkreg(struct sam_spidev_s *priv, bool wr,
# define spi_checkreg(priv,wr,regval,regaddr) (false)
#endif
#if 0 /* Not used */
static uint8_t spi_getreg8(struct sam_spidev_s *priv,
unsigned int offset);
#endif
static void spi_putreg8(struct sam_spidev_s *priv, uint8_t regval,
unsigned int offset);
static uint16_t spi_getreg16(struct sam_spidev_s *priv,
@ -231,6 +229,7 @@ static void spi_recvblock(struct spi_dev_s *dev, void *buffer,
/* Initialization */
static void spi_wait_synchronization(struct sam_spidev_s *priv);
static void spi_pad_configure(struct sam_spidev_s *priv);
/****************************************************************************
@ -577,9 +576,9 @@ static struct sam_spidev_s g_spi5dev =
static bool spi_checkreg(struct sam_spidev_s *priv, bool wr, uint32_t regval,
uint32_t regaddr)
{
if (wr == priv->wrlast && /* Same kind of access? */
regval == priv->valuelast && /* Same value? */
regaddr == priv->addresslast) /* Same address? */
if (wr == priv->wr && /* Same kind of access? */
regval == priv->regval && /* Same value? */
regaddr == priv->regaddr) /* Same address? */
{
/* Yes, then just keep a count of the number of times we did this. */
@ -599,10 +598,10 @@ static bool spi_checkreg(struct sam_spidev_s *priv, bool wr, uint32_t regval,
/* Save information about the new access */
priv->wrlast = wr;
priv->valuelast = regval;
priv->addresslast = regaddr;
priv->ntimes = 0;
priv->wr = wr;
priv->regval = regval;
priv->regaddr = regaddr;
priv->ntimes = 0;
}
/* Return true if this is the first time that we have done this operation */
@ -619,11 +618,10 @@ static bool spi_checkreg(struct sam_spidev_s *priv, bool wr, uint32_t regval,
*
****************************************************************************/
#if 0 /* Not used */
static uint8_t spi_getreg8(struct sam_spidev_s *priv, unsigned int offset)
{
uintptr_t regaddr = priv->base + offset;
uint8_t regval = getreg8(regaddr);
uint8_t regval = getreg8(regaddr);
#ifdef CONFIG_SAMD_SPI_REGDEBUG
if (spi_checkreg(priv, false, (uint32_t)regval, regaddr))
@ -634,7 +632,6 @@ static uint8_t spi_getreg8(struct sam_spidev_s *priv, unsigned int offset)
return regval;
}
#endif
/****************************************************************************
* Name: spi_putreg8
@ -670,7 +667,7 @@ static void spi_putreg8(struct sam_spidev_s *priv, uint8_t regval,
static uint16_t spi_getreg16(struct sam_spidev_s *priv, unsigned int offset)
{
uintptr_t regaddr = priv->base + offset;
uint16_t regval = getreg16(regaddr);
uint16_t regval = getreg16(regaddr);
#ifdef CONFIG_SAMD_SPI_REGDEBUG
if (spi_checkreg(priv, false, (uint32_t)regval, regaddr))
@ -770,16 +767,16 @@ static void spi_putreg32(struct sam_spidev_s *priv, uint32_t regval,
static void spi_dumpregs(struct sam_spidev_s *priv, const char *msg)
{
spivdbg("%s:\n", msg);
spivdbg(" CTRLA:%08x CTRLB:%08x DBGCTRL:%08x\n",
spivdbg(" CTRLA:%08x CTRLB:%08x DBGCTRL:%02x\n",
getreg32(priv->base + SAM_SPI_CTRLA_OFFSET),
getreg32(priv->base + SAM_SPI_CTRLB_OFFSET),
getreg32(priv->base + SAM_SPI_DBGCTRL_OFFSET));
spivdbg(" BAUD:%08x INTEN:%08x INTFLAG:%08x\n",
getreg32(priv->base + SAM_SPI_BAUD_OFFSET),
getreg32(priv->base + SAM_SPI_INTENCLR_OFFSET),
getreg32(priv->base + SAM_SPI_INTFLAG_OFFSET));
spivdbg(" STATUS:%08x ADDR:%08x\n",
getreg32(priv->base + SAM_SPI_STATUS_OFFSET),
getreg8(priv->base + SAM_SPI_DBGCTRL_OFFSET));
spivdbg(" BAUD:%02x INTEN:%02x INTFLAG:%02x\n",
getreg8(priv->base + SAM_SPI_BAUD_OFFSET),
getreg8(priv->base + SAM_SPI_INTENCLR_OFFSET),
getreg8(priv->base + SAM_SPI_INTFLAG_OFFSET));
spivdbg(" STATUS:%04x ADDR:%08x\n",
getreg16(priv->base + SAM_SPI_STATUS_OFFSET),
getreg32(priv->base + SAM_SPI_ADDR_OFFSET));
}
#endif
@ -807,8 +804,8 @@ static int spi_interrupt(struct sam_spidev_s *dev)
* unmasked interrupts).
*/
intflag = sam_serialin8(priv, SAM_SPI_INTFLAG_OFFSET);
inten = sam_serialin8(priv, SAM_SPI_INTENCLR_OFFSET);
intflag = sam_getreg8(priv, SAM_SPI_INTFLAG_OFFSET);
inten = sam_getreg8(priv, SAM_SPI_INTENCLR_OFFSET);
pending = intflag & inten;
/* Handle an incoming, receive byte. The RXC flag is set when there is
@ -962,6 +959,7 @@ static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency)
uint32_t maxfreq;
uint32_t actual;
uint32_t baud;
uint32_t ctrla;
spivdbg("sercom=%d frequency=%d\n", priv->sercom, frequency);
@ -1009,7 +1007,33 @@ static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency)
baud = 255;
}
spi_putreg8(priv, (uint8_t)baud, SAM_SPI_BAUD_OFFSET);
/* Momentarily disable SPI while we apply the new BAUD setting (if it was
* previously enabled)
*/
ctrla = spi_getreg32(priv, SAM_SPI_CTRLA_OFFSET);
if ((ctrla & SPI_CTRLA_ENABLE) != 0)
{
/* Disable SPI.. waiting for synchronization */
spi_putreg32(priv, ctrla & ~SPI_CTRLA_ENABLE, SAM_SPI_CTRLA_OFFSET);
spi_wait_synchronization(priv);
/* Set the new BAUD value */
spi_putreg8(priv, (uint8_t)baud, SAM_SPI_BAUD_OFFSET);
/* Re-enable SPI.. waiting for synchronization */
spi_putreg32(priv, ctrla, SAM_SPI_CTRLA_OFFSET);
spi_wait_synchronization(priv);
}
else
{
/* Set the new BAUD when the SPI is already disabled */
spi_putreg8(priv, (uint8_t)baud, SAM_SPI_BAUD_OFFSET);
}
/* Calculate the new actual frequency */
@ -1189,6 +1213,9 @@ static uint16_t spi_send(struct spi_dev_s *dev, uint16_t wd)
* Returned Value:
* None
*
* Assumptions/Limitations:
* Data must be 16-bit aligned in 9-bit data transfer mode.
*
****************************************************************************/
static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
@ -1270,15 +1297,15 @@ static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
* transferred to the serializer.
*/
while ((spi_getreg32(priv, SAM_SPI_INTFLAG_OFFSET) & SPI_INT_DRE) == 0);
while ((spi_getreg8(priv, SAM_SPI_INTFLAG_OFFSET) & SPI_INT_DRE) == 0);
/* Write the data to transmitted to the DATA Register (TDR) */
spi_putreg32(priv, data, SAM_SPI_DATA_OFFSET);
spi_putreg16(priv, data, SAM_SPI_DATA_OFFSET);
/* Wait for the read data to be available in the DATA register. */
while ((spi_getreg32(priv, SAM_SPI_INTFLAG_OFFSET) & SPI_INT_RXC) == 0);
while ((spi_getreg8(priv, SAM_SPI_INTFLAG_OFFSET) & SPI_INT_RXC) == 0);
/* Check for data overflow. The BUFOVF bit provides the status of the
* next DATA to be read. On buffer overflow, the corresponding DATA
@ -1377,7 +1404,7 @@ static void spi_recvblock(struct spi_dev_s *dev, void *buffer, size_t nwords)
static void spi_wait_synchronization(struct sam_spidev_s *priv)
{
while ((getreg16(priv->base + SAM_SPI_STATUS_OFFSET) & SPI_STATUS_SYNCBUSY) != 0);
while ((spi_getreg16(priv, SAM_SPI_STATUS_OFFSET) & SPI_STATUS_SYNCBUSY) != 0);
}
/****************************************************************************
@ -1506,11 +1533,11 @@ struct spi_dev_s *up_spiinitialize(int port)
sercom_coreclk_configure(priv->sercom, priv->gclkgen, false);
sercom_slowclk_configure(BOARD_SERCOM_SLOW_GCLKGEN);
/* Set the SERCOM in SPI master mode */
/* Set the SERCOM in SPI master mode (no address) */
regval = spi_getreg32(priv, SAM_SPI_CTRLA_OFFSET);
regval &= ~SPI_CTRLA_MODE_MASK;
regval |= SPI_CTRLA_MODE_MASTER;
regval |= (SPI_CTRLA_MODE_MASTER | SPI_CTRLA_FORM_SPI);
spi_putreg32(priv, regval, SAM_SPI_CTRLA_OFFSET);
/* Configure pads */
@ -1528,24 +1555,22 @@ struct spi_dev_s *up_spiinitialize(int port)
*/
regval = (SPI_CTRLA_MSBFIRST | priv->muxconfig);
spi_putreg8(priv, regval, SAM_SPI_CTRLA_OFFSET);
spi_putreg32(priv, regval, SAM_SPI_CTRLA_OFFSET);
/* Enable the receiver. Note that 8-bit data width is assumed initially */
regval = (SPI_CTRLB_RXEN | SPI_CTRLB_CHSIZE_8BITS);
spi_putreg8(priv, regval, SAM_SPI_CTRLB_OFFSET);
spi_putreg32(priv, regval, SAM_SPI_CTRLB_OFFSET);
spi_wait_synchronization(priv);
priv->nbits = 8;
/* Wait until the synchronization is complete */
spi_wait_synchronization(priv);
/* Enable SPI */
regval = spi_getreg32(priv, SAM_SPI_CTRLA_OFFSET);
regval = spi_getreg32(priv, SAM_SPI_CTRLA_OFFSET);
regval |= SPI_CTRLA_ENABLE;
spi_putreg32(priv, regval, SAM_SPI_CTRLA_OFFSET);
spi_wait_synchronization(priv);
/* Disable all interrupts at the SPI source and clear all pending
* status that we can.

View File

@ -618,8 +618,8 @@ Configurations
reconfiguration process.
2. Unless stated otherwise, all configurations generate console
output of on SERCOM4 which is available on EXT1 or EXT3 (see the
section "Serial Consoles" above). The virtual COM port could
output of on SERCOM4 which is available on EXT1, EXT2, or EXT3 (see
the section "Serial Consoles" above). The virtual COM port could
be used, instead, by reconfiguring to use SERCOM3 instead of
SERCOM4:
@ -678,8 +678,8 @@ Configurations
use to set or PATH variable. The path in the that file may not,
however, be correct for your installation.
See also the "NOTE about Windows native toolchains" in the section call
"GNU Toolchain Options" above.
See also the "NOTE about Windows native toolchains" in the section
called "GNU Toolchain Options" above.
Configuration sub-directories
-----------------------------
@ -883,8 +883,11 @@ Configuration sub-directories
be clock related???
- The program seems to be running normally, just producing bad output.
3. The configuration suggests CONFIG_MMCSD_HAVECARDDETECT=y, but as of
3. SPI current hangs so no much progress has been made tested the I/O1
module.
The configuration suggests CONFIG_MMCSD_HAVECARDDETECT=y, but as of
this writing, there is no support for EIC pin interrupts.
4. OLED1 module is untested. These intructions were just ported from
4. OLED1 module is untested. These instructions were just lifted from
the SAM4L Xplained Pro README.txt file.