source: rtems/c/src/lib/libcpu/bfin/serial/spi.c @ 9dd2fdb9

5
Last change on this file since 9dd2fdb9 was 9dd2fdb9, checked in by Chris Johns <chrisj@…>, on 01/04/18 at 07:55:19

bsps/bfin: Use public include path

Update #3254.

  • Property mode set to 100644
File size: 7.5 KB
Line 
1/*  SPI driver for Blackfin
2 *
3 *  Copyright (c) 2010 Kallisti Labs, Los Gatos, CA, USA
4 *             written by Allan Hessenflow <allanh@kallisti.com>
5 *
6 *  The license and distribution terms for this file may be
7 *  found in the file LICENSE in this distribution or at
8 *  http://www.rtems.org/license/LICENSE.
9 */
10
11#include <stdlib.h>
12#include <bsp.h>
13#include <rtems/error.h>
14#include <rtems/bspIo.h>
15#include <errno.h>
16#include <rtems/libi2c.h>
17#include <libcpu/spiRegs.h>
18#include <libcpu/spi.h>
19
20
21#ifndef BFIN_REG16
22#define BFIN_REG16(base, offset) \
23        (*((uint16_t volatile *) ((uint8_t *)(base) + (offset))))
24#endif
25
26
27static bfin_spi_state_t *bfin_spi;
28
29
30void bfin_spi_isr(int v) {
31  bfin_spi_state_t *state;
32  uint16_t r;
33
34  state = bfin_spi;
35  if (state->len > state->bytes_per_word) {
36    if (state->wr_ptr) {
37      if (state->bytes_per_word == 2)
38        r = *(uint16_t *) state->wr_ptr;
39      else
40        r = (uint16_t) *state->wr_ptr;
41      state->wr_ptr += state->bytes_per_word;
42    } else
43      r = state->idle_pattern;
44    BFIN_REG16(state->base, SPI_TDBR_OFFSET) = r;
45  }
46  state->len -= state->bytes_per_word;
47  if (state->len <= 0) {
48    /*
49       The transfers are done, so I don't want to kick off another
50       transfer or get any more interrupts.  Reading the last word from
51       SPI_SHADOW instead of SPI_RDBR should prevent it from triggering
52       another transfer, but that doesn't clear the interrupt flag.  I
53       could mask the interrupt in the SIC, but that would preclude ever
54       using the DMA channel that shares the interrupt independently (and
55       they might just share it with something more important in some other
56       member of the Blackfin family).  And who knows what problems it
57       might cause in this code potentially dealing with that still pended
58       interrupt at the beginning of the next transfer.
59
60       So instead I disable the SPI interface, read the data from RDBR
61       (thus clearing the interrupt but not triggering another transfer
62       since the interface is disabled), then re-eanble the interface.
63       This has the problem that the bf537 tri-states the SPI signals
64       while the interface is disabled.  Either adding pull-ups on at
65       least the chip select signals, or using GPIOs for them so they're
66       not controlled by the SPI module, would be correct fixes for that
67       (really pull-ups/downs should be added to the SPI CLK and MOSI
68       signals as well to insure they cannot float into some region that
69       causes input structures to consume excessive power).  Or they can
70       all be left alone, assuming that there's enough capacitance on the
71       lines to prevent any problems for the short time they're being left
72       disabled.
73
74       An alternative approach I attempted involved switching TIMOD
75       between RDBR and TDBR when starting and finishing a transfer, but
76       I didn't get anywhere with that.  In my limited testing TIMOD TDBR
77       wasn't behaving as I expected it to, but maybe with more
78       experimentation I'd find some solution there.  However I'm out
79       of time for this project, at least for now.
80    */
81
82    BFIN_REG16(state->base, SPI_CTL_OFFSET) &= ~SPI_CTL_SPE;
83    r = BFIN_REG16(state->base, SPI_RDBR_OFFSET);
84    BFIN_REG16(state->base, SPI_CTL_OFFSET) |= SPI_CTL_SPE;
85    rtems_semaphore_release(state->sem);
86  } else
87    r = BFIN_REG16(state->base, SPI_RDBR_OFFSET);
88
89  if (state->rd_ptr) {
90    if (state->bytes_per_word == 2)
91      *(uint16_t *) state->rd_ptr = r;
92    else
93      *state->rd_ptr = (uint8_t) r;
94    state->rd_ptr += state->bytes_per_word;
95  }
96}
97
98static rtems_status_code setTFRMode(rtems_libi2c_bus_t *bus,
99                                    const rtems_libi2c_tfr_mode_t *tfrMode) {
100  rtems_status_code result;
101  bfin_spi_state_t *state;
102  uint32_t divisor;
103  uint16_t ctrl;
104
105  result = RTEMS_SUCCESSFUL;
106  state = &((bfin_spi_bus_t *) bus)->p;
107
108  if (result == RTEMS_SUCCESSFUL) {
109    if (tfrMode->bits_per_char != 8 &&
110        tfrMode->bits_per_char != 16)
111      result = RTEMS_INVALID_NUMBER;
112    if (tfrMode->baudrate <= 0)
113      result = RTEMS_INVALID_NUMBER;
114  }
115  if (result == RTEMS_SUCCESSFUL) {
116    divisor = (SCLK / 2 + tfrMode->baudrate - 1) /
117              tfrMode->baudrate;
118    if (divisor < 2)
119      divisor = 2;
120    else if (divisor > 65535)
121      result = RTEMS_INVALID_NUMBER;
122  }
123  if (result == RTEMS_SUCCESSFUL) {
124    state->idle_pattern = (uint16_t) tfrMode->idle_char;
125    state->bytes_per_word = (tfrMode->bits_per_char > 8) ? 2 : 1;
126    BFIN_REG16(state->base, SPI_BAUD_OFFSET) = divisor;
127    ctrl = BFIN_REG16(state->base, SPI_CTL_OFFSET);
128    if (tfrMode->lsb_first)
129      ctrl |= SPI_CTL_LSBF;
130    else
131      ctrl &= ~SPI_CTL_LSBF;
132    if (tfrMode->bits_per_char > 8)
133      ctrl |= SPI_CTL_SIZE;
134    else
135      ctrl &= ~SPI_CTL_SIZE;
136    if (tfrMode->clock_inv)
137      ctrl |= SPI_CTL_CPOL;
138    else
139      ctrl &= ~SPI_CTL_CPOL;
140    if (tfrMode->clock_phs)
141      ctrl |= SPI_CTL_CPHA;
142    else
143      ctrl &= ~SPI_CTL_CPHA;
144    BFIN_REG16(state->base, SPI_CTL_OFFSET) = ctrl;
145  }
146
147  return result;
148}
149
150static int readWrite(rtems_libi2c_bus_t *bus, uint8_t *rdBuf,
151                     const uint8_t *wrBuf, int len) {
152  rtems_status_code result;
153  bfin_spi_state_t *state;
154  uint16_t r;
155
156  result = RTEMS_SUCCESSFUL;
157  state = &((bfin_spi_bus_t *) bus)->p;
158
159  if (len) {
160    state->rd_ptr = rdBuf;
161    state->wr_ptr = wrBuf;
162    state->len = len;
163    if (state->wr_ptr) {
164      if (state->bytes_per_word == 2)
165        r = *(uint16_t *) state->wr_ptr;
166      else
167        r = (uint16_t) *state->wr_ptr;
168      state->wr_ptr += state->bytes_per_word;
169    } else
170      r = state->idle_pattern;
171    BFIN_REG16(state->base, SPI_TDBR_OFFSET) = r;
172    BFIN_REG16(state->base, SPI_RDBR_OFFSET); /* trigger */
173    /* wait until done */
174    do {
175      result = rtems_semaphore_obtain(state->sem, RTEMS_WAIT, 100);
176    } while (result == RTEMS_SUCCESSFUL && state->len > 0);
177  }
178
179  return (result == RTEMS_SUCCESSFUL) ? len : -result;
180}
181
182
183rtems_status_code bfin_spi_init(rtems_libi2c_bus_t *bus) {
184  rtems_status_code result;
185  bfin_spi_state_t *state;
186
187  state = &((bfin_spi_bus_t *) bus)->p;
188
189  BFIN_REG16(state->base, SPI_CTL_OFFSET) = SPI_CTL_SPE |
190                                            SPI_CTL_MSTR |
191                                            SPI_CTL_CPHA |
192                                            SPI_CTL_TIMOD_RDBR;
193
194  result = rtems_semaphore_create(rtems_build_name('s','p','i','s'),
195                                  0,
196                                  RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE,
197                                  0,
198                                  &state->sem);
199  if (result == RTEMS_SUCCESSFUL)
200    bfin_spi = state; /* for isr */
201
202  return result;
203}
204
205rtems_status_code bfin_spi_send_start(rtems_libi2c_bus_t *bus) {
206
207  return RTEMS_SUCCESSFUL;
208}
209
210int bfin_spi_read_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len) {
211
212  return readWrite(bus, buf, NULL, len);
213}
214
215int bfin_spi_write_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len) {
216
217  return readWrite(bus, NULL, buf, len);
218}
219
220int bfin_spi_ioctl(rtems_libi2c_bus_t *bus, int cmd, void *arg) {
221  int result;
222
223  result = -RTEMS_NOT_DEFINED;
224  switch(cmd) {
225  case RTEMS_LIBI2C_IOCTL_SET_TFRMODE:
226    result = -setTFRMode(bus, (const rtems_libi2c_tfr_mode_t *) arg);
227    break;
228  case RTEMS_LIBI2C_IOCTL_READ_WRITE:
229    result = readWrite(bus,
230                       ((rtems_libi2c_read_write_t *) arg)->rd_buf,
231                       ((rtems_libi2c_read_write_t *) arg)->wr_buf,
232                       ((rtems_libi2c_read_write_t *) arg)->byte_cnt);
233    break;
234  default:
235    break;
236  }
237
238  return result;
239}
240
Note: See TracBrowser for help on using the repository browser.