source: rtems/c/src/lib/libbsp/sparc/leon3/console/console.c @ 7579e25

4.115
Last change on this file since 7579e25 was 7579e25, checked in by Sebastian Huber <sebastian.huber@…>, on 11/25/13 at 07:46:19

bsp/leon3: New BSP variant leon3_qemu

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