/**************************************************************************** * arch/arm/src/imx/imx_spi.c * * Copyright (C) 2009-2010, 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include "up_internal.h" #include "up_arch.h" #include "chip.h" #include "imx_gpio.h" #include "imx_cspi.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* The i.MX1/L supports 2 SPI interfaces. Which have been enabled? */ #ifdef CONFIG_IMX_SPI1 # define SPI1_NDX 0 /* Index to SPI1 in g_spidev[] */ # ifdef CONFIG_IMX_SPI2 # define SPI2_NDX 1 /* Index to SPI2 in g_spidev[] */ # define NSPIS 2 /* Two SPI interfaces: SPI1 & SPI2 */ # else # define NSPIS 1 /* One SPI interface: SPI1 */ # endif #else # ifdef CONFIG_IMX_SPI2 # define SPI2_NDX 0 /* Index to SPI2 in g_spidev[] */ # define NSPIS 1 /* One SPI interface: SPI2 */ # else # define NSPIS 0 /* No SPI interfaces */ # endif #endif /* Compile the rest of the file only if at least one SPI interface has been * enabled. */ #if NSPIS > 0 /* The number of words that will fit in the Tx FIFO */ #define IMX_TXFIFO_WORDS 8 /**************************************************************************** * Private Type Definitions ****************************************************************************/ struct imx_spidev_s { const struct spi_ops_s *ops; /* Common SPI operations */ #ifndef CONFIG_SPI_POLLWAIT sem_t sem; /* Wait for transfer to complete */ #endif /* These following are the source and destination buffers of the transfer. * they are retained in this structure so that they will be accessible * from an interrupt handler. The actual type of the buffer is uint8_t is * nbits <=8 and uint16_t is nbits >8. */ void *txbuffer; /* Source buffer */ void *rxbuffer; /* Destination buffer */ /* These are functions pointers that are configured to perform the * appropriate transfer for the particular kind of exchange that is * occurring. Differnt functions may be selected depending on (1) * if the tx or txbuffer is NULL and depending on the number of bits * per word. */ void (*txword)(struct imx_spidev_s *priv); void (*rxword)(struct imx_spidev_s *priv); uint32_t base; /* SPI register base address */ uint32_t frequency; /* Current desired SCLK frequency */ uint32_t actual; /* Current actual SCLK frequency */ int ntxwords; /* Number of words left to transfer on the Tx FIFO */ int nrxwords; /* Number of words received on the Rx FIFO */ int nwords; /* Number of words to be exchanged */ uint8_t mode; /* Current mode */ uint8_t nbits; /* Current number of bits per word */ #ifndef CONFIG_SPI_POLLWAIT uint8_t irq; /* SPI IRQ number */ #endif }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* SPI register access */ static inline uint32_t spi_getreg(struct imx_spidev_s *priv, unsigned int offset); static inline void spi_putreg(struct imx_spidev_s *priv, unsigned int offset, uint32_t value); /* SPI data transfer */ static void spi_txnull(struct imx_spidev_s *priv); static void spi_txuint16(struct imx_spidev_s *priv); static void spi_txuint8(struct imx_spidev_s *priv); static void spi_rxnull(struct imx_spidev_s *priv); static void spi_rxuint16(struct imx_spidev_s *priv); static void spi_rxuint8(struct imx_spidev_s *priv); static int spi_performtx(struct imx_spidev_s *priv); static inline void spi_performrx(struct imx_spidev_s *priv); static int spi_transfer(struct imx_spidev_s *priv, const void *txbuffer, void *rxbuffer, unsigned int nwords); /* Interrupt handling */ #ifndef CONFIG_SPI_POLLWAIT static inline struct imx_spidev_s *spi_mapirq(int irq); static int spi_interrupt(int irq, void *context); #endif /* SPI methods */ #ifndef CONFIG_SPI_OWNBUS static int spi_lock(FAR struct spi_dev_s *dev, bool lock); #endif static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency); static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode); static void spi_setbits(FAR struct spi_dev_s *dev, int nbits); static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd); #ifdef CONFIG_SPI_EXCHANGE static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, FAR void *rxbuffer, size_t nwords); #else static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size_t nwords); static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nwords); #endif /**************************************************************************** * Private Data ****************************************************************************/ /* Common SPI operations */ static const struct spi_ops_s g_spiops = { #ifndef CONFIG_SPI_OWNBUS .lock = spi_lock, #endif .select = imx_spiselect, /* Provided externally by board logic */ .setfrequency = spi_setfrequency, .setmode = spi_setmode, .setbits = spi_setbits, .status = imx_spistatus, /* Provided externally by board logic */ #ifdef CONFIG_SPI_CMDDATA .cmddata = imx_spicmddata, #endif .send = spi_send, #ifdef CONFIG_SPI_EXCHANGE .exchange = spi_exchange, #else .sndblock = spi_sndblock, .recvblock = spi_recvblock, #endif }; /* This supports is up to two SPI busses/ports */ static struct imx_spidev_s g_spidev[] = { #ifdef CONFIG_IMX_SPI1 { .ops = &g_spiops, .base = IMX_CSPI1_VBASE, #ifndef CONFIG_SPI_POLLWAIT .irq = IMX_IRQ_CSPI1, #endif }, #endif #ifdef CONFIG_IMX_SPI2 { .ops = &g_spiops, .base = IMX_CSPI2_VBASE, #ifndef CONFIG_SPI_POLLWAIT .irq = IMX_IRQ_CSPI2, #endif }, #endif }; /**************************************************************************** * Public Data ****************************************************************************/ /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: spi_getreg * * Description: * Read the SPI register at this offeset * * Input Parameters: * priv - Device-specific state data * offset - Offset to the SPI register from the register base address * * Returned Value: * Value of the register at this offset * ****************************************************************************/ static inline uint32_t spi_getreg(struct imx_spidev_s *priv, unsigned int offset) { return getreg32(priv->base + offset); } /**************************************************************************** * Name: spi_putreg * * Description: * Write the value to the SPI register at this offeset * * Input Parameters: * priv - Device-specific state data * offset - Offset to the SPI register from the register base address * value - Value to write * * Returned Value: * None * ****************************************************************************/ static inline void spi_putreg(struct imx_spidev_s *priv, unsigned int offset, uint32_t value) { putreg32(value, priv->base + offset); } /**************************************************************************** * Name: spi_txnull, spi_txuint16, and spi_txuint8 * * Description: * Transfer all ones, a uint8_t, or uint16_t to Tx FIFO and update the txbuffer * pointer appropriately. The selected function dependes on (1) if there * is a source txbuffer provided, and (2) if the number of bits per * word is <=8 or >8. * * Input Parameters: * priv - Device-specific state data * * Returned Value: * None * ****************************************************************************/ static void spi_txnull(struct imx_spidev_s *priv) { spi_putreg(priv, CSPI_TXD_OFFSET, 0xffff); } static void spi_txuint16(struct imx_spidev_s *priv) { uint16_t *ptr = (uint16_t*)priv->txbuffer; spi_putreg(priv, CSPI_TXD_OFFSET, *ptr++); priv->txbuffer = (void*)ptr; } static void spi_txuint8(struct imx_spidev_s *priv) { uint8_t *ptr = (uint8_t*)priv->txbuffer; spi_putreg(priv, CSPI_TXD_OFFSET, *ptr++); priv->txbuffer = (void*)ptr; } /**************************************************************************** * Name: spi_rxnull,spi_rxuint16, and spi_rxuint8 * * Description: * Discard input, save a uint8_t, or or save a uint16_t from Tx FIFO in the * user rxvbuffer and update the rxbuffer pointer appropriately. The * selected function dependes on (1) if there is a desination rxbuffer * provided, and (2) if the number of bits per word is <=8 or >8. * * Input Parameters: * priv - Device-specific state data * * Returned Value: * None * ****************************************************************************/ static void spi_rxnull(struct imx_spidev_s *priv) { (void)spi_getreg(priv, CSPI_RXD_OFFSET); } static void spi_rxuint16(struct imx_spidev_s *priv) { uint16_t *ptr = (uint16_t*)priv->rxbuffer; *ptr++ = (uint16_t)spi_getreg(priv, CSPI_TXD_OFFSET); priv->rxbuffer = (void*)ptr; } static void spi_rxuint8(struct imx_spidev_s *priv) { uint8_t *ptr = (uint8_t*)priv->rxbuffer; *ptr++ = (uint8_t)spi_getreg(priv, CSPI_TXD_OFFSET); priv->rxbuffer = (void*)ptr; } /**************************************************************************** * Name: spi_performtx * * Description: * If the Tx FIFO is empty, then transfer as many words as we can to * the FIFO. * * Input Parameters: * priv - Device-specific state data * * Returned Value: * The number of words written to the Tx FIFO (a value from 0 to 8, * inclusive). * ****************************************************************************/ static int spi_performtx(struct imx_spidev_s *priv) { uint32_t regval; int ntxd = 0; /* Number of words written to Tx FIFO */ /* Check if the Tx FIFO is empty */ if ((spi_getreg(priv, CSPI_INTCS_OFFSET) & CSPI_INTCS_TE) != 0) { /* Check if all of the Tx words have been sent */ if (priv->ntxwords > 0) { /* No.. Transfer more words until either the TxFIFO is full or * until all of the user provided data has been sent. */ for (; ntxd < priv->ntxwords && ntxd < IMX_TXFIFO_WORDS; ntxd++) { priv->txword(priv); } /* Update the count of words to to transferred */ priv->ntxwords -= ntxd; } else { /* Yes.. The transfer is complete, disable Tx FIFO empty interrupt */ regval = spi_getreg(priv, CSPI_INTCS_OFFSET); regval &= ~CSPI_INTCS_TEEN; spi_putreg(priv, CSPI_INTCS_OFFSET, regval); } } return ntxd; } /**************************************************************************** * Name: spi_performrx * * Description: * Transfer as many bytes as possible from the Rx FIFO to the user Rx * buffer (if one was provided). * * Input Parameters: * priv - Device-specific state data * * Returned Value: * None * ****************************************************************************/ static inline void spi_performrx(struct imx_spidev_s *priv) { /* Loop while data is available in the Rx FIFO */ while ((spi_getreg(priv, CSPI_INTCS_OFFSET) & CSPI_INTCS_RR) != 0) { /* Have all of the requested words been transferred from the Rx FIFO? */ if (priv->nrxwords < priv->nwords) { /* No.. Read more data from Rx FIFO */ priv->rxword(priv); priv->nrxwords++; } } } /**************************************************************************** * Name: spi_startxfr * * Description: * If data was added to the Tx FIFO, then start the exchange * * Input Parameters: * priv - Device-specific state data * ntxd - The number of bytes added to the Tx FIFO by spi_performtx. * * Returned Value: * None * ****************************************************************************/ static void spi_startxfr(struct imx_spidev_s *priv, int ntxd) { uint32_t regval; /* The XCH bit initiates an exchange in master mode. It remains set * remains set while the exchange is in progress but is automatically * clear when all data in the Tx FIFO and shift register are shifted out. * So if we have added data to the Tx FIFO on this interrupt, we must * set the XCH bit to resume the exchange. */ if (ntxd > 0) { regval = spi_getreg(priv, CSPI_CTRL_OFFSET); regval |= CSPI_CTRL_XCH; spi_putreg(priv, CSPI_CTRL_OFFSET, regval); } } /**************************************************************************** * Name: spi_transfer * * Description: * Exchange a block data with the SPI device * * Input Parameters: * priv - Device-specific state data * txbuffer - The buffer of data to send to the device (may be NULL). * rxbuffer - The buffer to receive data from the device (may be NULL). * nwords - The total number of words to be exchanged. If the interface * uses <= 8 bits per word, then this is the number of uint8_t's; * if the interface uses >8 bits per word, then this is the * number of uint16_t's * * Returned Value: * 0: success, <0:Negated error number on failure * ****************************************************************************/ static int spi_transfer(struct imx_spidev_s *priv, const void *txbuffer, void *rxbuffer, unsigned int nwords) { #ifndef CONFIG_SPI_POLLWAIT irqstate_t flags; uint32_t regval; int ret; #endif int ntxd; /* Set up to perform the transfer */ priv->txbuffer = (uint8_t*)txbuffer; /* Source buffer */ priv->rxbuffer = (uint8_t*)rxbuffer; /* Destination buffer */ priv->ntxwords = nwords; /* Number of words left to send */ priv->nrxwords = 0; /* Number of words received */ priv->nwords = nwords; /* Total number of exchanges */ /* Set up the low-level data transfer function pointers */ if (priv->nbits > 8) { priv->txword = spi_txuint16; priv->rxword =spi_rxuint16; } else { priv->txword = spi_txuint8; priv->rxword = spi_rxuint8; } if (!txbuffer) { priv->txword = spi_txnull; } if (!rxbuffer) { priv->rxword = spi_rxnull; } /* Prime the Tx FIFO to start the sequence (saves one interrupt) */ #ifndef CONFIG_SPI_POLLWAIT flags = irqsave(); ntxd = spi_performtx(priv); spi_startxfr(priv, ntxd); /* Enable transmit empty interrupt */ regval = spi_getreg(priv, CSPI_INTCS_OFFSET); regval |= CSPI_INTCS_TEEN; spi_putreg(priv, CSPI_INTCS_OFFSET, regval); irqrestore(flags); /* Wait for the transfer to complete. Since there is no handshake * with SPI, the following should complete even if there are problems * with the transfer, so it should be safe with no timeout. */ do { /* Wait to be signaled from the interrupt handler */ ret = sem_wait(&priv->sem); } while (ret < 0 && errno == EINTR); #else /* Perform the transfer using polling logic. This will totally * dominate the CPU until the transfer is complete. Only recommended * if (1) your SPI is very fast, and (2) if you only use very short * transfers. */ do { /* Handle outgoing Tx FIFO transfers */ ntxd = spi_performtx(priv); /* Handle incoming Rx FIFO transfers */ spi_performrx(priv); /* Resume the transfer */ spi_startxfr(priv, ntxd); /* If there are other threads at this same priority level, * the following may help: */ sched_yield(); } while (priv->nrxwords < priv->nwords); #endif return OK; } /**************************************************************************** * Name: spi_mapirq * * Description: * Map an IRQ number into the appropriate SPI device * * Input Parameters: * irq - The IRQ number to be mapped * * Returned Value: * On success, a reference to the private data structgure for this IRQ. * NULL on failrue. * ****************************************************************************/ #ifndef CONFIG_SPI_POLLWAIT static inline struct imx_spidev_s *spi_mapirq(int irq) { switch (irq) { #ifdef CONFIG_IMX_SPI1 case IMX_IRQ_CSPI1: return &g_spidev[SPI1_NDX]; #endif #ifdef CONFIG_IMX_SPI2 case IMX_IRQ_CSPI2: return &g_spidev[SPI2_NDX]; #endif default: return NULL; } } #endif /**************************************************************************** * Name: spi_interrupt * * Description: * Exchange a block data with the SPI device * * Input Parameters: * priv - Device-specific state data * txbuffer - The buffer of data to send to the device (may be NULL). * rxbuffer - The buffer to receive data from the device (may be NULL). * nwords - The total number of words to be exchanged. If the interface * uses <= 8 bits per word, then this is the number of uint8_t's; * if the interface uses >8 bits per word, then this is the * number of uint16_t's * * Returned Value: * 0: success, <0:Negated error number on failure * ****************************************************************************/ #ifndef CONFIG_SPI_POLLWAIT static int spi_interrupt(int irq, void *context) { struct imx_spidev_s *priv = spi_mapirq(irq); int ntxd; DEBUGASSERT(priv != NULL); /* Handle outgoing Tx FIFO transfers */ ntxd = spi_performtx(priv); /* Handle incoming Rx FIFO transfers */ spi_performrx(priv); /* Resume the transfer */ spi_startxfr(priv, ntxd); /* Check if the transfer is complete */ if (priv->nrxwords >= priv->nwords) { /* Yes, wake up the waiting thread */ sem_post(&priv->sem); } return OK; } #endif /**************************************************************************** * Name: spi_lock * * Description: * On SPI busses where there are multiple devices, it will be necessary to * lock SPI to have exclusive access to the busses for a sequence of * transfers. The bus should be locked before the chip is selected. After * locking the SPI bus, the caller should then also call the setfrequency, * setbits, and setmode methods to make sure that the SPI is properly * configured for the device. If the SPI buss is being shared, then it * may have been left in an incompatible state. * * Input Parameters: * dev - Device-specific state data * lock - true: Lock spi bus, false: unlock SPI bus * * Returned Value: * None * ****************************************************************************/ #ifndef CONFIG_SPI_OWNBUS static int spi_lock(FAR struct spi_dev_s *dev, bool lock) { /* Not implemented */ return -ENOSYS; } #endif /**************************************************************************** * Name: spi_setfrequency * * Description: * Set the SPI frequency. * * Input Parameters: * dev - Device-specific state data * frequency - The SPI frequency requested * * Returned Value: * Returns the actual frequency selected * ****************************************************************************/ static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency) { struct imx_spidev_s *priv = (struct imx_spidev_s *)dev; uint32_t actual; DEBUGASSERT(priv); actual = priv->actual; if (frequency != priv->frequency) { uint32_t freqbits; uint32_t regval; if (frequency >= IMX_PERCLK2_FREQ / 4) { freqbits = CSPI_CTRL_DIV4; actual = IMX_PERCLK2_FREQ / 4; } else if (frequency >= IMX_PERCLK2_FREQ / 8) { freqbits = CSPI_CTRL_DIV8; actual = IMX_PERCLK2_FREQ / 8; } else if (frequency >= IMX_PERCLK2_FREQ / 16) { freqbits = CSPI_CTRL_DIV16; actual = IMX_PERCLK2_FREQ / 16; } else if (frequency >= IMX_PERCLK2_FREQ / 32) { freqbits = CSPI_CTRL_DIV32; actual = IMX_PERCLK2_FREQ / 32; } else if (frequency >= IMX_PERCLK2_FREQ / 64) { freqbits = CSPI_CTRL_DIV64; actual = IMX_PERCLK2_FREQ / 64; } else if (frequency >= IMX_PERCLK2_FREQ / 128) { freqbits = CSPI_CTRL_DIV128; actual = IMX_PERCLK2_FREQ / 128; } else if (frequency >= IMX_PERCLK2_FREQ / 256) { freqbits = CSPI_CTRL_DIV256; actual = IMX_PERCLK2_FREQ / 256; } else /*if (frequency >= IMX_PERCLK2_FREQ / 512) */ { freqbits = CSPI_CTRL_DIV512; actual = IMX_PERCLK2_FREQ / 512; } /* Then set the selected frequency */ regval = spi_getreg(priv, CSPI_CTRL_OFFSET); regval &= ~(CSPI_CTRL_DATARATE_MASK); regval |= freqbits; spi_putreg(priv, CSPI_CTRL_OFFSET, regval); priv->frequency = frequency; priv->actual = actual; } return actual; } /**************************************************************************** * Name: spi_setmode * * Description: * Set the SPI mode. Optional. See enum spi_mode_e for mode definitions * * Input Parameters: * dev - Device-specific state data * mode - The SPI mode requested * * Returned Value: * none * ****************************************************************************/ static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode) { struct imx_spidev_s *priv = (struct imx_spidev_s *)dev; if (priv && mode != priv->mode) { uint32_t modebits; uint32_t regval; /* Select the CTL register bits based on the selected mode */ switch (mode) { case SPIDEV_MODE0: /* CPOL=0 CHPHA=0 */ modebits = 0; break; case SPIDEV_MODE1: /* CPOL=0 CHPHA=1 */ modebits = CSPI_CTRL_PHA; break; case SPIDEV_MODE2: /* CPOL=1 CHPHA=0 */ modebits = CSPI_CTRL_POL; break; case SPIDEV_MODE3: /* CPOL=1 CHPHA=1 */ modebits = CSPI_CTRL_PHA|CSPI_CTRL_POL; break; default: return; } /* Then set the selected mode */ regval = spi_getreg(priv, CSPI_CTRL_OFFSET); regval &= ~(CSPI_CTRL_PHA|CSPI_CTRL_POL); regval |= modebits; spi_putreg(priv, CSPI_CTRL_OFFSET, regval); } } /**************************************************************************** * Name: spi_setbits * * Description: * Set the number of bits per word. * * Input Parameters: * dev - Device-specific state data * nbits - The number of bits requests * * Returned Value: * none * ****************************************************************************/ static void spi_setbits(FAR struct spi_dev_s *dev, int nbits) { struct imx_spidev_s *priv = (struct imx_spidev_s *)dev; if (priv && nbits != priv->nbits && nbits > 0 && nbits <= 16) { uint32_t regval = spi_getreg(priv, CSPI_CTRL_OFFSET); regval &= ~CSPI_CTRL_BITCOUNT_MASK; regval |= ((nbits - 1) << CSPI_CTRL_BITCOUNT_SHIFT); spi_putreg(priv, CSPI_CTRL_OFFSET, regval); priv->nbits = nbits; } } /**************************************************************************** * Name: spi_send * * Description: * Exchange one word on SPI * * Input Parameters: * dev - Device-specific state data * wd - The word to send. the size of the data is determined by the * number of bits selected for the SPI interface. * * Returned Value: * response * ****************************************************************************/ static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd) { struct imx_spidev_s *priv = (struct imx_spidev_s*)dev; uint16_t response = 0; (void)spi_transfer(priv, &wd, &response, 1); return response; } /**************************************************************************** * Name: SPI_EXCHANGE * * Description: * Exahange a block of data from SPI. Required. * * Input Parameters: * dev - Device-specific state data * buffer - A pointer to the buffer of data to be sent * rxbuffer - A pointer to the buffer in which to recieve data * nwords - the length of data that to be exchanged in units of words. * The wordsize is determined by the number of bits-per-word * selected for the SPI interface. If nbits <= 8, the data is * packed into uint8_t's; if nbits >8, the data is packed into uint16_t's * * Returned Value: * None * ****************************************************************************/ #ifdef CONFIG_SPI_EXCHANGE static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, FAR void *rxbuffer, size_t nwords) { struct imx_spidev_s *priv = (struct imx_spidev_s *)dev; (void)spi_transfer(priv, txbuffer, rxbuffer, nwords); } #endif /************************************************************************* * Name: spi_sndblock * * Description: * Send a block of data on SPI * * Input Parameters: * dev - Device-specific state data * buffer - A pointer to the buffer of data to be sent * nwords - the length of data to send from the buffer in number of words. * The wordsize is determined by the number of bits-per-word * selected for the SPI interface. If nbits <= 8, the data is * packed into uint8_t's; if nbits >8, the data is packed into uint16_t's * * Returned Value: * None * ****************************************************************************/ #ifndef CONFIG_SPI_EXCHANGE static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size_t nwords) { struct imx_spidev_s *priv = (struct imx_spidev_s *)dev; (void)spi_transfer(priv, buffer, NULL, nwords); } #endif /**************************************************************************** * Name: spi_recvblock * * Description: * Revice a block of data from SPI * * Input Parameters: * dev - Device-specific state data * buffer - A pointer to the buffer in which to recieve data * nwords - the length of data that can be received in the buffer in number * of words. The wordsize is determined by the number of bits-per-word * selected for the SPI interface. If nbits <= 8, the data is * packed into uint8_t's; if nbits >8, the data is packed into uint16_t's * * Returned Value: * None * ****************************************************************************/ #ifndef CONFIG_SPI_EXCHANGE static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nwords) { struct imx_spidev_s *priv = (struct imx_spidev_s *)dev; (void)spi_transfer(priv, NULL, buffer, nwords); } #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: up_spiinitialize * * Description: * Initialize common parts the selected SPI port. Initialization of * chip select GPIOs must have been performed by board specific logic * prior to calling this function. Specifically: GPIOs should have * been configured for output, and all chip selects disabled. * * One GPIO, SS (PB2 on the eZ8F091) is reserved as a chip select. However, * If multiple devices on on the bus, then multiple chip selects will be * required. Theregore, all GPIO chip management is deferred to board- * specific logic. * * Input Parameter: * Port number (for hardware that has mutiple SPI interfaces) * * Returned Value: * Valid SPI device structre reference on succcess; a NULL on failure * ****************************************************************************/ FAR struct spi_dev_s *up_spiinitialize(int port) { struct imx_spidev_s *priv; uint8_t regval; /* Only the SPI1 interface is supported */ switch (port) { #ifdef CONFIG_IMX_SPI1 case 1: /* Select SPI1 */ priv = &g_spidev[SPI1_NDX]; /* Configure SPI1 GPIOs (NOTE that SS is not initialized here, the * logic in this file makes no assumptions about chip select) */ imxgpio_configpfinput(GPIOC, 13); /* Port C, pin 13: RDY */ imxgpio_configpfoutput(GPIOC, 14); /* Port C, pin 14: SCLK */ imxgpio_configpfinput(GPIOC, 16); /* Port C, pin 16: MISO */ imxgpio_configpfoutput(GPIOC, 17); /* Port C, pin 17: MOSI */ break; #endif /* CONFIG_IMX_SPI1 */ #ifdef CONFIG_IMX_SPI2 case 2: /* Select SPI2 */ priv = &g_spidev[SPI2_NDX]; /* Configure SPI2 GPIOs */ /* SCLK: AIN of Port A, pin 0 -OR- AIN of Port D, pin 7 */ #if 1 imxgpio_configoutput(GPIOA, 0); /* Set GIUS=1 OCR=0 DIR=OUT */ #else imxgpio_configoutput(GPIOD, 7); /* Set GIUS=1 OCR=0 DIR=OUT */ #endif /* SS: AIN of Port A, pin 17 -OR- AIN of Port D, pin 8.(NOTE that SS * is not initialized here, the logic in this file makes no assumptions * about chip select) */ /* RXD: AOUT of Port A, pin 1 -OR- AOUT of Port D, pin 9 */ #if 1 imxgpio_configinput(GPIOA, 1); /* Set GIUS=1 OCR=0 DIR=IN */ /* Select input from SPI2_RXD_0 pin (AOUT Port A, pin 1) */ regval = getreg32(IMX_SC_FMCR); regval &= ~FMCR_SPI2_RXDSEL; putreg32(regval, IMX_SC_FMCR); #else imxgpio_configinput(GPIOD, 9); /* Set GIUS=1 OCR=0 DIR=IN */ /* Select input from SPI2_RXD_1 pin (AOUT Port D, pin 9) */ regval = getreg32(IMX_SC_FMCR); regval |= FMCR_SPI2_RXDSEL; putreg32(regval, IMX_SC_FMCR); #endif /* TXD: BIN of Port D, pin 31 -OR- AIN of Port D, pin 10 */ #if 1 imxgpio_configinput(GPIOD, 31); imxgpio_ocrbin(GPIOD, 31); imxgpio_dirout(GPIOD, 31); #else imxgpio_configoutput(GPIOD, 10); #endif break; #endif /* CONFIG_IMX_SPI2 */ default: return NULL; } /* Initialize the state structure */ #ifndef CONFIG_SPI_POLLWAIT sem_init(&priv->sem, 0, 0); #endif /* Initialize control register: min frequency, ignore ready, master mode, mode=0, 8-bit */ spi_putreg(priv, CSPI_CTRL_OFFSET, CSPI_CTRL_DIV512 | /* Lowest frequency */ CSPI_CTRL_DRCTL_IGNRDY | /* Ignore ready */ CSPI_CTRL_MODE | /* Master mode */ (7 << CSPI_CTRL_BITCOUNT_SHIFT)); /* 8-bit data */ /* Make sure state agrees with data */ priv->mode = SPIDEV_MODE0; priv->nbits = 8; /* Set the initial clock frequency for identification mode < 400kHz */ spi_setfrequency((FAR struct spi_dev_s *)priv, 400000); /* Enable interrupts on data ready (and certain error conditions */ #ifndef CONFIG_SPI_POLLWAIT spi_putreg(priv, CSPI_INTCS_OFFSET, CSPI_INTCS_RREN | /* RXFIFO Data Ready Interrupt Enable */ CSPI_INTCS_ROEN | /* RXFIFO Overflow Interrupt Enable */ CSPI_INTCS_BOEN); /* Bit Count Overflow Interrupt Enable */ #else spi_putreg(priv, CSPI_INTCS_OFFSET, 0); /* No interrupts */ #endif /* Set the clock source=bit clock and number of clocks inserted between * transactions = 2. */ spi_putreg(priv, CSPI_SPCR_OFFSET, 2); /* No DMA */ spi_putreg(priv, CSPI_DMA_OFFSET, 0); /* Attach the interrupt */ #ifndef CONFIG_SPI_POLLWAIT irq_attach(priv->irq, (xcpt_t)spi_interrupt); #endif /* Enable SPI */ regval = spi_getreg(priv, CSPI_CTRL_OFFSET); regval |= CSPI_CTRL_SPIEN; spi_putreg(priv, CSPI_CTRL_OFFSET, regval); /* Enable SPI interrupts */ #ifndef CONFIG_SPI_POLLWAIT up_enable_irq(priv->irq); #endif return (FAR struct spi_dev_s *)priv; } #endif /* NSPIS > 0 */