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

4.104.11
Last change on this file since fe83cef6 was fe83cef6, checked in by Joel Sherrill <joel.sherrill@…>, on Jan 11, 2010 at 4:14:47 PM

2010-01-11 Allan Hessenflow <allanh@…>

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