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 #if defined(SPIBUS0_DOUBLE_BUFFER) || defined(SPIBUS1_DOUBLE_BUFFER)
00055 #warning Double buffered SPI not tested for AT91 targets.
00056 
00059 static void At91SpiInterrupt(void *arg)
00060 {
00061     NutEventPostFromIrq((void **)arg);
00062 }
00063 #endif /* SPIBUS0_DOUBLE_BUFFER */
00064 
00072 int At91SpiSetup(NUTSPINODE * node)
00073 {
00074     uint32_t clk;
00075     uint32_t clkdiv;
00076     AT91SPIREG *spireg;
00077 
00078     NUTASSERT(node != NULL);
00079     NUTASSERT(node->node_stat != NULL);
00080     NUTASSERT(node->node_bus != NULL);
00081     NUTASSERT(node->node_bus->bus_base != 0);
00082     spireg = node->node_stat;
00083 
00084     spireg->at91spi_mr &= ~(SPI_MODFDIS | SPI_LLB);
00085     if ((node->node_mode & SPI_MODE_FAULT) == 0) {
00086         spireg->at91spi_mr |= SPI_MODFDIS;
00087     }
00088     if (node->node_mode & SPI_MODE_LOOPBACK) {
00089         spireg->at91spi_mr |= SPI_LLB;
00090     }
00091 
00092     spireg->at91spi_csr &= ~(SPI_BITS | SPI_CPOL | SPI_NCPHA | SPI_CSAAT | SPI_SCBR);
00093     if (node->node_bits) {
00094         spireg->at91spi_csr |= ((uint32_t)(node->node_bits - 8) << SPI_BITS_LSB) & SPI_BITS;
00095     }
00096     if (node->node_mode & SPI_MODE_CPOL) {
00097         spireg->at91spi_csr |= SPI_CPOL;
00098     }
00099     if ((node->node_mode & SPI_MODE_CPHA) == 0) {
00100         spireg->at91spi_csr |= SPI_NCPHA;
00101     }
00102     if (node->node_mode & SPI_MODE_CSKEEP) {
00103         spireg->at91spi_csr |= SPI_CSAAT;
00104     }
00105 
00106     /* Query peripheral clock. */
00107     clk = NutClockGet(NUT_HWCLK_PERIPHERAL);
00108     /* Calculate the SPI clock divider. Avoid rounding errors. */
00109     clkdiv = (clk + node->node_rate - 1) / node->node_rate;
00110     /* The divider value minimum is 1. */
00111     if (clkdiv < 1) {
00112         clkdiv++;
00113     }
00114     /* The divider value maximum is 255. */
00115     else if (clkdiv > 255) {
00116         clkdiv = 255;
00117     }
00118     spireg->at91spi_csr |= clkdiv << SPI_SCBR_LSB;
00119 
00120     /* Update interface parameters. */
00121     node->node_rate = clk / clkdiv;
00122     node->node_mode &= ~SPI_MODE_UPDATE;
00123 
00124     return 0;
00125 }
00126 
00137 int At91SpiBusNodeInit(NUTSPINODE * node)
00138 {
00139     int rc;
00140     NUTSPIBUS *bus;
00141 
00142     /* Sanity check. */
00143     NUTASSERT(node != NULL);
00144     NUTASSERT(node->node_bus != NULL);
00145     bus = node->node_bus;
00146 
00147     /* Try to deactivate the node's chip select. */
00148 #if defined(SPI1_BASE)
00149     if (bus->bus_base == SPI1_BASE) {
00150         rc = At91Spi1ChipSelect(node->node_cs, (node->node_mode & SPI_MODE_CSHIGH) == 0);
00151     } else
00152 #endif
00153     rc = At91Spi0ChipSelect(node->node_cs, (node->node_mode & SPI_MODE_CSHIGH) == 0);
00154 
00155     /* It should not hurt us being called more than once. Thus, we
00156        ** check wether any initialization had been taken place already. */
00157     if (rc == 0 && node->node_stat == NULL) {
00158         /* Allocate and set our shadow registers. */
00159         AT91SPIREG *spireg = malloc(sizeof(AT91SPIREG));
00160         if (spireg) {
00161             /* Set interface defaults. */
00162             spireg->at91spi_mr = SPI_MODFDIS | SPI_MSTR;
00163             switch (node->node_cs) {
00164             case 0:
00165                 spireg->at91spi_mr |= SPI_PCS_0;
00166                 break;
00167             case 1:
00168                 spireg->at91spi_mr |= SPI_PCS_1;
00169                 break;
00170             case 2:
00171                 spireg->at91spi_mr |= SPI_PCS_2;
00172                 break;
00173             case 3:
00174                 spireg->at91spi_mr |= SPI_PCS_3;
00175                 break;
00176             }
00177             spireg->at91spi_csr = 0;
00178             /* Update with node's defaults. */
00179             node->node_stat = (void *)spireg;
00180             At91SpiSetup(node);
00181 
00182             /* 
00183              * Register and enable SPI interrupt handler. 
00184              */
00185 #if !defined(SPIBUS1_POLLING_MODE) && defined(SPI1_BASE)
00186             if (bus->bus_base == SPI1_BASE) {
00187 #if defined(SPIBUS1_DOUBLE_BUFFER)
00188                 NutRegisterIrqHandler(bus->bus_sig, At91SpiInterrupt, &bus->bus_ready);
00189 #else
00190                 NutRegisterIrqHandler(bus->bus_sig, At91SpiBus1Interrupt, &bus->bus_ready);
00191 #endif
00192                 outr(bus->bus_base + SPI_IDR_OFF, (unsigned int) - 1);
00193                 NutIrqEnable(bus->bus_sig);
00194             } else
00195 #endif /* !SPIBUS1_POLLING_MODE */
00196 
00197             {
00198 #if !defined(SPIBUS0_POLLING_MODE)
00199 #if defined(SPIBUS0_DOUBLE_BUFFER)
00200                 NutRegisterIrqHandler(bus->bus_sig, At91SpiInterrupt, &bus->bus_ready);
00201 #else
00202                 NutRegisterIrqHandler(bus->bus_sig, At91SpiBus0Interrupt, &bus->bus_ready);
00203 #endif
00204                 outr(bus->bus_base + SPI_IDR_OFF, (unsigned int) - 1);
00205                 NutIrqEnable(bus->bus_sig);
00206 #endif /* !SPIBUS0_POLLING_MODE */
00207             }
00208         } else {
00209             /* Out of memory? */
00210             rc = -1;
00211         }
00212     }
00213     return rc;
00214 }
00215 
00216 #if defined(SPIBUS0_POLLING_MODE) || defined(SPIBUS1_POLLING_MODE)
00217 
00231 int At91SpiBusPollTransfer(NUTSPINODE * node, CONST void *txbuf, void *rxbuf, int xlen)
00232 {
00233     uint8_t b = 0xff;
00234     uint8_t *txp = (uint8_t *) txbuf;
00235     uint8_t *rxp = (uint8_t *) rxbuf;
00236     uintptr_t base;
00237 
00238     /* Sanity check. */
00239     NUTASSERT(node != NULL);
00240     NUTASSERT(node->node_bus != NULL);
00241     NUTASSERT(node->node_bus->bus_base != 0);
00242     base = node->node_bus->bus_base;
00243 
00244     while (xlen--) {
00245         if (txp) {
00246             b = *txp++;
00247         }
00248         /* Transmission starts by writing the transmit data. */
00249         outr(base + SPI_TDR_OFF, b);
00250         /* Wait for receiver data register full. */
00251         while((inr(base + SPI_SR_OFF) & SPI_RDRF) == 0);
00252         /* Read incoming data. */
00253         b = (uint8_t)inr(base + SPI_RDR_OFF);
00254         if (rxp) {
00255             *rxp++ = b;
00256         }
00257     }
00258     return 0;
00259 }
00260 #endif
00261 
00262 #if defined(SPIBUS0_DOUBLE_BUFFER) || defined(SPIBUS1_DOUBLE_BUFFER)
00263 
00282 int At91SpiBusDblBufTransfer(NUTSPINODE * node, CONST void *txbuf, void *rxbuf, int xlen)
00283 {
00284     uintptr_t base;
00285     uint32_t cr;
00286     uint32_t ir = 0;
00287 
00288     /* Sanity check. */
00289     NUTASSERT(node != NULL);
00290     NUTASSERT(node->node_bus != NULL);
00291     NUTASSERT(node->node_bus->bus_base != 0);
00292     base = node->node_bus->bus_base;
00293 
00294     outr(base + PERIPH_PTCR_OFF, PDC_TXTDIS | PDC_RXTDIS);
00295 
00296     if (xlen) {
00297         /* Set first transmit pointer and counter. */
00298         if (txbuf) {
00299             outr(base + PERIPH_TPR_OFF, (uint32_t)txbuf);
00300         } else {
00301             outr(base + PERIPH_TPR_OFF, (uint32_t)rxbuf);
00302         }
00303         cr = PDC_TXTEN;
00304         outr(base + PERIPH_TCR_OFF, xlen);
00305         /* Set first receive pointer and counter. */
00306         if (rxbuf) {
00307             outr(base + PERIPH_RPR_OFF, (uint32_t)rxbuf);
00308             outr(base + PERIPH_RCR_OFF, xlen);
00309             cr |= PDC_RXTEN;
00310             ir = SPI_RXBUFF;
00311         } else {
00312             cr |= PDC_RXTDIS;
00313             ir = SPI_TXBUFE;
00314             outr(base + PERIPH_RPR_OFF, 0);
00315             outr(base + PERIPH_RCR_OFF, 0);
00316         }
00317         outr(base + PERIPH_TNPR_OFF, 0);
00318         outr(base + PERIPH_TNCR_OFF, 0);
00319         outr(base + PERIPH_RNPR_OFF, 0);
00320         outr(base + PERIPH_RNCR_OFF, 0);
00321 
00322         outr(base + SPI_IDR_OFF, (unsigned int) - 1);
00323         outr(base + SPI_IER_OFF, ir);
00324         outr(base + PERIPH_PTCR_OFF, cr);
00325         
00326         NutEventWait(&node->node_bus->bus_ready, NUT_WAIT_INFINITE);
00327         outr(base + PERIPH_PTCR_OFF, PDC_TXTDIS | PDC_RXTDIS);
00328     }
00329     return -1;
00330 }
00331 #endif
00332 
00333 #if !defined(SPIBUS0_DOUBLE_BUFFER) || !defined(SPIBUS1_DOUBLE_BUFFER)
00334 
00343 int At91SpiBusWait(NUTSPINODE * node, uint32_t tmo)
00344 {
00345     while ((inr(node->node_bus->bus_base + SPI_SR_OFF) & SPI_RXBUFF) == 0) {
00346         if (NutEventWait(&node->node_bus->bus_ready, tmo)) {
00347             return -1;
00348         }
00349     }
00350     return 0;
00351 }
00352 #endif /* SPIBUS_POLLING_MODE */

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/