From ab84bdd2d8f11ba70a262a951fd83574227cc4bc Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sun, 16 Jun 2013 10:09:21 -0600 Subject: [PATCH] Re-architected SAM3/4 SPI interface; Change BUSY bit handling in the ADS7843E driver --- arch/arm/src/sam34/chip/sam_spi.h | 3 + arch/arm/src/sam34/sam_spi.c | 215 ++++++++++++++++-------------- arch/arm/src/sam34/sam_spi.h | 38 +----- 3 files changed, 120 insertions(+), 136 deletions(-) diff --git a/arch/arm/src/sam34/chip/sam_spi.h b/arch/arm/src/sam34/chip/sam_spi.h index 887b7f656d..a0344c6a84 100644 --- a/arch/arm/src/sam34/chip/sam_spi.h +++ b/arch/arm/src/sam34/chip/sam_spi.h @@ -49,6 +49,9 @@ /**************************************************************************************** * Pre-processor Definitions ****************************************************************************************/ +/* General definitions ******************************************************************/ + +#define SAM_SPI_NCS 4 /* Four chip selects */ /* SPI register offsets *****************************************************************/ diff --git a/arch/arm/src/sam34/sam_spi.c b/arch/arm/src/sam34/sam_spi.c index cf7518d0dd..65ebfa922b 100644 --- a/arch/arm/src/sam34/sam_spi.c +++ b/arch/arm/src/sam34/sam_spi.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,7 @@ /**************************************************************************** * Definitions ****************************************************************************/ +/* Configuration ************************************************************/ /* Select MCU-specific settings * * For the SAM3U, SPI is driven by the main clock. @@ -81,6 +83,7 @@ # error Unrecognized SAM architecture #endif +/* Debug *******************************************************************/ /* Check if SPI debut is enabled (non-standard.. no support in * include/debug.h */ @@ -106,26 +109,16 @@ * Private Types ****************************************************************************/ -/* The state of one chip select */ - -#ifndef CONFIG_SPI_OWNBUS -struct sam_chipselect_s -{ - uint32_t frequency; /* Requested clock frequency */ - uint32_t actual; /* Actual clock frequency */ - uint8_t nbits; /* Width of word in bits (8 to 16) */ - uint8_t mode; /* Mode 0,1,2,3 */ -}; -#endif - -/* The overall state of the SPI interface */ +/* The state of the one chip select */ struct sam_spidev_s { struct spi_dev_s spidev; /* Externally visible part of the SPI interface */ #ifndef CONFIG_SPI_OWNBUS - sem_t exclsem; /* Held while chip is selected for mutual exclusion */ - struct sam_chipselect_s csstate[4]; + uint32_t frequency; /* Requested clock frequency */ + uint32_t actual; /* Actual clock frequency */ + uint8_t nbits; /* Width of word in bits (8 to 16) */ + uint8_t mode; /* Mode 0,1,2,3 */ #endif uint8_t cs; /* Chip select number */ }; @@ -194,12 +187,17 @@ static const struct spi_ops_s g_spiops = .registercallback = 0, /* Not implemented */ }; -/* SPI device structure */ +#ifdef CONFIG_SPI_OWNBUS +/* Single chip select device structure structure */ -static struct sam_spidev_s g_spidev = -{ - .spidev = { &g_spiops }, -}; +static struct sam_spidev_s g_spidev; + +#else +/* Held while chip is selected for mutual exclusion */ + +static sem_t g_spisem; +static bool g_spinitialized = false; +#endif /* This array maps chip select numbers (0-3) to CSR register addresses */ @@ -330,14 +328,12 @@ static inline uint32_t spi_cs2pcs(FAR struct sam_spidev_s *priv) #ifndef CONFIG_SPI_OWNBUS static int spi_lock(FAR struct spi_dev_s *dev, bool lock) { - FAR struct sam_spidev_s *priv = (FAR struct sam_spidev_s *)dev; - spivdbg("lock=%d\n", lock); if (lock) { /* Take the semaphore (perhaps waiting) */ - while (sem_wait(&priv->exclsem) != 0) + while (sem_wait(&g_spisem) != 0) { /* The only case that an error should occur here is if the wait was awakened * by a signal. @@ -348,8 +344,9 @@ static int spi_lock(FAR struct spi_dev_s *dev, bool lock) } else { - (void)sem_post(&priv->exclsem); + (void)sem_post(&g_spisem); } + return OK; } #endif @@ -381,15 +378,7 @@ static int spi_lock(FAR struct spi_dev_s *dev, bool lock) spivdbg("selected=%d\n", selected); if (selected) { - /* At this point, we expect no chip selected */ - - DEBUGASSERT(priv->cs == 0xff); - - /* Get the chip select number used with this SPI device */ - - priv->cs = sam_spicsnumber(devid); spivdbg("cs=%d\n", priv->cs); - DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3); /* Before writing the TDR, the PCS field in the SPI_MR register must be set * in order to select a slave. @@ -400,19 +389,6 @@ static int spi_lock(FAR struct spi_dev_s *dev, bool lock) regval |= (spi_cs2pcs(priv) << SPI_MR_PCS_SHIFT); putreg32(regval, SAM_SPI_MR); } - else - { - /* At this point, we expect the chip to have already been selected */ - -#ifdef CONFIG_DEBUG - int cs = sam_spicsnumber(devid); - DEBUGASSERT(priv->cs == cs); -#endif - - /* Mark no chip selected */ - - priv->cs = 0xff; - } /* Perform any board-specific chip select operations. PIO chip select * pins may be programmed by the board specific logic in one of two @@ -429,7 +405,7 @@ static int spi_lock(FAR struct spi_dev_s *dev, bool lock) */ sam_spiselect(devid, selected); - } +} /**************************************************************************** * Name: spi_setfrequency @@ -457,16 +433,15 @@ static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency) uint32_t regaddr; spivdbg("cs=%d frequency=%d\n", priv->cs, frequency); - DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3); /* Check if the requested frequency is the same as the frequency selection */ #ifndef CONFIG_SPI_OWNBUS - if (priv->csstate[priv->cs].frequency == frequency) + if (priv->frequency == frequency) { /* We are already at this frequency. Return the actual. */ - return priv->csstate[priv->cs].actual; + return priv->actual; } #endif @@ -492,7 +467,7 @@ static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency) regaddr = g_csraddr[priv->cs]; regval = getreg32(regaddr); - regval &= ~(SPI_CSR_SCBR_MASK|SPI_CSR_DLYBS_MASK|SPI_CSR_DLYBCT_MASK); + regval &= ~(SPI_CSR_SCBR_MASK | SPI_CSR_DLYBS_MASK | SPI_CSR_DLYBCT_MASK); regval |= scbr << SPI_CSR_SCBR_SHIFT; /* DLYBS: Delay Before SPCK. This field defines the delay from NPCS valid to the @@ -534,8 +509,8 @@ static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency) /* Save the frequency setting */ #ifndef CONFIG_SPI_OWNBUS - priv->csstate[priv->cs].frequency = frequency; - priv->csstate[priv->cs].actual = actual; + priv->frequency = frequency; + priv->actual = actual; #endif spidbg("Frequency %d->%d\n", frequency, actual); @@ -564,19 +539,18 @@ static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode) uint32_t regaddr; spivdbg("cs=%d mode=%d\n", priv->cs, mode); - DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3); /* Has the mode changed? */ #ifndef CONFIG_SPI_OWNBUS - if (mode != priv->csstate[priv->cs].mode) + if (mode != priv->mode) { #endif /* Yes... Set the mode appropriately */ regaddr = g_csraddr[priv->cs]; regval = getreg32(regaddr); - regval &= ~(SPI_CSR_CPOL|SPI_CSR_NCPHA); + regval &= ~(SPI_CSR_CPOL | SPI_CSR_NCPHA); switch (mode) { @@ -606,7 +580,7 @@ static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode) /* Save the mode so that subsequent re-configurations will be faster */ #ifndef CONFIG_SPI_OWNBUS - priv->csstate[priv->cs].mode = mode; + priv->mode = mode; } #endif } @@ -634,7 +608,6 @@ static void spi_setbits(FAR struct spi_dev_s *dev, int nbits) spivdbg("cs=%d nbits=%d\n", priv->cs, nbits); DEBUGASSERT(priv && nbits > 7 && nbits < 17); - DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3); /* NOTE: The logic in spi_send and in spi_exchange only handles 8-bit * data at the present time. So the following extra assertion is a @@ -646,7 +619,7 @@ static void spi_setbits(FAR struct spi_dev_s *dev, int nbits) /* Has the number of bits changed? */ #ifndef CONFIG_SPI_OWNBUS - if (nbits != priv->csstate[priv->cs].nbits) + if (nbits != priv->nbits) { #endif /* Yes... Set number of bits appropriately */ @@ -662,7 +635,7 @@ static void spi_setbits(FAR struct spi_dev_s *dev, int nbits) /* Save the selection so the subsequence re-configurations will be faster */ #ifndef CONFIG_SPI_OWNBUS - priv->csstate[priv->cs].nbits = nbits; + priv->nbits = nbits; } #endif } @@ -889,70 +862,106 @@ static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nw * Initialize the selected SPI port * * Input Parameter: - * Port number (for hardware that has mutiple SPI interfaces) + * cs - Chip select number (identifying the "logical" SPI port) * * Returned Value: * Valid SPI device structure reference on succcess; a NULL on failure * ****************************************************************************/ -FAR struct spi_dev_s *up_spiinitialize(int port) +FAR struct spi_dev_s *up_spiinitialize(int cs) { - FAR struct sam_spidev_s *priv = &g_spidev; + FAR struct sam_spidev_s *priv; irqstate_t flags; - /* The SAM3U has only a single SPI port */ + /* The support SAM parts have only a single SPI port */ - spivdbg("port=%d\n", port); - DEBUGASSERT(port == 0); + spivdbg("cs=%d\n", cs); + DEBUGASSERT(cs >= 0 && cs <= SAM_SPI_NCS); - /* Set up the initial state */ - - priv->cs = 0xff; - - /* Enable clocking to the SPI block */ - - flags = irqsave(); - sam_spi_enableclk(); - - /* Configure multiplexed pins as connected on the board. Chip select pins - * must be configured by board-specific logic. +#ifdef CONFIG_SPI_OWNBUS + /* There is only one device on the bus and, therefore, there is only one + * supported chip select. In this case, use the single, pre-allocated + * chip select structure. */ - sam_configgpio(GPIO_SPI0_MISO); - sam_configgpio(GPIO_SPI0_MOSI); - sam_configgpio(GPIO_SPI0_SPCK); + priv = &g_spidev; - /* Disable SPI clocking */ +#else + /* Allocate a new state structure for this chip select. NOTE that there + * is no protection if the same chip select is used in two different + * chip select structures. + */ - putreg32(SPI_CR_SPIDIS, SAM_SPI_CR); + priv = (FAR struct sam_spidev_s *)zalloc(sizeof(struct sam_spidev_s)); + if (!priv) + { + spivdbg("ERROR: Failed to allocate a chip select structure\n", cs); + return NULL; + } +#endif - /* Execute a software reset of the SPI (twice) */ + /* Set up the initial state for this chip select structure. Other fields + * were zeroed by zalloc(). + */ - putreg32(SPI_CR_SWRST, SAM_SPI_CR); - putreg32(SPI_CR_SWRST, SAM_SPI_CR); - irqrestore(flags); - - /* Configure the SPI mode register */ - - putreg32(SPI_MR_MSTR | SPI_MR_MODFDIS, SAM_SPI_MR); - - /* And enable the SPI */ - - putreg32(SPI_CR_SPIEN, SAM_SPI_CR); - up_mdelay(20); - - /* Flush any pending transfers */ - - (void)getreg32(SAM_SPI_SR); - (void)getreg32(SAM_SPI_RDR); - - /* Initialize the SPI semaphore that enforces mutually exclusive access */ + priv->spidev.ops = &g_spiops; + priv->cs = cs; #ifndef CONFIG_SPI_OWNBUS - sem_init(&priv->exclsem, 0, 1); + /* Has the SPI hardware been initialized? */ + + if (!g_spinitialized) #endif - spi_dumpregs("After initialization"); + { + /* Enable clocking to the SPI block */ + + flags = irqsave(); + sam_spi_enableclk(); + + /* Configure multiplexed pins as connected on the board. Chip select + * pins must be configured by board-specific logic. + */ + + sam_configgpio(GPIO_SPI0_MISO); + sam_configgpio(GPIO_SPI0_MOSI); + sam_configgpio(GPIO_SPI0_SPCK); + + /* Disable SPI clocking */ + + putreg32(SPI_CR_SPIDIS, SAM_SPI_CR); + + /* Execute a software reset of the SPI (twice) */ + + putreg32(SPI_CR_SWRST, SAM_SPI_CR); + putreg32(SPI_CR_SWRST, SAM_SPI_CR); + irqrestore(flags); + + /* Configure the SPI mode register */ + + putreg32(SPI_MR_MSTR | SPI_MR_MODFDIS, SAM_SPI_MR); + + /* And enable the SPI */ + + putreg32(SPI_CR_SPIEN, SAM_SPI_CR); + up_mdelay(20); + + /* Flush any pending transfers */ + + (void)getreg32(SAM_SPI_SR); + (void)getreg32(SAM_SPI_RDR); + +#ifndef CONFIG_SPI_OWNBUS + /* Initialize the SPI semaphore that enforces mutually exclusive + * access to the SPI registers. + */ + + sem_init(&g_spisem, 0, 1); + g_spinitialized = true; +#endif + spi_dumpregs("After initialization"); + } + return &priv->spidev; } #endif /* CONFIG_SAM34_SPI */ diff --git a/arch/arm/src/sam34/sam_spi.h b/arch/arm/src/sam34/sam_spi.h index 77e6e26f37..dfcfe20b3a 100644 --- a/arch/arm/src/sam34/sam_spi.h +++ b/arch/arm/src/sam34/sam_spi.h @@ -79,15 +79,13 @@ extern "C" ************************************************************************************/ /**************************************************************************** - * Name: sam_spicsnumber, sam_spiselect, sam_spistatus, and sam_spicmddata + * Name: sam_spiselect, sam_spistatus, and sam_spicmddata * * Description: * These external functions must be provided by board-specific logic. They * include: * - * o sam_spicsnumber and sam_spiselect which are helper functions to - * manage the board-specific aspects of the unique SAM3/4 chip select - * architecture. + * o sam_spiselect is a functions tomanage the board-specific chip selects * o sam_spistatus and sam_spicmddata: Implementations of the status * and cmddata methods of the SPI interface defined by struct spi_ops_ * (see include/nuttx/spi.h). All other methods including @@ -97,10 +95,9 @@ extern "C" * * 1. Provide logic in sam_boardinitialize() to configure SPI chip select * pins. - * 2. Provide sam_spicsnumber(), sam_spiselect() and sam_spistatus() - * functions in your board-specific logic. These functions will perform - * chip selection and status operations using GPIOs in the way your board - * is configured. + * 2. Provide sam_spiselect() and sam_spistatus() functions in your board- + * specific logic. These functions will perform chip selection and + * status operations using GPIOs in the way your board is configured. * 2. If CONFIG_SPI_CMDDATA is defined in the NuttX configuration, provide * sam_spicmddata() functions in your board-specific logic. This * function will perform cmd/data selection operations using GPIOs in @@ -118,31 +115,6 @@ extern "C" struct spi_dev_s; enum spi_dev_e; -/**************************************************************************** - * Name: sam_spicsnumber - * - * Description: - * The SAM3/4 has 4 CS registers for controlling device features. This - * function must be provided by board-specific code. Given a logical device - * ID, this function returns a number from 0 to 3 that identifies one of - * these SAM3/4 CS resources. - * - * If CONFIG_SPI_OWNBUS is not defined and the GPIO is controlled by - * sam_spiselect, then the same CS register may be used to control - * multiple devices. - * - * Input Parameters: - * dev - SPI device info - * devid - Identifies the (logical) device - * - * Returned Values: - * On success, a CS number from 0 to 3 is returned; A negated errno may - * be returned on a failure. - * - ****************************************************************************/ - -int sam_spicsnumber(enum spi_dev_e devid); - /**************************************************************************** * Name: sam_spiselect *