source: rtems/bsps/m68k/mcf5329/console/console.c @ d7d66d7

5
Last change on this file since d7d66d7 was d7d66d7, checked in by Sebastian Huber <sebastian.huber@…>, on 04/19/18 at 04:28:01

bsps: Move console drivers to bsps

This patch is a part of the BSP source reorganization.

Update #3285.

  • Property mode set to 100644
File size: 20.7 KB
Line 
1 /*
2  *  Multi UART console serial I/O.
3  *
4  * TO DO: Add DMA input/output
5  */
6
7#include <stdio.h>
8#include <fcntl.h>
9#include <termios.h>
10#include <malloc.h>
11
12#include <rtems/libio.h>
13#include <rtems/termiostypes.h>
14#include <rtems/console.h>
15#include <rtems/bspIo.h>
16
17#include <bsp.h>
18
19#define UART_INTC0_IRQ_VECTOR(x) (64+26+(x))
20
21#define MCF_UART_USR_ERROR ( MCF_UART_USR_RB | \
22                             MCF_UART_USR_FE | \
23                             MCF_UART_USR_PE | \
24                             MCF_UART_USR_OE )
25
26static ssize_t IntUartPollWrite(int minor, const char *buf, size_t len);
27static ssize_t IntUartInterruptWrite(int minor, const char *buf, size_t len);
28
29static void _BSP_null_char(char c)
30{
31  int level;
32
33  rtems_interrupt_disable(level);
34  while ((MCF_UART_USR(CONSOLE_PORT) & MCF_UART_USR_TXRDY) == 0)
35    continue;
36  MCF_UART_UTB(CONSOLE_PORT) = c;
37  while ((MCF_UART_USR(CONSOLE_PORT) & MCF_UART_USR_TXRDY) == 0)
38    continue;
39  rtems_interrupt_enable(level);
40}
41
42BSP_output_char_function_type     BSP_output_char = _BSP_null_char;
43BSP_polling_getchar_function_type BSP_poll_char = NULL;
44
45#define MAX_UART_INFO     3
46#define RX_BUFFER_SIZE    512
47
48struct IntUartInfoStruct
49{
50  int iomode;
51  volatile int uimr;
52  int baud;
53  int databits;
54  int parity;
55  int stopbits;
56  int hwflow;
57  int rx_in;
58  int rx_out;
59  char rx_buffer[RX_BUFFER_SIZE];
60  void *ttyp;
61};
62
63struct IntUartInfoStruct IntUartInfo[MAX_UART_INFO];
64
65/***************************************************************************
66   Function : IntUartSet
67
68   Description : This updates the hardware UART settings.
69 ***************************************************************************/
70static void
71IntUartSet(int minor, int baud, int databits, int parity, int stopbits,
72           int hwflow)
73{
74  int divisor;
75  uint32_t clock_speed;
76  uint8_t umr1 = 0;
77  uint8_t umr2 = 0;
78  struct IntUartInfoStruct *info = &IntUartInfo[minor];
79  int level;
80
81  rtems_interrupt_disable(level);
82
83  /* disable interrupts, clear RTS line, and disable the UARTS */
84  MCF_UART_UIMR(minor) = 0;
85  MCF_UART_UOP0(minor) = 1;
86  MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_DISABLED | MCF_UART_UCR_RX_DISABLED);
87
88  /* save the current values */
89  info->uimr = 0;
90  info->baud = baud;
91  info->databits = databits;
92  info->parity = parity;
93  info->stopbits = stopbits;
94  info->hwflow = hwflow;
95
96  clock_speed = bsp_get_BUS_clock_speed();
97  /* determine the baud divisor value */
98  divisor = ((clock_speed) / (32 * baud));
99  if (divisor < 2)
100    divisor = 2;
101
102  /* check to see if doing hardware flow control */
103  if (hwflow) {
104    /* set hardware flow options */
105    umr1 |= MCF_UART_UMR_RXRTS;
106    umr2 |= MCF_UART_UMR_TXCTS;
107  }
108
109  /* determine the new umr values */
110  umr1 |= (parity | databits);
111  umr2 |= (stopbits);
112
113  /* reset the uart */
114  MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_ERROR;
115  MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_RX;
116  MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_TX;
117
118  /* reset the uart mode register and update values */
119  MCF_UART_UCR(minor) = MCF_UART_UCR_RESET_MR;
120  MCF_UART_UMR(minor) = umr1;
121  MCF_UART_UMR(minor) = umr2;
122
123  /* set the baud rate values */
124  MCF_UART_UCSR(minor) =
125    (MCF_UART_UCSR_RCS_SYS_CLK | MCF_UART_UCSR_TCS_SYS_CLK);
126  MCF_UART_UBG1(minor) = (divisor & 0xff00) >> 8;
127  MCF_UART_UBG2(minor) = (divisor & 0x00ff);
128
129  /* enable the uart */
130  MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_ENABLED | MCF_UART_UCR_RX_ENABLED);
131
132  /* check to see if interrupts need to be enabled */
133  if (info->iomode != TERMIOS_POLLED) {
134    /* enable rx interrupts */
135    info->uimr |= MCF_UART_UIMR_RXRDY_FU;
136    MCF_UART_UIMR(minor) = info->uimr;
137  }
138
139  /* check to see if doing hardware flow control */
140  if (hwflow) {
141    /* assert the RTS line */
142    MCF_UART_UOP1(minor) = 1;
143  }
144
145  rtems_interrupt_enable(level);
146
147}
148
149/***************************************************************************
150   Function : IntUartSetAttributes
151
152   Description : This provides the hardware-dependent portion of tcsetattr().
153   value and sets it. At the moment this just sets the baud rate.
154
155   Note: The highest baudrate is 115200 as this stays within
156   an error of +/- 5% at 25MHz processor clock
157 ***************************************************************************/
158static int IntUartSetAttributes(int minor, const struct termios *t)
159{
160  /* set default index values */
161  int baud = (int) 19200;
162  int databits = (int) MCF_UART_UMR_BC_8;
163  int parity = (int) MCF_UART_UMR_PM_NONE;
164  int stopbits = (int) MCF_UART_UMR_SB_STOP_BITS_1;
165  int hwflow = (int) 0;
166  struct IntUartInfoStruct *info = &IntUartInfo[minor];
167
168  /* check to see if input is valid */
169  if (t != (const struct termios *) 0) {
170    /* determine baud rate index */
171    baud = rtems_termios_baud_to_number(t->c_ospeed);
172
173    /* determine data bits */
174    switch (t->c_cflag & CSIZE) {
175      case CS5:
176        databits = (int) MCF_UART_UMR_BC_5;
177        break;
178      case CS6:
179        databits = (int) MCF_UART_UMR_BC_6;
180        break;
181      case CS7:
182        databits = (int) MCF_UART_UMR_BC_7;
183        break;
184      case CS8:
185        databits = (int) MCF_UART_UMR_BC_8;
186        break;
187    }
188
189    /* determine if parity is enabled */
190    if (t->c_cflag & PARENB) {
191      if (t->c_cflag & PARODD) {
192        /* odd parity */
193        parity = (int) MCF_UART_UMR_PM_ODD;
194      } else {
195        /* even parity */
196        parity = (int) MCF_UART_UMR_PM_EVEN;
197      }
198    }
199
200    /* determine stop bits */
201    if (t->c_cflag & CSTOPB) {
202      /* two stop bits */
203      stopbits = (int) MCF_UART_UMR_SB_STOP_BITS_2;
204    }
205
206    /* check to see if hardware flow control */
207    if (t->c_cflag & CRTSCTS) {
208      hwflow = 1;
209    }
210  }
211
212  /* check to see if values have changed */
213  if ((baud != info->baud) ||
214      (databits != info->databits) ||
215      (parity != info->parity) ||
216      (stopbits != info->stopbits) || (hwflow != info->hwflow)) {
217
218    /* call function to set values */
219    IntUartSet(minor, baud, databits, parity, stopbits, hwflow);
220  }
221
222  return (RTEMS_SUCCESSFUL);
223
224}
225
226/***************************************************************************
227   Function : IntUartInterruptHandler
228
229   Description : This is the interrupt handler for the internal uart. It
230   determines which channel caused the interrupt before queueing any received
231   chars and dequeueing chars waiting for transmission.
232 ***************************************************************************/
233static rtems_isr IntUartInterruptHandler(rtems_vector_number v)
234{
235  unsigned int chan = v - UART_INTC0_IRQ_VECTOR(0);
236  struct IntUartInfoStruct *info = &IntUartInfo[chan];
237
238  /* check to see if received data */
239  if (MCF_UART_UISR(chan) & MCF_UART_UISR_RXRDY_FU) {
240    /* read data and put into the receive buffer */
241    while (MCF_UART_USR(chan) & MCF_UART_USR_RXRDY) {
242
243      if (MCF_UART_USR(chan) & MCF_UART_USR_ERROR) {
244        /* clear the error */
245        MCF_UART_UCR(chan) = MCF_UART_UCR_RESET_ERROR;
246      }
247      /* put data in rx buffer and check for errors */
248      info->rx_buffer[info->rx_in] = MCF_UART_URB(chan);
249
250      /* update buffer values */
251      info->rx_in++;
252
253      if (info->rx_in >= RX_BUFFER_SIZE) {
254        info->rx_in = 0;
255      }
256    }
257    /* Make sure the port has been opened */
258    if (info->ttyp) {
259
260      /* check to see if task driven */
261      if (info->iomode == TERMIOS_TASK_DRIVEN) {
262        /* notify rx task that rx buffer has data */
263        rtems_termios_rxirq_occured(info->ttyp);
264      } else {
265        /* Push up the received data */
266        rtems_termios_enqueue_raw_characters(info->ttyp, info->rx_buffer,
267                                             info->rx_in);
268        info->rx_in = 0;
269      }
270    }
271  }
272
273  /* check to see if data needs to be transmitted */
274  if ((info->uimr & MCF_UART_UIMR_TXRDY) &&
275      (MCF_UART_UISR(chan) & MCF_UART_UISR_TXRDY)) {
276
277    /* disable tx interrupts */
278    info->uimr &= ~MCF_UART_UIMR_TXRDY;
279    MCF_UART_UIMR(chan) = info->uimr;
280
281    /* tell upper level that character has been sent */
282    if (info->ttyp)
283      rtems_termios_dequeue_characters(info->ttyp, 1);
284  }
285}
286
287/***************************************************************************
288   Function : IntUartInitialize
289
290   Description : This initialises the internal uart hardware for all
291   internal uarts. If the internal uart is to be interrupt driven then the
292   interrupt vectors are hooked.
293 ***************************************************************************/
294static void IntUartInitialize(void)
295{
296  unsigned int chan;
297  struct IntUartInfoStruct *info;
298  rtems_isr_entry old_handler;
299  int level;
300
301  for (chan = 0; chan < MAX_UART_INFO; chan++) {
302    info = &IntUartInfo[chan];
303
304    info->ttyp = NULL;
305    info->rx_in = 0;
306    info->rx_out = 0;
307    info->baud = -1;
308    info->databits = -1;
309    info->parity = -1;
310    info->stopbits = -1;
311    info->hwflow = -1;
312    info->iomode = TERMIOS_POLLED;                /*polled console io */
313
314    MCF_UART_UACR(chan) = 0;
315    MCF_UART_UIMR(chan) = 0;
316    if (info->iomode != TERMIOS_POLLED) {
317      rtems_interrupt_catch(IntUartInterruptHandler,
318                            UART_INTC0_IRQ_VECTOR(chan), &old_handler);
319    }
320
321    /* set uart default values */
322    IntUartSetAttributes(chan, NULL);
323
324    /* unmask interrupt */
325    rtems_interrupt_disable(level);
326    switch (chan) {
327      case 0:
328        MCF_INTC0_ICR26 = MCF_INTC_ICR_IL(UART0_IRQ_LEVEL);
329        MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_INT_MASK26);
330        break;
331
332      case 1:
333        MCF_INTC0_ICR27 = MCF_INTC_ICR_IL(UART1_IRQ_LEVEL);
334        MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_INT_MASK27);
335        break;
336
337      case 2:
338        MCF_INTC0_ICR28 = MCF_INTC_ICR_IL(UART2_IRQ_LEVEL);
339        MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_INT_MASK28);
340        break;
341    }
342    rtems_interrupt_enable(level);
343
344  }                                               /* of chan loop */
345
346}                                                 /* IntUartInitialise */
347
348/***************************************************************************
349   Function : IntUartInterruptWrite
350
351   Description : This writes a single character to the appropriate uart
352   channel. This is either called during an interrupt or in the user's task
353   to initiate a transmit sequence. Calling this routine enables Tx
354   interrupts.
355 ***************************************************************************/
356static ssize_t IntUartInterruptWrite(int minor, const char *buf, size_t len)
357{
358  if (len > 0) {
359    /* write out character */
360    MCF_UART_UTB(minor) = *buf;
361
362    /* enable tx interrupt */
363    IntUartInfo[minor].uimr |= MCF_UART_UIMR_TXRDY;
364    MCF_UART_UIMR(minor) = IntUartInfo[minor].uimr;
365  }
366
367  return (0);
368}
369
370/***************************************************************************
371   Function : IntUartInterruptOpen
372
373   Description : This enables interrupts when the tty is opened.
374 ***************************************************************************/
375static int IntUartInterruptOpen(int major, int minor, void *arg)
376{
377  struct IntUartInfoStruct *info = &IntUartInfo[minor];
378
379  /* enable the uart */
380  MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_ENABLED | MCF_UART_UCR_RX_ENABLED);
381
382  /* check to see if interrupts need to be enabled */
383  if (info->iomode != TERMIOS_POLLED) {
384    /* enable rx interrupts */
385    info->uimr |= MCF_UART_UIMR_RXRDY_FU;
386    MCF_UART_UIMR(minor) = info->uimr;
387  }
388
389  /* check to see if doing hardware flow control */
390  if (info->hwflow) {
391    /* assert the RTS line */
392    MCF_UART_UOP1(minor) = 1;
393  }
394
395  return (0);
396}
397
398/***************************************************************************
399   Function : IntUartInterruptClose
400
401   Description : This disables interrupts when the tty is closed.
402 ***************************************************************************/
403static int IntUartInterruptClose(int major, int minor, void *arg)
404{
405  struct IntUartInfoStruct *info = &IntUartInfo[minor];
406
407  /* disable the interrupts and the uart */
408  MCF_UART_UIMR(minor) = 0;
409  MCF_UART_UCR(minor) = (MCF_UART_UCR_TX_DISABLED | MCF_UART_UCR_RX_DISABLED);
410
411  /* reset values */
412  info->ttyp = NULL;
413  info->uimr = 0;
414  info->rx_in = 0;
415  info->rx_out = 0;
416
417  return (0);
418}
419
420/***************************************************************************
421   Function : IntUartTaskRead
422
423   Description : This reads all available characters from the internal uart
424   and places them into the termios buffer.  The rx interrupts will be
425   re-enabled after all data has been read.
426 ***************************************************************************/
427static int IntUartTaskRead(int minor)
428{
429  char buffer[RX_BUFFER_SIZE];
430  int count;
431  int rx_in;
432  int index = 0;
433  struct IntUartInfoStruct *info = &IntUartInfo[minor];
434
435  /* determine number of values to copy out */
436  rx_in = info->rx_in;
437  if (info->rx_out <= rx_in) {
438    count = rx_in - info->rx_out;
439  } else {
440    count = (RX_BUFFER_SIZE - info->rx_out) + rx_in;
441  }
442
443  /* copy data into local buffer from rx buffer */
444  while ((index < count) && (index < RX_BUFFER_SIZE)) {
445    /* copy data byte */
446    buffer[index] = info->rx_buffer[info->rx_out];
447    index++;
448
449    /* increment rx buffer values */
450    info->rx_out++;
451    if (info->rx_out >= RX_BUFFER_SIZE) {
452      info->rx_out = 0;
453    }
454  }
455
456  /* check to see if buffer is not empty */
457  if (count > 0) {
458    /* set characters into termios buffer  */
459    rtems_termios_enqueue_raw_characters(info->ttyp, buffer, count);
460  }
461
462  return (EOF);
463}
464
465/***************************************************************************
466   Function : IntUartPollRead
467
468   Description : This reads a character from the internal uart. It returns
469   to the caller without blocking if not character is waiting.
470 ***************************************************************************/
471static int IntUartPollRead(int minor)
472{
473  if ((MCF_UART_USR(minor) & MCF_UART_USR_RXRDY) == 0)
474    return (-1);
475
476  return (MCF_UART_URB(minor));
477}
478
479/***************************************************************************
480   Function : IntUartPollWrite
481
482   Description : This writes out each character in the buffer to the
483   appropriate internal uart channel waiting till each one is sucessfully
484   transmitted.
485 ***************************************************************************/
486static ssize_t IntUartPollWrite(int minor, const char *buf, size_t len)
487{
488  size_t retval = len;
489  /* loop over buffer */
490  while (len--) {
491    /* block until we can transmit */
492    while ((MCF_UART_USR(minor) & MCF_UART_USR_TXRDY) == 0)
493      continue;
494    /* transmit data byte */
495    MCF_UART_UTB(minor) = *buf++;
496  }
497  return retval;
498}
499
500/***************************************************************************
501   Function : console_initialize
502
503   Description : This initialises termios, both sets of uart hardware before
504   registering /dev/tty devices for each channel and the system /dev/console.
505 ***************************************************************************/
506rtems_device_driver console_initialize(rtems_device_major_number major,
507                                       rtems_device_minor_number minor,
508                                       void *arg)
509{
510  rtems_status_code status;
511
512  /* Set up TERMIOS */
513  rtems_termios_initialize();
514
515  /* set io modes for the different channels and initialize device */
516  IntUartInfo[minor].iomode = TERMIOS_IRQ_DRIVEN;
517  IntUartInitialize();
518
519  /* Register the console port */
520  status = rtems_io_register_name("/dev/console", major, CONSOLE_PORT);
521  if (status != RTEMS_SUCCESSFUL) {
522    rtems_fatal_error_occurred(status);
523  }
524
525  /* Register the other port */
526  if (CONSOLE_PORT != 0) {
527    status = rtems_io_register_name("/dev/tty00", major, 0);
528    if (status != RTEMS_SUCCESSFUL) {
529      rtems_fatal_error_occurred(status);
530    }
531  }
532  if (CONSOLE_PORT != 1) {
533    status = rtems_io_register_name("/dev/tty01", major, 1);
534    if (status != RTEMS_SUCCESSFUL) {
535      rtems_fatal_error_occurred(status);
536    }
537  }
538
539  return (RTEMS_SUCCESSFUL);
540}
541
542/***************************************************************************
543   Function : console_open
544
545   Description : This actually opens the device depending on the minor
546   number set during initialisation. The device specific access routines are
547   passed to termios when the devices is opened depending on whether it is
548   polled or not.
549 ***************************************************************************/
550rtems_device_driver console_open(rtems_device_major_number major,
551                                 rtems_device_minor_number minor, void *arg)
552{
553  rtems_status_code status = RTEMS_INVALID_NUMBER;
554  rtems_libio_open_close_args_t *args = (rtems_libio_open_close_args_t *) arg;
555  struct IntUartInfoStruct *info;
556
557  static const rtems_termios_callbacks IntUartPollCallbacks = {
558    NULL,                                         /* firstOpen */
559    NULL,                                         /* lastClose */
560    IntUartPollRead,                              /* pollRead */
561    IntUartPollWrite,                             /* write */
562    IntUartSetAttributes,                         /* setAttributes */
563    NULL,                                         /* stopRemoteTx */
564    NULL,                                         /* startRemoteTx */
565    TERMIOS_POLLED                                /* mode */
566  };
567  static const rtems_termios_callbacks IntUartIntrCallbacks = {
568    IntUartInterruptOpen,                         /* firstOpen */
569    IntUartInterruptClose,                        /* lastClose */
570    NULL,                                         /* pollRead */
571    IntUartInterruptWrite,                        /* write */
572    IntUartSetAttributes,                         /* setAttributes */
573    NULL,                                         /* stopRemoteTx */
574    NULL,                                         /* startRemoteTx */
575    TERMIOS_IRQ_DRIVEN                            /* mode */
576  };
577
578  static const rtems_termios_callbacks IntUartTaskCallbacks = {
579    IntUartInterruptOpen,                         /* firstOpen */
580    IntUartInterruptClose,                        /* lastClose */
581    IntUartTaskRead,                              /* pollRead */
582    IntUartInterruptWrite,                        /* write */
583    IntUartSetAttributes,                         /* setAttributes */
584    NULL,                                         /* stopRemoteTx */
585    NULL,                                         /* startRemoteTx */
586    TERMIOS_TASK_DRIVEN                           /* mode */
587  };
588
589  /* open the port depending on the minor device number */
590  if ((minor >= 0) && (minor < MAX_UART_INFO)) {
591    info = &IntUartInfo[minor];
592    switch (info->iomode) {
593      case TERMIOS_POLLED:
594        status = rtems_termios_open(major, minor, arg, &IntUartPollCallbacks);
595        break;
596      case TERMIOS_IRQ_DRIVEN:
597        status = rtems_termios_open(major, minor, arg, &IntUartIntrCallbacks);
598        info->ttyp = args->iop->data1;
599        break;
600      case TERMIOS_TASK_DRIVEN:
601        status = rtems_termios_open(major, minor, arg, &IntUartTaskCallbacks);
602        info->ttyp = args->iop->data1;
603        break;
604    }
605  }
606
607  if (status == RTEMS_SUCCESSFUL) {
608    /*
609     * Reset the default baudrate.
610     */
611    struct termios term;
612
613    if (tcgetattr(STDIN_FILENO, &term) >= 0) {
614      term.c_cflag &= ~(CSIZE);
615      term.c_cflag |= CS8;
616      term.c_ispeed = B19200;
617      term.c_ospeed = B19200;
618      tcsetattr(STDIN_FILENO, TCSANOW, &term);
619    }
620  }
621
622  return (status);
623}
624
625/***************************************************************************
626   Function : console_close
627
628   Description : This closes the device via termios
629 ***************************************************************************/
630rtems_device_driver console_close(rtems_device_major_number major,
631                                  rtems_device_minor_number minor, void *arg)
632{
633  return (rtems_termios_close(arg));
634}
635
636/***************************************************************************
637   Function : console_read
638
639   Description : Read from the device via termios
640 ***************************************************************************/
641rtems_device_driver console_read(rtems_device_major_number major,
642                                 rtems_device_minor_number minor, void *arg)
643{
644  return (rtems_termios_read(arg));
645}
646
647/***************************************************************************
648   Function : console_write
649
650   Description : Write to the device via termios
651 ***************************************************************************/
652rtems_device_driver console_write(rtems_device_major_number major,
653                                  rtems_device_minor_number minor, void *arg)
654{
655  return (rtems_termios_write(arg));
656}
657
658/***************************************************************************
659   Function : console_ioctl
660
661   Description : Pass the IOCtl call to termios
662 ***************************************************************************/
663rtems_device_driver console_control(rtems_device_major_number major,
664                                    rtems_device_minor_number minor,
665                                    void *arg)
666{
667  return (rtems_termios_ioctl(arg));
668}
Note: See TracBrowser for help on using the repository browser.