spi_dma_at91.c

Go to the documentation of this file.
00001 
00040 #include "cfg/cfg_spi_dma.h"
00041 
00042 #include "spi_dma_at91.h"
00043 #include "hw/hw_spi_dma.h"
00044 
00045 #include <kern/kfile.h>
00046 #include <struct/fifobuf.h>
00047 #include <struct/kfile_fifo.h>
00048 #include <drv/timer.h>
00049 
00050 #include <cpu/attr.h>
00051 #include <cpu/power.h>
00052 
00053 #include <string.h> /* memset */
00054 
00055 static uint8_t tx_fifo_buffer[CONFIG_SPI_DMA_TXBUFSIZE];
00056 static FIFOBuffer tx_fifo;
00057 static KFileFifo kfifo;
00058 
00059 
00060 INLINE void spi_dma_startTx(void)
00061 {
00062     if (fifo_isempty(&tx_fifo))
00063         return;
00064 
00065     if (SPI0_SR & BV(SPI_TXBUFE))
00066     {
00067         SPI0_PTCR = BV(PDC_TXTDIS);
00068         SPI0_TPR = (reg32_t)tx_fifo.head;
00069         if (tx_fifo.head < tx_fifo.tail)
00070             SPI0_TCR = tx_fifo.tail - tx_fifo.head;
00071         else
00072             SPI0_TCR = tx_fifo.end - tx_fifo.head + 1;
00073 
00074         SPI0_PTCR = BV(PDC_TXTEN);
00075     }
00076 }
00077 
00078 static void spi0_dma_write_irq_handler(void) __attribute__ ((interrupt));
00079 static void spi0_dma_write_irq_handler(void)
00080 {
00081     SPI_DMA_STROBE_ON();
00082     /* Pop sent chars from FIFO */
00083     tx_fifo.head = (uint8_t *)SPI0_TPR;
00084     if (tx_fifo.head > tx_fifo.end)
00085         tx_fifo.head = tx_fifo.begin;
00086 
00087     spi_dma_startTx();
00088 
00089     AIC_EOICR = 0;
00090     SPI_DMA_STROBE_OFF();
00091 }
00092 
00093 
00094 void spi_dma_setclock(uint32_t rate)
00095 {
00096     SPI0_CSR0 &= ~SPI_SCBR;
00097 
00098     ASSERT((uint8_t)DIV_ROUND(CPU_FREQ, rate));
00099     SPI0_CSR0 |= DIV_ROUND(CPU_FREQ, rate) << SPI_SCBR_SHIFT;
00100 }
00101 
00102 static size_t spi_dma_write(UNUSED_ARG(struct KFile *, fd), const void *_buf, size_t size)
00103 {
00104     size_t count, total_wr = 0;
00105     const uint8_t *buf = (const uint8_t *) _buf;
00106 
00107     // copy buffer to internal fifo
00108     while (size)
00109     {
00110         #if CONFIG_SPI_DMA_TX_TIMEOUT != -1
00111             ticks_t start = timer_clock();
00112             while (fifo_isfull(&tx_fifo) && (timer_clock() - start < ms_to_ticks(CONFIG_SPI_DMA_TX_TIMEOUT)))
00113                 cpu_relax();
00114 
00115             if (fifo_isfull(&tx_fifo))
00116                 break;
00117         #else
00118             while (fifo_isfull(&tx_fifo))
00119                 cpu_relax();
00120         #endif /* CONFIG_SPI_DMA_TX_TIMEOUT */
00121 
00122         // FIXME: improve copy performance
00123         count = kfile_write(&kfifo.fd, buf, size);
00124         size -= count;
00125         buf += count;
00126         total_wr += count;
00127         spi_dma_startTx();
00128     }
00129 
00130     return total_wr;
00131 }
00132 
00133 static int spi_dma_flush(UNUSED_ARG(struct KFile *, fd))
00134 {
00135     /* Wait FIFO flush */
00136     while (!fifo_isempty(&tx_fifo))
00137         cpu_relax();
00138 
00139     /* Wait until last bit has been shifted out */
00140     while (!(SPI0_SR & BV(SPI_TXEMPTY)))
00141         cpu_relax();
00142 
00143     return 0;
00144 }
00145 
00146 static void spi0_dma_read_irq_handler(void) __attribute__ ((interrupt));
00147 static void spi0_dma_read_irq_handler(void)
00148 {
00149     /* do nothing */
00150     AIC_EOICR = 0;
00151 }
00152 
00153 /*
00154  * Dummy buffer used to transmit 0xff chars while receiving data.
00155  * This buffer is completetly constant and the compiler should allocate it
00156  * in flash memory.
00157  */
00158 static const uint8_t tx_dummy_buf[CONFIG_SPI_DMA_MAX_RX] = { [0 ... (CONFIG_SPI_DMA_MAX_RX - 1)] = 0xFF };
00159 
00160 static size_t spi_dma_read(struct KFile *fd, void *_buf, size_t size)
00161 {
00162     size_t count, total_rx = 0;
00163     uint8_t *buf = (uint8_t *)_buf;
00164 
00165     spi_dma_flush(fd);
00166 
00167     /* Dummy irq handler that do nothing */
00168     AIC_SVR(SPI0_ID) = spi0_dma_read_irq_handler;
00169 
00170     while (size)
00171     {
00172         count = MIN(size, (size_t)CONFIG_SPI_DMA_MAX_RX);
00173 
00174         SPI0_PTCR = BV(PDC_TXTDIS) | BV(PDC_RXTDIS);
00175 
00176         SPI0_RPR = (reg32_t)buf;
00177         SPI0_RCR = count;
00178         SPI0_TPR = (reg32_t)tx_dummy_buf;
00179         SPI0_TCR = count;
00180 
00181         /* Avoid reading the previous sent char */
00182         *buf = SPI0_RDR;
00183 
00184         /* Start transfer */
00185         SPI0_PTCR = BV(PDC_RXTEN) | BV(PDC_TXTEN);
00186 
00187         /* wait for transfer to finish */
00188         while (!(SPI0_SR & BV(SPI_ENDRX)))
00189             cpu_relax();
00190 
00191         size -= count;
00192         total_rx += count;
00193         buf += count;
00194     }
00195     SPI0_PTCR = BV(PDC_RXTDIS) | BV(PDC_TXTDIS);
00196 
00197     /* set write irq handler back in place */
00198     AIC_SVR(SPI0_ID) = spi0_dma_write_irq_handler;
00199 
00200     return total_rx;
00201 }
00202 
00203 #define SPI_DMA_IRQ_PRIORITY 4
00204 
00205 void spi_dma_init(SpiDmaAt91 *spi)
00206 {
00207     /* Disable PIO on SPI pins */
00208     PIOA_PDR = BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO);
00209 
00210     /* Reset device */
00211     SPI0_CR = BV(SPI_SWRST);
00212 
00213     /*
00214      * Set SPI to master mode, fixed peripheral select, chip select directly connected to a peripheral device,
00215      * SPI clock set to MCK, mode fault detection disabled, loopback disable, NPCS0 active, Delay between CS = 0
00216      */
00217     SPI0_MR = BV(SPI_MSTR) | BV(SPI_MODFDIS);
00218 
00219     /*
00220      * Set SPI mode.
00221      * At reset clock division factor is set to 0, that is
00222      * *forbidden*. Set SPI clock to minimum to keep it valid.
00223      */
00224     SPI0_CSR0 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
00225 
00226     /* Disable all irqs */
00227     SPI0_IDR = 0xFFFFFFFF;
00228     /* Set the vector. */
00229     AIC_SVR(SPI0_ID) = spi0_dma_write_irq_handler;
00230     /* Initialize to edge triggered with defined priority. */
00231     AIC_SMR(SPI0_ID) = AIC_SRCTYPE_INT_EDGE_TRIGGERED | SPI_DMA_IRQ_PRIORITY;
00232     /* Enable the USART IRQ */
00233     AIC_IECR = BV(SPI0_ID);
00234     PMC_PCER = BV(SPI0_ID);
00235 
00236     /* Enable interrupt on tx buffer empty */
00237     SPI0_IER = BV(SPI_ENDTX);
00238 
00239     /* Enable SPI */
00240     SPI0_CR = BV(SPI_SPIEN);
00241 
00242     DB(spi->fd._type = KFT_SPIDMAAT91);
00243     spi->fd.write = spi_dma_write;
00244     spi->fd.read = spi_dma_read;
00245     spi->fd.flush = spi_dma_flush;
00246 
00247     fifo_init(&tx_fifo, tx_fifo_buffer, sizeof(tx_fifo_buffer));
00248     kfilefifo_init(&kfifo, &tx_fifo);
00249 
00250     SPI_DMA_STROBE_INIT();
00251 }