Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:40

0001 /*  SPI driver for Blackfin
0002  *
0003  *  Copyright (c) 2010 Kallisti Labs, Los Gatos, CA, USA
0004  *             written by Allan Hessenflow <allanh@kallisti.com>
0005  *
0006  *  The license and distribution terms for this file may be
0007  *  found in the file LICENSE in this distribution or at
0008  *  http://www.rtems.org/license/LICENSE.
0009  */
0010 
0011 #include <stdlib.h>
0012 #include <bsp.h>
0013 #include <rtems/error.h>
0014 #include <rtems/bspIo.h>
0015 #include <errno.h>
0016 #include <rtems/libi2c.h>
0017 #include <libcpu/spiRegs.h>
0018 #include <libcpu/spi.h>
0019 
0020 
0021 #ifndef BFIN_REG16
0022 #define BFIN_REG16(base, offset) \
0023         (*((uint16_t volatile *) ((uint8_t *)(base) + (offset))))
0024 #endif
0025 
0026 
0027 static bfin_spi_state_t *bfin_spi;
0028 
0029 
0030 void bfin_spi_isr(int v) {
0031   bfin_spi_state_t *state;
0032   uint16_t r;
0033 
0034   state = bfin_spi;
0035   if (state->len > state->bytes_per_word) {
0036     if (state->wr_ptr) {
0037       if (state->bytes_per_word == 2)
0038         r = *(uint16_t *) state->wr_ptr;
0039       else
0040         r = (uint16_t) *state->wr_ptr;
0041       state->wr_ptr += state->bytes_per_word;
0042     } else
0043       r = state->idle_pattern;
0044     BFIN_REG16(state->base, SPI_TDBR_OFFSET) = r;
0045   }
0046   state->len -= state->bytes_per_word;
0047   if (state->len <= 0) {
0048     /*
0049        The transfers are done, so I don't want to kick off another
0050        transfer or get any more interrupts.  Reading the last word from
0051        SPI_SHADOW instead of SPI_RDBR should prevent it from triggering
0052        another transfer, but that doesn't clear the interrupt flag.  I
0053        could mask the interrupt in the SIC, but that would preclude ever
0054        using the DMA channel that shares the interrupt independently (and
0055        they might just share it with something more important in some other
0056        member of the Blackfin family).  And who knows what problems it
0057        might cause in this code potentially dealing with that still pended
0058        interrupt at the beginning of the next transfer.
0059 
0060        So instead I disable the SPI interface, read the data from RDBR
0061        (thus clearing the interrupt but not triggering another transfer
0062        since the interface is disabled), then re-eanble the interface.
0063        This has the problem that the bf537 tri-states the SPI signals
0064        while the interface is disabled.  Either adding pull-ups on at
0065        least the chip select signals, or using GPIOs for them so they're
0066        not controlled by the SPI module, would be correct fixes for that
0067        (really pull-ups/downs should be added to the SPI CLK and MOSI
0068        signals as well to insure they cannot float into some region that
0069        causes input structures to consume excessive power).  Or they can
0070        all be left alone, assuming that there's enough capacitance on the
0071        lines to prevent any problems for the short time they're being left
0072        disabled.
0073 
0074        An alternative approach I attempted involved switching TIMOD
0075        between RDBR and TDBR when starting and finishing a transfer, but
0076        I didn't get anywhere with that.  In my limited testing TIMOD TDBR
0077        wasn't behaving as I expected it to, but maybe with more
0078        experimentation I'd find some solution there.  However I'm out
0079        of time for this project, at least for now.
0080     */
0081 
0082     BFIN_REG16(state->base, SPI_CTL_OFFSET) &= ~SPI_CTL_SPE;
0083     r = BFIN_REG16(state->base, SPI_RDBR_OFFSET);
0084     BFIN_REG16(state->base, SPI_CTL_OFFSET) |= SPI_CTL_SPE;
0085     rtems_semaphore_release(state->sem);
0086   } else
0087     r = BFIN_REG16(state->base, SPI_RDBR_OFFSET);
0088 
0089   if (state->rd_ptr) {
0090     if (state->bytes_per_word == 2)
0091       *(uint16_t *) state->rd_ptr = r;
0092     else
0093       *state->rd_ptr = (uint8_t) r;
0094     state->rd_ptr += state->bytes_per_word;
0095   }
0096 }
0097 
0098 static rtems_status_code setTFRMode(rtems_libi2c_bus_t *bus,
0099                                     const rtems_libi2c_tfr_mode_t *tfrMode) {
0100   rtems_status_code result;
0101   bfin_spi_state_t *state;
0102   uint32_t divisor;
0103   uint16_t ctrl;
0104 
0105   result = RTEMS_SUCCESSFUL;
0106   state = &((bfin_spi_bus_t *) bus)->p;
0107 
0108   if (result == RTEMS_SUCCESSFUL) {
0109     if (tfrMode->bits_per_char != 8 &&
0110         tfrMode->bits_per_char != 16)
0111       result = RTEMS_INVALID_NUMBER;
0112     if (tfrMode->baudrate <= 0)
0113       result = RTEMS_INVALID_NUMBER;
0114   }
0115   if (result == RTEMS_SUCCESSFUL) {
0116     divisor = (SCLK / 2 + tfrMode->baudrate - 1) /
0117               tfrMode->baudrate;
0118     if (divisor < 2)
0119       divisor = 2;
0120     else if (divisor > 65535)
0121       result = RTEMS_INVALID_NUMBER;
0122   }
0123   if (result == RTEMS_SUCCESSFUL) {
0124     state->idle_pattern = (uint16_t) tfrMode->idle_char;
0125     state->bytes_per_word = (tfrMode->bits_per_char > 8) ? 2 : 1;
0126     BFIN_REG16(state->base, SPI_BAUD_OFFSET) = divisor;
0127     ctrl = BFIN_REG16(state->base, SPI_CTL_OFFSET);
0128     if (tfrMode->lsb_first)
0129       ctrl |= SPI_CTL_LSBF;
0130     else
0131       ctrl &= ~SPI_CTL_LSBF;
0132     if (tfrMode->bits_per_char > 8)
0133       ctrl |= SPI_CTL_SIZE;
0134     else
0135       ctrl &= ~SPI_CTL_SIZE;
0136     if (tfrMode->clock_inv)
0137       ctrl |= SPI_CTL_CPOL;
0138     else
0139       ctrl &= ~SPI_CTL_CPOL;
0140     if (tfrMode->clock_phs)
0141       ctrl |= SPI_CTL_CPHA;
0142     else
0143       ctrl &= ~SPI_CTL_CPHA;
0144     BFIN_REG16(state->base, SPI_CTL_OFFSET) = ctrl;
0145   }
0146 
0147   return result;
0148 }
0149 
0150 static int readWrite(rtems_libi2c_bus_t *bus, uint8_t *rdBuf,
0151                      const uint8_t *wrBuf, int len) {
0152   rtems_status_code result;
0153   bfin_spi_state_t *state;
0154   uint16_t r;
0155 
0156   result = RTEMS_SUCCESSFUL;
0157   state = &((bfin_spi_bus_t *) bus)->p;
0158 
0159   if (len) {
0160     state->rd_ptr = rdBuf;
0161     state->wr_ptr = wrBuf;
0162     state->len = len;
0163     if (state->wr_ptr) {
0164       if (state->bytes_per_word == 2)
0165         r = *(uint16_t *) state->wr_ptr;
0166       else
0167         r = (uint16_t) *state->wr_ptr;
0168       state->wr_ptr += state->bytes_per_word;
0169     } else
0170       r = state->idle_pattern;
0171     BFIN_REG16(state->base, SPI_TDBR_OFFSET) = r;
0172     BFIN_REG16(state->base, SPI_RDBR_OFFSET); /* trigger */
0173     /* wait until done */
0174     do {
0175       result = rtems_semaphore_obtain(state->sem, RTEMS_WAIT, 100);
0176     } while (result == RTEMS_SUCCESSFUL && state->len > 0);
0177   }
0178 
0179   return (result == RTEMS_SUCCESSFUL) ? len : -result;
0180 }
0181 
0182 
0183 rtems_status_code bfin_spi_init(rtems_libi2c_bus_t *bus) {
0184   rtems_status_code result;
0185   bfin_spi_state_t *state;
0186 
0187   state = &((bfin_spi_bus_t *) bus)->p;
0188 
0189   BFIN_REG16(state->base, SPI_CTL_OFFSET) = SPI_CTL_SPE |
0190                                             SPI_CTL_MSTR |
0191                                             SPI_CTL_CPHA |
0192                                             SPI_CTL_TIMOD_RDBR;
0193 
0194   result = rtems_semaphore_create(rtems_build_name('s','p','i','s'),
0195                                   0,
0196                                   RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE,
0197                                   0,
0198                                   &state->sem);
0199   if (result == RTEMS_SUCCESSFUL)
0200     bfin_spi = state; /* for isr */
0201 
0202   return result;
0203 }
0204 
0205 rtems_status_code bfin_spi_send_start(rtems_libi2c_bus_t *bus) {
0206 
0207   return RTEMS_SUCCESSFUL;
0208 }
0209 
0210 int bfin_spi_read_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len) {
0211 
0212   return readWrite(bus, buf, NULL, len);
0213 }
0214 
0215 int bfin_spi_write_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len) {
0216 
0217   return readWrite(bus, NULL, buf, len);
0218 }
0219 
0220 int bfin_spi_ioctl(rtems_libi2c_bus_t *bus, int cmd, void *arg) {
0221   int result;
0222 
0223   result = -RTEMS_NOT_DEFINED;
0224   switch(cmd) {
0225   case RTEMS_LIBI2C_IOCTL_SET_TFRMODE:
0226     result = -setTFRMode(bus, (const rtems_libi2c_tfr_mode_t *) arg);
0227     break;
0228   case RTEMS_LIBI2C_IOCTL_READ_WRITE:
0229     result = readWrite(bus,
0230                        ((rtems_libi2c_read_write_t *) arg)->rd_buf,
0231                        ((rtems_libi2c_read_write_t *) arg)->wr_buf,
0232                        ((rtems_libi2c_read_write_t *) arg)->byte_cnt);
0233     break;
0234   default:
0235     break;
0236   }
0237 
0238   return result;
0239 }
0240