source: rtems/c/src/lib/libcpu/bfin/serial/uart.c @ c193baad

4.104.115
Last change on this file since c193baad was a93d5c74, checked in by Ralf Corsepius <ralf.corsepius@…>, on 12/11/09 at 04:15:58

2009-12-11 Ralf Corsépius <ralf.corsepius@…>

  • serial/uart.c: Reflect changes to rtems_termios_callbacks->write.
  • Property mode set to 100644
File size: 10.1 KB
Line 
1/*  UART driver for Blackfin
2 *
3 *  Copyright (c) 2008 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
14#include <rtems.h>
15#include <rtems/libio.h>
16#include <rtems/termiostypes.h>
17#include <termios.h>
18#include <stdlib.h>
19
20#include <libcpu/uartRegs.h>
21#include "uart.h"
22
23
24/* flags */
25#define BFIN_UART_XMIT_BUSY 0x01
26
27
28static bfin_uart_config_t *uartsConfig;
29
30
31static void initializeHardware(int minor) {
32  uint16_t divisor;
33  char *base;
34  uint16_t r;
35
36  base = uartsConfig->channels[minor].base_address;
37
38  *(uint16_t volatile *) (base + UART_IER_OFFSET) = 0;
39
40  if (uartsConfig->channels[minor].force_baud)
41    divisor = (uint16_t) (uartsConfig->freq /
42                          (uartsConfig->channels[minor].force_baud * 16));
43  else
44    divisor = (uint16_t) (uartsConfig->freq / (9600 * 16));
45  *(uint16_t volatile *) (base + UART_LCR_OFFSET) = UART_LCR_DLAB;
46  *(uint16_t volatile *) (base + UART_DLL_OFFSET) = (divisor & 0xff);
47  *(uint16_t volatile *) (base + UART_DLH_OFFSET) = ((divisor >> 8) & 0xff);
48
49  *(uint16_t volatile *) (base + UART_LCR_OFFSET) = UART_LCR_WLS_8;
50
51  *(uint16_t volatile *) (base + UART_GCTL_OFFSET) = UART_GCTL_UCEN;
52
53  r = *(uint16_t volatile *) (base + UART_LSR_OFFSET);
54  r = *(uint16_t volatile *) (base + UART_RBR_OFFSET);
55  r = *(uint16_t volatile *) (base + UART_IIR_OFFSET);
56
57  return;
58}
59
60static int pollRead(int minor) {
61  int c;
62  char *base;
63
64  base = uartsConfig->channels[minor].base_address;
65
66  /* check to see if driver is using interrupts so this call will be
67     harmless (though non-functional) in case some debug code tries to
68     use it */
69  if (!uartsConfig->channels[minor].use_interrupts &&
70      *((uint16_t volatile *) (base + UART_LSR_OFFSET)) & UART_LSR_DR)
71    c = *((uint16_t volatile *) (base + UART_RBR_OFFSET));
72  else
73    c = -1;
74
75  return c;
76}
77
78char bfin_uart_poll_read(int minor) {
79  int c;
80
81  do {
82    c = pollRead(minor);
83  } while (c == -1);
84
85  return c;
86}
87
88void bfin_uart_poll_write(int minor, char c) {
89  char *base;
90
91  base = uartsConfig->channels[minor].base_address;
92
93  while (!(*((uint16_t volatile *) (base + UART_LSR_OFFSET)) & UART_LSR_THRE))
94    ;
95  *(uint16_t volatile *) (base + UART_THR_OFFSET) = c;
96}
97
98/* begin BISON */
99void debug_write_char(char c) {
100  bfin_uart_poll_write(0, c);
101}
102
103void debug_write_string(char *s) {
104
105  while (s && *s) {
106    if (*s == '\n')
107      debug_write_char('\r');
108    debug_write_char(*s++);
109  }
110}
111
112void debug_write_crlf(void) {
113
114  debug_write_char('\r');
115  debug_write_char('\n');
116}
117
118void debug_write_nybble(int nybble) {
119
120  nybble &= 0x0f;
121  debug_write_char((nybble > 9) ? 'a' + (nybble - 10) : '0' + nybble);
122}
123
124void debug_write_byte(int byte) {
125
126  byte &= 0xff;
127  debug_write_nybble(byte >> 4);
128  debug_write_nybble(byte & 0x0f);
129}
130
131void debug_write_half(int half) {
132
133  half &= 0xffff;
134  debug_write_byte(half >> 8);
135  debug_write_byte(half & 0xff);
136}
137
138void debug_write_word(int word) {
139
140  word &= 0xffffffff;
141  debug_write_half(word >> 16);
142  debug_write_half(word & 0xffff);
143}
144/* end BISON */
145
146/*
147 *  Console Termios Support Entry Points
148 *
149 */
150
151static ssize_t pollWrite(int minor, const char *buf, size_t len) {
152
153  size_t count;
154  for ( count = 0; count < len; count++ )
155    bfin_uart_poll_write(minor, *buf++);
156
157  return count;
158}
159
160static void enableInterrupts(int minor) {
161  char *base;
162
163  base = uartsConfig->channels[minor].base_address;
164
165  *(uint16_t volatile *) (base + UART_IER_OFFSET) = UART_IER_ETBEI |
166                                                    UART_IER_ERBFI;
167}
168
169static void disableAllInterrupts(void) {
170  int i;
171  char *base;
172
173  for (i = 0; i < uartsConfig->num_channels; i++) {
174    base = uartsConfig->channels[i].base_address;
175    *(uint16_t volatile *) (base + UART_IER_OFFSET) = 0;
176  }
177}
178
179static ssize_t interruptWrite(int minor, const char *buf, size_t len) {
180  char *base;
181
182  base = uartsConfig->channels[minor].base_address;
183
184  uartsConfig->channels[minor].flags |= BFIN_UART_XMIT_BUSY;
185  *(uint16_t volatile *) (base + UART_THR_OFFSET) = *buf;
186
187  /* one byte written */
188  return 1;
189}
190
191static int setAttributes(int minor, const struct termios *termios) {
192  char *base;
193  int baud;
194  uint16_t divisor;
195  uint16_t lcr;
196
197  base = uartsConfig->channels[minor].base_address;
198  switch (termios->c_cflag & CBAUD) {
199  case B0:
200    baud = 0;
201    break;
202  case B50:
203    baud = 50;
204    break;
205  case B75:
206    baud = 75;
207    break;
208  case B110:
209    baud = 110;
210    break;
211  case B134:
212    baud = 134;
213    break;
214  case B150:
215    baud = 150;
216    break;
217  case B200:
218    baud = 200;
219    break;
220  case B300:
221    baud = 300;
222    break;
223  case B600:
224    baud = 600;
225    break;
226  case B1200:
227    baud = 1200;
228    break;
229  case B1800:
230    baud = 1800;
231    break;
232  case B2400:
233    baud = 2400;
234    break;
235  case B4800:
236    baud = 4800;
237    break;
238  case B9600:
239    baud = 9600;
240    break;
241  case B19200:
242    baud = 19200;
243    break;
244  case B38400:
245    baud = 38400;
246    break;
247  case B57600:
248    baud = 57600;
249    break;
250  case B115200:
251    baud = 115200;
252    break;
253  case B230400:
254    baud = 230400;
255    break;
256  case B460800:
257    baud = 460800;
258    break;
259  default:
260    baud = -1;
261    break;
262  }
263  if (baud > 0 && uartsConfig->channels[minor].force_baud)
264    baud = uartsConfig->channels[minor].force_baud;
265  switch (termios->c_cflag & CSIZE) {
266  case CS5:
267    lcr = UART_LCR_WLS_5;
268    break;
269  case CS6:
270    lcr = UART_LCR_WLS_6;
271    break;
272  case CS7:
273    lcr = UART_LCR_WLS_7;
274    break;
275  case CS8:
276  default:
277    lcr = UART_LCR_WLS_8;
278    break;
279  }
280  switch (termios->c_cflag & (PARENB | PARODD)) {
281  case PARENB:
282    lcr |= UART_LCR_PEN | UART_LCR_EPS;
283    break;
284  case PARENB | PARODD:
285    lcr |= UART_LCR_PEN;
286    break;
287  default:
288    break;
289  }
290  if (termios->c_cflag & CSTOPB)
291    lcr |= UART_LCR_STB;
292
293  if (baud > 0) {
294    divisor = (uint16_t) (uartsConfig->freq / (baud * 16));
295    *(uint16_t volatile *) (base + UART_LCR_OFFSET) = lcr | UART_LCR_DLAB;
296    *(uint16_t volatile *) (base + UART_DLL_OFFSET) = (divisor & 0xff);
297    *(uint16_t volatile *) (base + UART_DLH_OFFSET) = ((divisor >> 8) & 0xff);
298  }
299  *(uint16_t volatile *) (base + UART_LCR_OFFSET) = lcr;
300
301  return 0;
302}
303
304void bfin_uart_isr(int source) {
305  int i;
306  char *base;
307  uint16_t uartStat;
308  char c;
309  uint8_t uartLSR;
310
311  /* Just use one ISR and check for all UART interrupt sources in it.
312     This is less efficient than making use of the vector to narrow down
313     the things we need to check, but not all Blackfins separate the
314     UART interrupt sources in the same ways.  This way we don't have
315     to make this code dependent on the type of Blackfin.  */
316  for (i = 0; i < uartsConfig->num_channels; i++) {
317    if (uartsConfig->channels[i].use_interrupts) {
318      base = uartsConfig->channels[i].base_address;
319      uartStat = *(uint16_t volatile *) (base + UART_IIR_OFFSET);
320      if ((uartStat & UART_IIR_NINT) == 0) {
321        switch (uartStat & UART_IIR_STATUS_MASK) {
322        case UART_IIR_STATUS_THRE:
323          if (uartsConfig->channels[i].termios &&
324              (uartsConfig->channels[i].flags & BFIN_UART_XMIT_BUSY)) {
325            uartsConfig->channels[i].flags &= ~BFIN_UART_XMIT_BUSY;
326            rtems_termios_dequeue_characters(uartsConfig->channels[i].termios,
327                                             1);
328          }
329          break;
330        case UART_IIR_STATUS_RDR:
331          c = *(uint16_t volatile *) (base + UART_RBR_OFFSET);
332          if (uartsConfig->channels[i].termios)
333            rtems_termios_enqueue_raw_characters(
334                uartsConfig->channels[i].termios, &c, 1);
335          break;
336        case UART_IIR_STATUS_LS:
337          uartLSR = *(uint16_t volatile *) (base + UART_LSR_OFFSET);
338          /* break, framing error, parity error, or overrun error
339             has been detected */
340          break;
341        default:
342          break;
343        }
344      }
345    }
346  }
347}
348
349rtems_status_code bfin_uart_initialize(rtems_device_major_number major,
350                                       bfin_uart_config_t *config) {
351  rtems_status_code status;
352  int i;
353
354  status = RTEMS_SUCCESSFUL;
355
356  rtems_termios_initialize();
357
358  /*
359   *  Register Device Names
360   */
361
362  uartsConfig = config;
363  for (i = 0; i < config->num_channels; i++) {
364    config->channels[i].termios = NULL;
365    config->channels[i].flags = 0;
366    initializeHardware(i);
367    status = rtems_io_register_name(config->channels[i].name, major, i);
368  }
369
370   return RTEMS_SUCCESSFUL;
371}
372
373rtems_device_driver bfin_uart_open(rtems_device_major_number major,
374                                   rtems_device_minor_number minor,
375                                   void *arg) {
376  rtems_status_code sc;
377  rtems_libio_open_close_args_t *args;
378  static const rtems_termios_callbacks pollCallbacks = {
379    NULL,                        /* firstOpen */
380    NULL,                        /* lastClose */
381    pollRead,                    /* pollRead */
382    pollWrite,                   /* write */
383    setAttributes,               /* setAttributes */
384    NULL,                        /* stopRemoteTx */
385    NULL,                        /* startRemoteTx */
386    TERMIOS_POLLED               /* outputUsesInterrupts */
387  };
388  static const rtems_termios_callbacks interruptCallbacks = {
389    NULL,                        /* firstOpen */
390    NULL,                        /* lastClose */
391    NULL,                        /* pollRead */
392    interruptWrite,              /* write */
393    setAttributes,               /* setAttributes */
394    NULL,                        /* stopRemoteTx */
395    NULL,                        /* startRemoteTx */
396    TERMIOS_IRQ_DRIVEN           /* outputUsesInterrupts */
397  };
398
399  if (uartsConfig == NULL || minor < 0 || minor >= uartsConfig->num_channels)
400    return RTEMS_INVALID_NUMBER;
401
402  sc = rtems_termios_open(major, minor, arg,
403                          uartsConfig->channels[minor].use_interrupts ?
404                          &interruptCallbacks : &pollCallbacks);
405  args = arg;
406  uartsConfig->channels[minor].termios = args->iop->data1;
407
408  if (uartsConfig->channels[minor].use_interrupts)
409    enableInterrupts(minor);
410  atexit(disableAllInterrupts);
411
412  return sc;
413}
414
Note: See TracBrowser for help on using the repository browser.