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

5
Last change on this file since d7d66d7 was d7d66d7, checked in by Sebastian Huber <sebastian.huber@…>, on Apr 19, 2018 at 4:28:01 AM

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