Nut/OS  4.10.3
API Reference
spibus0avr.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 
00055 #include <cfg/spi.h>
00056 #include <cfg/arch/gpio.h>
00057 
00058 #include <dev/spibus_avr.h>
00059 #include <dev/irqreg.h>
00060 #include <sys/event.h>
00061 #include <sys/nutdebug.h>
00062 
00063 #include <errno.h>
00064 #include <stdlib.h>
00065 #include <memdebug.h>
00066 
00067 #if defined(SPI0_CS0_PIO_BIT)
00068 #if defined(SPI0_CS0_PIO_ID)
00069 #undef GPIO_ID
00070 #define GPIO_ID SPI0_CS0_PIO_ID
00071 #include <cfg/arch/porttran.h>
00072 static INLINE void SPI0_CS0_LO(void) { GPIO_SET_LO(SPI0_CS0_PIO_BIT); }
00073 static INLINE void SPI0_CS0_HI(void) { GPIO_SET_HI(SPI0_CS0_PIO_BIT); }
00074 static INLINE void SPI0_CS0_SO(void) { GPIO_OUTPUT(SPI0_CS0_PIO_BIT); }
00075 #else
00076 #define SPI0_CS0_LO()
00077 #define SPI0_CS0_HI()
00078 #define SPI0_CS0_SO()
00079 #endif
00080 #endif
00081 
00082 #if defined(SPI0_CS1_PIO_BIT)
00083 #if defined(SPI0_CS1_PIO_ID)
00084 #undef GPIO_ID
00085 #define GPIO_ID SPI0_CS1_PIO_ID
00086 #include <cfg/arch/porttran.h>
00087 static INLINE void SPI0_CS1_LO(void) { GPIO_SET_LO(SPI0_CS1_PIO_BIT); }
00088 static INLINE void SPI0_CS1_HI(void) { GPIO_SET_HI(SPI0_CS1_PIO_BIT); }
00089 static INLINE void SPI0_CS1_SO(void) { GPIO_OUTPUT(SPI0_CS1_PIO_BIT); }
00090 #else
00091 #define SPI0_CS1_LO()
00092 #define SPI0_CS1_HI()
00093 #define SPI0_CS1_SO()
00094 #endif
00095 #endif
00096 
00097 #if defined(SPI0_CS2_PIO_BIT)
00098 #if defined(SPI0_CS2_PIO_ID)
00099 #undef GPIO_ID
00100 #define GPIO_ID SPI0_CS2_PIO_ID
00101 #include <cfg/arch/porttran.h>
00102 static INLINE void SPI0_CS2_LO(void) { GPIO_SET_LO(SPI0_CS2_PIO_BIT); }
00103 static INLINE void SPI0_CS2_HI(void) { GPIO_SET_HI(SPI0_CS2_PIO_BIT); }
00104 static INLINE void SPI0_CS2_SO(void) { GPIO_OUTPUT(SPI0_CS2_PIO_BIT); }
00105 #else
00106 #define SPI0_CS2_LO()
00107 #define SPI0_CS2_HI()
00108 #define SPI0_CS2_SO()
00109 #endif
00110 #endif
00111 
00112 #if defined(SPI0_CS3_PIO_BIT)
00113 #if defined(SPI0_CS3_PIO_ID)
00114 #undef GPIO_ID
00115 #define GPIO_ID SPI0_CS3_PIO_ID
00116 #include <cfg/arch/porttran.h>
00117 static INLINE void SPI0_CS3_LO(void) { GPIO_SET_LO(SPI0_CS3_PIO_BIT); }
00118 static INLINE void SPI0_CS3_HI(void) { GPIO_SET_HI(SPI0_CS3_PIO_BIT); }
00119 static INLINE void SPI0_CS3_SO(void) { GPIO_OUTPUT(SPI0_CS3_PIO_BIT); }
00120 #else
00121 #define SPI0_CS3_LO()
00122 #define SPI0_CS3_HI()
00123 #define SPI0_CS4_SO()
00124 #endif
00125 #endif
00126 
00130 static int AvrSpi0ChipSelect(uint_fast8_t cs, uint_fast8_t hi)
00131 {
00132     int rc = 0;
00133 
00134     switch (cs) {
00135 #if defined(SPI0_CS0_PIO_BIT)
00136     case 0:
00137         if (hi) {
00138             SPI0_CS0_HI();
00139         } else {
00140             SPI0_CS0_LO();
00141         }
00142         SPI0_CS0_SO();
00143         break;
00144 #endif
00145 #if defined(SPI0_CS1_PIO_BIT)
00146     case 1:
00147         if (hi) {
00148             SPI0_CS1_HI();
00149         } else {
00150             SPI0_CS1_LO();
00151         }
00152         SPI0_CS1_SO();
00153         break;
00154 #endif
00155 #if defined(SPI0_CS2_PIO_BIT)
00156     case 2:
00157         if (hi) {
00158             SPI0_CS2_HI();
00159         } else {
00160             SPI0_CS2_LO();
00161         }
00162         SPI0_CS2_SO();
00163         break;
00164 #endif
00165 #if defined(SPI0_CS3_PIO_BIT)
00166     case 3:
00167         if (hi) {
00168             SPI0_CS3_HI();
00169         } else {
00170             SPI0_CS3_LO();
00171         }
00172         SPI0_CS3_SO();
00173         break;
00174 #endif
00175     default:
00176         errno = EIO;
00177         rc = -1;
00178         break;
00179     }
00180     return rc;
00181 }
00182 
00183 static uint8_t * volatile spi0_txp;
00184 static uint8_t * volatile spi0_rxp;
00185 
00186 #ifndef SPIBUS0_POLLING_MODE
00187 
00188 static HANDLE spi0_que;
00189 static volatile size_t spi0_xc;
00190 
00191 #ifdef SPIBUS0_DOUBLE_BUFFER
00192 /* Buffers used for double buffering. */
00193 static uint8_t * volatile spi0_ntxp;
00194 static uint8_t * volatile spi0_nrxp;
00195 static volatile size_t spi0_nxc;
00196 #endif
00197 
00201 static void AvrSpi0Interrupt(void *arg)
00202 {
00203     uint8_t b;
00204 
00205     /* Get the received byte. */
00206     b = inb(SPDR);
00207     if (spi0_xc) {
00208         if (spi0_rxp) {
00209             *spi0_rxp++ = b;
00210         }
00211         spi0_xc--;
00212     }
00213     if (spi0_xc == 0) {
00214 #ifdef SPIBUS0_DOUBLE_BUFFER
00215         if (spi0_nxc) {
00216             /* More data in secondary buffer. */
00217             spi0_txp = spi0_ntxp;
00218             spi0_rxp = spi0_nrxp;
00219             spi0_xc = spi0_nxc;
00220             spi0_nxc = 0;
00221         }
00222 #endif
00223         NutEventPostFromIrq(&spi0_que);
00224     }
00225     if (spi0_xc) {
00226         if (spi0_txp) {
00227             b = *spi0_txp++;
00228         }
00229         outb(SPDR, b);
00230     }
00231 }
00232 #endif /* SPIBUS0_POLLING_MODE */
00233 
00255 int AvrSpiBus0Transfer(NUTSPINODE * node, CONST void *txbuf, void *rxbuf, int xlen)
00256 {
00257     uint8_t b = 0xff;
00258 
00259     /* Sanity check. */
00260     NUTASSERT(node != NULL);
00261 
00262 #ifdef SPIBUS0_POLLING_MODE
00263     /*
00264      * Polling mode.
00265      */
00266     spi0_txp = (uint8_t *) txbuf;
00267     spi0_rxp = (uint8_t *) rxbuf;
00268 
00269     while (xlen--) {
00270         if (spi0_txp) {
00271             b = *spi0_txp++;
00272         }
00273         outb(SPDR, b);
00274         while ((inb(SPSR) & 0x80) == 0);
00275         b = inb(SPDR);
00276         if (spi0_rxp) {
00277             *spi0_rxp++ = b;
00278         }
00279     }
00280 #else                           /* SPIBUS0_POLLING_MODE */
00281     if (xlen) {
00282 #ifdef SPIBUS0_DOUBLE_BUFFER
00283         /*
00284          * Double buffer interrupt mode.
00285          */
00286         cbi(SPCR, SPIE);
00287         /* Wait until secondary buffer is available. */
00288         while (spi0_nxc) {
00289             sbi(SPCR, SPIE);
00290             NutEventWait(&spi0_que, NUT_WAIT_INFINITE);
00291             cbi(SPCR, SPIE);
00292         }
00293         if (spi0_xc) {
00294             /* Primary buffer in use. Prepare secondary buffer. */
00295             spi0_ntxp = (uint8_t *) txbuf;
00296             spi0_nrxp = (uint8_t *) rxbuf;
00297             spi0_nxc = (size_t) xlen;
00298             sbi(SPCR, SPIE);
00299         } else {
00300             spi0_txp = (uint8_t *) txbuf;
00301             spi0_rxp = (uint8_t *) rxbuf;
00302             spi0_xc = (size_t) xlen;
00303             if (spi0_txp) {
00304                 b = *spi0_txp++;
00305             }
00306             /* Enable and kick interrupts. */
00307             sbi(SPCR, SPIE);
00308             outb(SPDR, b);
00309         }
00310 #else                           /* SPIBUS0_DOUBLE_BUFFER */
00311         /*
00312          * Single buffer interrupt mode.
00313          */
00314         spi0_txp = (uint8_t *) txbuf;
00315         spi0_rxp = (uint8_t *) rxbuf;
00316         spi0_xc = (size_t) xlen;
00317         if (spi0_txp) {
00318             b = *spi0_txp++;
00319         }
00320         /* Enable and kick interrupts. */
00321         sbi(SPCR, SPIE);
00322         outb(SPDR, b);
00323         /* Wait until transfer has finished. */
00324         NutEventWait(&spi0_que, NUT_WAIT_INFINITE);
00325         cbi(SPCR, SPIE);
00326 #endif                          /* SPIBUS0_DOUBLE_BUFFER */
00327     }
00328 #endif                          /* SPIBUS0_POLLING_MODE */
00329     return 0;
00330 }
00331 
00332 #ifdef SPIBUS0_DOUBLE_BUFFER
00333 
00342 int AvrSpiBus0Wait(NUTSPINODE * node, uint32_t tmo)
00343 {
00344     cbi(SPCR, SPIE);
00345     while (spi0_xc) {
00346         sbi(SPCR, SPIE);
00347         if (NutEventWait(&spi0_que, tmo)) {
00348             return -1;
00349         }
00350         cbi(SPCR, SPIE);
00351     }
00352     return 0;
00353 }
00354 #endif                          /* SPIBUS0_DOUBLE_BUFFER */
00355 
00366 int AvrSpiBus0NodeInit(NUTSPINODE * node)
00367 {
00368     int rc;
00369 
00370     /* Sanity check. */
00371     NUTASSERT(node != NULL);
00372 
00373     /* Try to deactivate the node's chip select. */
00374     rc = AvrSpi0ChipSelect(node->node_cs, (node->node_mode & SPI_MODE_CSHIGH) == 0);
00375     /* It should not hurt us being called more than once. Thus, we
00376        ** check wether any initialization had been taken place already. */
00377     if (rc == 0 && node->node_stat == NULL) {
00378         /* Allocate our shadow registers. */
00379         node->node_stat = malloc(sizeof(AVRSPIREG));
00380         if (node->node_stat) {
00381             AvrSpiSetup(node);
00382 #ifndef SPIBUS0_POLLING_MODE
00383             /* Register and enable SPI interrupt handler. */
00384             NutRegisterIrqHandler(node->node_bus->bus_sig, AvrSpi0Interrupt, NULL);
00385 #endif
00386         } else {
00387             /* Out of memory? */
00388             rc = -1;
00389         }
00390     }
00391     return rc;
00392 }
00393 
00405 int AvrSpiBus0Select(NUTSPINODE * node, uint32_t tmo)
00406 {
00407     int rc;
00408 
00409     /* Sanity check. */
00410     NUTASSERT(node != NULL);
00411     NUTASSERT(node->node_stat != NULL);
00412 
00413     /* Allocate the bus. */
00414     rc = NutEventWait(&node->node_bus->bus_mutex, tmo);
00415     if (rc) {
00416         errno = EIO;
00417     } else {
00418         AVRSPIREG *spireg = node->node_stat;
00419 
00420         /* If the mode update bit is set, then update our shadow registers. */
00421         if (node->node_mode & SPI_MODE_UPDATE) {
00422             AvrSpiSetup(node);
00423         }
00424 
00425         /* Even when set to master mode, the SCK pin is not automatically 
00426            ** switched to output. Do it manually, maintaining the polarity. */
00427         if (spireg->avrspi_spcr & _BV(CPOL)) {
00428             cbi(PORTB, 1);
00429         } else {
00430             sbi(PORTB, 1);
00431         }
00432         sbi(DDRB, 1);
00433 
00434         /* Also MOSI requires manual setting. */
00435         cbi(PORTB, 2);
00436         sbi(DDRB, 2);
00437 
00438         /* When SS is configured as input, we may be forced into slave 
00439            ** mode if this pin goes low. Enable the pull-up. */
00440         if (bit_is_clear(DDRB, 0)) {
00441             sbi(PORTB, 0);
00442         }
00443 
00444         /* Enable MISO pull-up to avoid floating. */
00445         sbi(PORTB, 3);
00446 
00447         /* Set SPI mode for this device, using the shadow registers. */
00448         outb(SPCR, spireg->avrspi_spcr);
00449 #if defined(SPI2X)
00450         outb(SPSR, spireg->avrspi_spsr);
00451 #endif
00452 
00453         /* Clean-up the status. */
00454         {
00455             uint8_t ix = inb(SPSR);
00456             ix = inb(SPDR);
00457         }
00458 
00459         /* Finally activate the node's chip select. */
00460         rc = AvrSpi0ChipSelect(node->node_cs, (node->node_mode & SPI_MODE_CSHIGH) != 0);
00461         if (rc) {
00462             /* Release the bus in case of an error. */
00463             NutEventPost(&node->node_bus->bus_mutex);
00464         }
00465     }
00466     return rc;
00467 }
00468 
00477 int AvrSpiBus0Deselect(NUTSPINODE * node)
00478 {
00479     /* Sanity check. */
00480     NUTASSERT(node != NULL);
00481     NUTASSERT(node->node_bus != NULL);
00482 
00483 #ifdef SPIBUS0_DOUBLE_BUFFER
00484     AvrSpiBus0Wait(node, NUT_WAIT_INFINITE);
00485 #endif
00486     /* Deactivate the node's chip select. */
00487     AvrSpi0ChipSelect(node->node_cs, (node->node_mode & SPI_MODE_CSHIGH) == 0);
00488 
00489     /* Release the bus. */
00490     NutEventPost(&node->node_bus->bus_mutex);
00491 
00492     return 0;
00493 }
00494 
00498 NUTSPIBUS spiBus0Avr = {
00499     NULL,                       
00500     NULL,                       
00501     0,                          
00502     &sig_SPI,                   
00503     AvrSpiBus0NodeInit,         
00504     AvrSpiBus0Select,           
00505     AvrSpiBus0Deselect,         
00506     AvrSpiBus0Transfer,         
00507 #ifdef SPIBUS0_DOUBLE_BUFFER
00508     AvrSpiBus0Wait,
00509 #else
00510     NutSpiBusWait,              
00511 #endif
00512     NutSpiBusSetMode,           
00513     NutSpiBusSetRate,           
00514     NutSpiBusSetBits            
00515 };