Nut/OS  4.10.3
API Reference
spibus_at91.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008-2009 by egnite GmbH
00003  *
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *
00010  * 1. Redistributions of source code must retain the above copyright
00011  *    notice, this list of conditions and the following disclaimer.
00012  * 2. Redistributions in binary form must reproduce the above copyright
00013  *    notice, this list of conditions and the following disclaimer in the
00014  *    documentation and/or other materials provided with the distribution.
00015  * 3. Neither the name of the copyright holders nor the names of
00016  *    contributors may be used to endorse or promote products derived
00017  *    from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00020  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00021  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00022  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00023  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00024  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00025  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00026  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00027  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00028  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00029  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00030  * SUCH DAMAGE.
00031  *
00032  * For additional information see http://www.ethernut.de/
00033  */
00034 
00044 #include <cfg/spi.h>
00045 
00046 #include <sys/timer.h>
00047 #include <sys/nutdebug.h>
00048 
00049 #include <stdlib.h>
00050 #include <memdebug.h>
00051 
00052 #include <dev/spibus_at91.h>
00053 
00054 #define SPI_DOUBLE_BUFFER_MIN_TRANSFER_SIZE 4
00055 
00056 #if defined(SPIBUS0_DOUBLE_BUFFER) || defined(SPIBUS1_DOUBLE_BUFFER)
00057 
00061 static void At91SpiInterrupt(void *arg)
00062 {
00063     NutEventPostFromIrq((void **)arg);
00064 }
00065 #endif /* SPIBUS0_DOUBLE_BUFFER */
00066 
00074 int At91SpiSetup(NUTSPINODE * node)
00075 {
00076     uint32_t clk;
00077     uint32_t clkdiv;
00078     AT91SPIREG *spireg;
00079 
00080     NUTASSERT(node != NULL);
00081     NUTASSERT(node->node_stat != NULL);
00082     NUTASSERT(node->node_bus != NULL);
00083     NUTASSERT(node->node_bus->bus_base != 0);
00084     spireg = node->node_stat;
00085 
00086     spireg->at91spi_mr &= ~(SPI_MODFDIS | SPI_LLB);
00087     if ((node->node_mode & SPI_MODE_FAULT) == 0) {
00088         spireg->at91spi_mr |= SPI_MODFDIS;
00089     }
00090     if (node->node_mode & SPI_MODE_LOOPBACK) {
00091         spireg->at91spi_mr |= SPI_LLB;
00092     }
00093 
00094     spireg->at91spi_csr &= ~(SPI_BITS | SPI_CPOL | SPI_NCPHA | SPI_CSAAT | SPI_SCBR);
00095     if (node->node_bits) {
00096         spireg->at91spi_csr |= ((uint32_t)(node->node_bits - 8) << SPI_BITS_LSB) & SPI_BITS;
00097     }
00098     if (node->node_mode & SPI_MODE_CPOL) {
00099         spireg->at91spi_csr |= SPI_CPOL;
00100     }
00101     if ((node->node_mode & SPI_MODE_CPHA) == 0) {
00102         spireg->at91spi_csr |= SPI_NCPHA;
00103     }
00104     if (node->node_mode & SPI_MODE_CSKEEP) {
00105         spireg->at91spi_csr |= SPI_CSAAT;
00106     }
00107 
00108     /* Query peripheral clock. */
00109     clk = NutClockGet(NUT_HWCLK_PERIPHERAL);
00110     /* Calculate the SPI clock divider. Avoid rounding errors. */
00111     clkdiv = (clk + node->node_rate - 1) / node->node_rate;
00112     /* The divider value minimum is 1. */
00113     if (clkdiv < 1) {
00114         clkdiv++;
00115     }
00116     /* The divider value maximum is 255. */
00117     else if (clkdiv > 255) {
00118         clkdiv = 255;
00119     }
00120     spireg->at91spi_csr |= clkdiv << SPI_SCBR_LSB;
00121 
00122     /* Update interface parameters. */
00123     node->node_rate = clk / clkdiv;
00124     node->node_mode &= ~SPI_MODE_UPDATE;
00125 
00126     return 0;
00127 }
00128 
00139 int At91SpiBusNodeInit(NUTSPINODE * node)
00140 {
00141     int rc;
00142     NUTSPIBUS *bus;
00143 
00144     /* Sanity check. */
00145     NUTASSERT(node != NULL);
00146     NUTASSERT(node->node_bus != NULL);
00147     bus = node->node_bus;
00148 
00149     /* Try to deactivate the node's chip select. */
00150 #if defined(SPI1_BASE)
00151     if (bus->bus_base == SPI1_BASE) {
00152         rc = At91Spi1ChipSelect(node->node_cs, (node->node_mode & SPI_MODE_CSHIGH) == 0);
00153     } else
00154 #endif
00155     rc = At91Spi0ChipSelect(node->node_cs, (node->node_mode & SPI_MODE_CSHIGH) == 0);
00156 
00157     /* It should not hurt us being called more than once. Thus, we
00158        ** check wether any initialization had been taken place already. */
00159     if (rc == 0 && node->node_stat == NULL) {
00160         /* Allocate and set our shadow registers. */
00161         AT91SPIREG *spireg = malloc(sizeof(AT91SPIREG));
00162         if (spireg) {
00163             /* Set interface defaults. */
00164             spireg->at91spi_mr = SPI_MODFDIS | SPI_MSTR;
00165             switch (node->node_cs) {
00166             case 0:
00167                 spireg->at91spi_mr |= SPI_PCS_0;
00168                 break;
00169             case 1:
00170                 spireg->at91spi_mr |= SPI_PCS_1;
00171                 break;
00172             case 2:
00173                 spireg->at91spi_mr |= SPI_PCS_2;
00174                 break;
00175             case 3:
00176                 spireg->at91spi_mr |= SPI_PCS_3;
00177                 break;
00178             }
00179             spireg->at91spi_csr = 0;
00180             /* Update with node's defaults. */
00181             node->node_stat = (void *)spireg;
00182             At91SpiSetup(node);
00183 
00184             /* 
00185              * Register and enable SPI interrupt handler. 
00186              */
00187 #if !defined(SPIBUS1_POLLING_MODE) && defined(SPI1_BASE)
00188             if (bus->bus_base == SPI1_BASE) {
00189 #if defined(SPIBUS1_DOUBLE_BUFFER)
00190                 NutRegisterIrqHandler(bus->bus_sig, At91SpiInterrupt, &bus->bus_ready);
00191 #else
00192                 NutRegisterIrqHandler(bus->bus_sig, At91SpiBus1Interrupt, &bus->bus_ready);
00193 #endif
00194                 outr(bus->bus_base + SPI_IDR_OFF, (unsigned int) - 1);
00195                 NutIrqEnable(bus->bus_sig);
00196             } else
00197 #endif /* !SPIBUS1_POLLING_MODE */
00198 
00199             {
00200 #if !defined(SPIBUS0_POLLING_MODE)
00201 #if defined(SPIBUS0_DOUBLE_BUFFER)
00202                 NutRegisterIrqHandler(bus->bus_sig, At91SpiInterrupt, &bus->bus_ready);
00203 #else
00204                 NutRegisterIrqHandler(bus->bus_sig, At91SpiBus0Interrupt, &bus->bus_ready);
00205 #endif
00206                 outr(bus->bus_base + SPI_IDR_OFF, (unsigned int) - 1);
00207                 NutIrqEnable(bus->bus_sig);
00208 #endif /* !SPIBUS0_POLLING_MODE */
00209             }
00210         } else {
00211             /* Out of memory? */
00212             rc = -1;
00213         }
00214     }
00215     return rc;
00216 }
00217 
00218 #if defined(SPIBUS0_POLLING_MODE) || defined(SPIBUS1_POLLING_MODE) || defined(SPIBUS0_DOUBLE_BUFFER_HEURISTIC) || defined(SPIBUS1_DOUBLE_BUFFER_HEURISTIC)
00219 
00233 int At91SpiBusPollTransfer(NUTSPINODE * node, CONST void *txbuf, void *rxbuf, int xlen)
00234 {
00235     uint8_t b = 0xff;
00236     uint8_t *txp = (uint8_t *) txbuf;
00237     uint8_t *rxp = (uint8_t *) rxbuf;
00238     uintptr_t base;
00239 
00240     /* Sanity check. */
00241     NUTASSERT(node != NULL);
00242     NUTASSERT(node->node_bus != NULL);
00243     NUTASSERT(node->node_bus->bus_base != 0);
00244     base = node->node_bus->bus_base;
00245 
00246     while (xlen--) {
00247         if (txp) {
00248             b = *txp++;
00249         }
00250         /* Transmission starts by writing the transmit data. */
00251         outr(base + SPI_TDR_OFF, b);
00252         /* Wait for receiver data register full. */
00253         while((inr(base + SPI_SR_OFF) & SPI_RDRF) == 0);
00254         /* Read incoming data. */
00255         b = (uint8_t)inr(base + SPI_RDR_OFF);
00256         if (rxp) {
00257             *rxp++ = b;
00258         }
00259     }
00260     return 0;
00261 }
00262 #endif
00263 
00264 #if defined(SPIBUS0_DOUBLE_BUFFER) || defined(SPIBUS1_DOUBLE_BUFFER)
00265 
00284 int At91SpiBusDblBufTransfer(NUTSPINODE * node, CONST void *txbuf, void *rxbuf, int xlen)
00285 {
00286     uintptr_t base;
00287     uint32_t cr;
00288     uint32_t ir = 0;
00289 
00290 #if defined(SPIBUS0_DOUBLE_BUFFER_HEURISTIC) || defined(SPIBUS1_DOUBLE_BUFFER_HEURISTIC)
00291     if (xlen < SPI_DOUBLE_BUFFER_MIN_TRANSFER_SIZE) {
00292         return At91SpiBusPollTransfer(node, txbuf, rxbuf, xlen);
00293     }
00294 #endif
00295 
00296     /* Sanity check. */
00297     NUTASSERT(node != NULL);
00298     NUTASSERT(node->node_bus != NULL);
00299     NUTASSERT(node->node_bus->bus_base != 0);
00300     base = node->node_bus->bus_base;
00301 
00302     outr(base + PERIPH_PTCR_OFF, PDC_TXTDIS | PDC_RXTDIS);
00303 
00304     if (xlen) {
00305         /* Set first transmit pointer and counter. */
00306         if (txbuf) {
00307             outr(base + PERIPH_TPR_OFF, (uint32_t)txbuf);
00308         } else {
00309             outr(base + PERIPH_TPR_OFF, (uint32_t)rxbuf);
00310         }
00311         cr = PDC_TXTEN;
00312         outr(base + PERIPH_TCR_OFF, xlen);
00313         /* Set first receive pointer and counter. */
00314         if (rxbuf) {
00315             outr(base + PERIPH_RPR_OFF, (uint32_t)rxbuf);
00316             outr(base + PERIPH_RCR_OFF, xlen);
00317             cr |= PDC_RXTEN;
00318             ir = SPI_RXBUFF;
00319         } else {
00320             cr |= PDC_RXTDIS;
00321             ir = SPI_TXBUFE;
00322             outr(base + PERIPH_RPR_OFF, 0);
00323             outr(base + PERIPH_RCR_OFF, 0);
00324         }
00325         outr(base + PERIPH_TNPR_OFF, 0);
00326         outr(base + PERIPH_TNCR_OFF, 0);
00327         outr(base + PERIPH_RNPR_OFF, 0);
00328         outr(base + PERIPH_RNCR_OFF, 0);
00329 
00330         outr(base + SPI_IDR_OFF, (unsigned int) - 1);
00331         outr(base + SPI_IER_OFF, ir);
00332         outr(base + PERIPH_PTCR_OFF, cr);
00333         
00334         NutEventWait(&node->node_bus->bus_ready, NUT_WAIT_INFINITE);
00335         outr(base + PERIPH_PTCR_OFF, PDC_TXTDIS | PDC_RXTDIS);
00336     }
00337     return 0;
00338 }
00339 #endif
00340 
00341 #if defined(SPIBUS0_DOUBLE_BUFFER) || defined(SPIBUS1_DOUBLE_BUFFER)
00342 
00351 int At91SpiBusWait(NUTSPINODE * node, uint32_t tmo)
00352 {
00353     while ((inr(node->node_bus->bus_base + SPI_SR_OFF) & SPI_RXBUFF) == 0) {
00354         if (NutEventWait(&node->node_bus->bus_ready, tmo)) {
00355             return -1;
00356         }
00357     }
00358     return 0;
00359 }
00360 #endif /* SPIBUS_POLLING_MODE */