source: rtems/c/src/lib/libbsp/sparc/leon3/console/console.c @ 6fe6d017

4.115
Last change on this file since 6fe6d017 was 6fe6d017, checked in by Sebastian Huber <sebastian.huber@…>, on 11/26/13 at 07:56:08

bsp/leon3: Avoid copy and paste in console driver

  • Property mode set to 100644
File size: 12.0 KB
Line 
1/*
2 *  This file contains the TTY driver for the serial ports on the LEON.
3 *
4 *  This driver uses the termios pseudo driver.
5 *
6 *  COPYRIGHT (c) 1989-1998.
7 *  On-Line Applications Research Corporation (OAR).
8 *
9 *  Modified for LEON3 BSP.
10 *  COPYRIGHT (c) 2004.
11 *  Gaisler Research.
12 *
13 *  The license and distribution terms for this file may be
14 *  found in the file LICENSE in this distribution or at
15 *  http://www.rtems.com/license/LICENSE.
16 */
17
18/* Define CONSOLE_USE_INTERRUPTS to enable APBUART interrupt handling instead
19 * of polling mode.
20 *
21 * Note that it is not possible to use the interrupt mode of the driver
22 * together with the "old" APBUART and -u to GRMON. However the new
23 * APBUART core (from GRLIB 1.0.17-b2710) has the GRMON debug bit and can
24 * handle interrupts.
25 *
26 * NOTE: This can be defined in the make/custom/leon3.cfg file.
27 */
28
29#include <bsp.h>
30#include <rtems/libio.h>
31#include <stdlib.h>
32#include <assert.h>
33#include <rtems/bspIo.h>
34#include <amba.h>
35#include <rtems/termiostypes.h>
36
37/* Let user override which on-chip APBUART will be debug UART
38 * 0 = Default APBUART. On MP system CPU0=APBUART0, CPU1=APBUART1...
39 * 1 = APBUART[0]
40 * 2 = APBUART[1]
41 * 3 = APBUART[2]
42 * ...
43 */
44int syscon_uart_index __attribute__((weak)) = 0;
45
46/*
47 *  apbuart_outbyte_polled
48 *
49 *  This routine transmits a character using polling.
50 */
51
52extern void apbuart_outbyte_polled(
53  struct apbuart_regs *regs,
54  unsigned char ch,
55  int do_cr_on_newline,
56  int wait_sent
57  );
58
59
60/* body is in printk_support.c */
61
62/*
63 *  apbuart_inbyte_nonblocking
64 *
65 *  This routine polls for a character.
66 */
67
68extern int apbuart_inbyte_nonblocking(struct apbuart_regs *regs);
69
70/* body is in debugputs.c */
71
72struct apbuart_priv {
73  struct apbuart_regs *regs;
74  unsigned int freq_hz;
75#if CONSOLE_USE_INTERRUPTS
76  int irq;
77  void *cookie;
78  volatile int sending;
79  char *buf;
80#endif
81};
82static struct apbuart_priv apbuarts[BSP_NUMBER_OF_TERMIOS_PORTS];
83static int uarts = 0;
84
85static struct apbuart_priv *leon3_console_get_uart(int minor)
86{
87  struct apbuart_priv *uart;
88
89  if (minor == 0)
90    uart = &apbuarts[syscon_uart_index];
91  else
92    uart = &apbuarts[minor - 1];
93
94  return uart;
95}
96
97#if CONSOLE_USE_INTERRUPTS
98
99/* Handle UART interrupts */
100static void leon3_console_isr(void *arg)
101{
102  struct apbuart_priv *uart = arg;
103  unsigned int status;
104  char data;
105
106  /* Get all received characters */
107  while ((status=uart->regs->status) & LEON_REG_UART_STATUS_DR) {
108    /* Data has arrived, get new data */
109    data = uart->regs->data;
110
111    /* Tell termios layer about new character */
112    rtems_termios_enqueue_raw_characters(uart->cookie, &data, 1);
113  }
114
115  if (
116    (status & LEON_REG_UART_STATUS_THE)
117      && (uart->regs->ctrl & LEON_REG_UART_CTRL_TI) != 0
118  ) {
119    /* write_interrupt will get called from this function */
120    rtems_termios_dequeue_characters(uart->cookie, 1);
121  }
122}
123
124/*
125 *  Console Termios Write-Buffer Support Entry Point
126 *
127 */
128static int leon3_console_write_support(int minor, const char *buf, size_t len)
129{
130  struct apbuart_priv *uart = leon3_console_get_uart(minor);
131  int sending;
132
133  if (len > 0) {
134    /* Enable TX interrupt (interrupt is edge-triggered) */
135    uart->regs->ctrl |= LEON_REG_UART_CTRL_TI;
136
137    /* start UART TX, this will result in an interrupt when done */
138    uart->regs->data = *buf;
139
140    sending = 1;
141  } else {
142    /* No more to send, disable TX interrupts */
143    uart->regs->ctrl &= ~LEON_REG_UART_CTRL_TI;
144
145    /* Tell close that we sent everything */
146    sending = 0;
147  }
148
149  uart->sending = sending;
150
151  return 0;
152}
153
154#else
155
156/*
157 * Prototypes to avoid warnings
158 */
159ssize_t console_write_polled(int minor, const char *buf, size_t len);
160int console_pollRead(int minor);
161
162/*
163 *  Console Termios Support Entry Points
164 */
165ssize_t console_write_polled(int minor, const char *buf, size_t len)
166{
167  struct apbuart_priv *uart = leon3_console_get_uart(minor);
168  int nwrite = 0;
169
170  while (nwrite < len) {
171    apbuart_outbyte_polled(uart->regs, *buf++, 1, 0);
172    nwrite++;
173  }
174  return nwrite;
175}
176
177int console_pollRead(int minor)
178{
179  struct apbuart_priv *uart = leon3_console_get_uart(minor);
180
181  return apbuart_inbyte_nonblocking(uart->regs);
182}
183
184#endif
185
186/*
187 * Prototypes to avoid warnings
188 */
189int console_set_attributes(int minor, const struct termios *t);
190int find_matching_apbuart(struct ambapp_dev *dev, int index, void *arg);
191int console_scan_uarts(void);
192
193
194int console_set_attributes(int minor, const struct termios *t)
195{
196  struct apbuart_priv *uart = leon3_console_get_uart(minor);
197  unsigned int scaler;
198  unsigned int ctrl;
199  int baud;
200
201  switch (t->c_cflag & CSIZE) {
202    default:
203    case CS5:
204    case CS6:
205    case CS7:
206      /* Hardware doesn't support other than CS8 */
207      return -1;
208    case CS8:
209      break;
210  }
211
212  /*
213   * FIXME: This read-modify-write sequence is broken since interrupts may
214   * interfere.
215   */
216
217  /* Read out current value */
218  ctrl = uart->regs->ctrl;
219
220  switch (t->c_cflag & (PARENB|PARODD)) {
221    case (PARENB|PARODD):
222      /* Odd parity */
223      ctrl |= LEON_REG_UART_CTRL_PE|LEON_REG_UART_CTRL_PS;
224      break;
225
226    case PARENB:
227      /* Even parity */
228      ctrl &= ~LEON_REG_UART_CTRL_PS;
229      ctrl |= LEON_REG_UART_CTRL_PE;
230      break;
231
232    default:
233    case 0:
234    case PARODD:
235      /* No Parity */
236      ctrl &= ~(LEON_REG_UART_CTRL_PS|LEON_REG_UART_CTRL_PE);
237  }
238
239  if (!(t->c_cflag & CLOCAL)) {
240    ctrl |= LEON_REG_UART_CTRL_FL;
241  } else {
242    ctrl &= ~LEON_REG_UART_CTRL_FL;
243  }
244
245  /* Update new settings */
246  uart->regs->ctrl = ctrl;
247
248  /* Baud rate */
249  baud = rtems_termios_baud_to_number(t->c_cflag);
250  if (baud > 0) {
251    /* Calculate Baud rate generator "scaler" number */
252    scaler = (((uart->freq_hz * 10) / (baud * 8)) - 5) / 10;
253
254    /* Set new baud rate by setting scaler */
255    uart->regs->scaler = scaler;
256  }
257
258  return 0;
259}
260
261/* AMBA PP find routine. Extract AMBA PnP information into data structure. */
262int find_matching_apbuart(struct ambapp_dev *dev, int index, void *arg)
263{
264  struct ambapp_apb_info *apb = (struct ambapp_apb_info *)dev->devinfo;
265
266  /* Extract needed information of one APBUART */
267  apbuarts[uarts].regs = (struct apbuart_regs *)apb->start;
268#if CONSOLE_USE_INTERRUPTS
269  apbuarts[uarts].irq = apb->irq;
270#endif
271  /* Get APBUART core frequency, it is assumed that it is the same
272   * as Bus frequency where the UART is situated
273   */
274  apbuarts[uarts].freq_hz = ambapp_freq_get(&ambapp_plb, dev);
275  uarts++;
276
277  if (uarts >= BSP_NUMBER_OF_TERMIOS_PORTS)
278    return 1; /* Satisfied number of UARTs, stop search */
279  else
280    return 0; /* Continue searching for more UARTs */
281}
282
283/* Find all UARTs */
284int console_scan_uarts(void)
285{
286  memset(apbuarts, 0, sizeof(apbuarts));
287
288  /* Find APBUART cores */
289  ambapp_for_each(&ambapp_plb, (OPTIONS_ALL|OPTIONS_APB_SLVS), VENDOR_GAISLER,
290                  GAISLER_APBUART, find_matching_apbuart, NULL);
291
292  return uarts;
293}
294
295/*
296 *  Console Device Driver Entry Points
297 *
298 */
299
300rtems_device_driver console_initialize(
301  rtems_device_major_number  major,
302  rtems_device_minor_number  minor,
303  void                      *arg
304)
305{
306  rtems_status_code status;
307  int i;
308  char console_name[16];
309
310  rtems_termios_initialize();
311
312  /* Find UARTs */
313  console_scan_uarts();
314
315  /* Update syscon_uart_index to index used as /dev/console
316   * Let user select System console by setting syscon_uart_index. If the
317   * BSP is to provide the default UART (syscon_uart_index==0):
318   *   non-MP: APBUART[0] is system console
319   *   MP: LEON CPU index select UART
320   */
321  if (syscon_uart_index == 0) {
322#if defined(RTEMS_MULTIPROCESSING)
323    syscon_uart_index = LEON3_Cpu_Index;
324#else
325    syscon_uart_index = 0;
326#endif
327  } else {
328    syscon_uart_index = syscon_uart_index - 1; /* User selected sys-console */
329  }
330
331  /*  Register Device Names
332   *
333   *  0 /dev/console   - APBUART[USER-SELECTED, DEFAULT=APBUART[0]]
334   *  1 /dev/console_a - APBUART[0] (by default not present because is console)
335   *  2 /dev/console_b - APBUART[1]
336   *  ...
337   *
338   * On a MP system one should not open UARTs that other OS instances use.
339   */
340  if (syscon_uart_index < uarts) {
341    status = rtems_io_register_name("/dev/console", major, 0);
342    if (status != RTEMS_SUCCESSFUL)
343      rtems_fatal_error_occurred(status);
344  }
345  strcpy(console_name,"/dev/console_a");
346  for (i = 0; i < uarts; i++) {
347    if (i == syscon_uart_index)
348      continue; /* skip UART that is registered as /dev/console */
349    console_name[13] = 'a' + i;
350    status = rtems_io_register_name( console_name, major, i+1);
351  }
352
353  return RTEMS_SUCCESSFUL;
354}
355
356#if CONSOLE_USE_INTERRUPTS
357static struct rtems_termios_tty *leon3_console_get_tty(
358  rtems_libio_open_close_args_t *args
359)
360{
361  return args->iop->data1;
362}
363#endif
364
365static int leon3_console_first_open(int major, int minor, void *arg)
366{
367  struct apbuart_priv *uart = leon3_console_get_uart(minor);
368
369#if CONSOLE_USE_INTERRUPTS
370  rtems_status_code sc;
371
372  uart->cookie = leon3_console_get_tty(arg);
373
374  /* Register Interrupt handler */
375  sc = rtems_interrupt_handler_install(uart->irq, "console",
376                                       RTEMS_INTERRUPT_SHARED,
377                                       leon3_console_isr,
378                                       uart);
379  if (sc != RTEMS_SUCCESSFUL)
380    return -1;
381
382  uart->sending = 0;
383  /* Enable Receiver and transmitter and Turn on RX interrupts */
384  uart->regs->ctrl |= LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE |
385                      LEON_REG_UART_CTRL_RI;
386#else
387  /* Initialize UART on opening */
388  uart->regs->ctrl |= LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE;
389#endif
390  uart->regs->status = 0;
391
392  return 0;
393}
394
395#if CONSOLE_USE_INTERRUPTS
396static int leon3_console_last_close(int major, int minor, void *arg)
397{
398  struct rtems_termios_tty *tty = leon3_console_get_tty(arg);
399  struct apbuart_priv *uart = leon3_console_get_uart(minor);
400  rtems_interrupt_level level;
401
402  /* Turn off RX interrupts */
403  rtems_termios_interrupt_lock_acquire(tty, level);
404  uart->regs->ctrl &= ~(LEON_REG_UART_CTRL_RI);
405  rtems_termios_interrupt_lock_release(tty, level);
406
407  /**** Flush device ****/
408  while (uart->sending) {
409    /* Wait until all data has been sent */
410  }
411
412  /* uninstall ISR */
413  rtems_interrupt_handler_remove(uart->irq, leon3_console_isr, uart);
414
415  return 0;
416}
417#endif
418
419rtems_device_driver console_open(
420  rtems_device_major_number major,
421  rtems_device_minor_number minor,
422  void                    * arg
423)
424{
425#if CONSOLE_USE_INTERRUPTS
426  /* Interrupt mode routines */
427  static const rtems_termios_callbacks Callbacks = {
428    leon3_console_first_open,    /* firstOpen */
429    leon3_console_last_close,    /* lastClose */
430    NULL,                        /* pollRead */
431    leon3_console_write_support, /* write */
432    console_set_attributes,      /* setAttributes */
433    NULL,                        /* stopRemoteTx */
434    NULL,                        /* startRemoteTx */
435    1                            /* outputUsesInterrupts */
436  };
437#else
438  /* Polling mode routines */
439  static const rtems_termios_callbacks Callbacks = {
440    leon3_console_first_open,    /* firstOpen */
441    NULL,                        /* lastClose */
442    console_pollRead,            /* pollRead */
443    console_write_polled,        /* write */
444    console_set_attributes,      /* setAttributes */
445    NULL,                        /* stopRemoteTx */
446    NULL,                        /* startRemoteTx */
447    0                            /* outputUsesInterrupts */
448  };
449#endif
450
451  assert(minor <= uarts);
452  if (minor > uarts || minor == (syscon_uart_index + 1))
453    return RTEMS_INVALID_NUMBER;
454
455  return rtems_termios_open(major, minor, arg, &Callbacks);
456}
457
458rtems_device_driver console_close(
459  rtems_device_major_number major,
460  rtems_device_minor_number minor,
461  void                    * arg
462)
463{
464  return rtems_termios_close(arg);
465}
466
467rtems_device_driver console_read(
468  rtems_device_major_number major,
469  rtems_device_minor_number minor,
470  void                    * arg
471)
472{
473  return rtems_termios_read(arg);
474}
475
476rtems_device_driver console_write(
477  rtems_device_major_number major,
478  rtems_device_minor_number minor,
479  void                    * arg
480)
481{
482  return rtems_termios_write(arg);
483}
484
485rtems_device_driver console_control(
486  rtems_device_major_number major,
487  rtems_device_minor_number minor,
488  void                    * arg
489)
490{
491  return rtems_termios_ioctl(arg);
492}
493
Note: See TracBrowser for help on using the repository browser.