source: rtems/c/src/lib/libbsp/m68k/mcf52235/console/console.c @ 39a9f8e

4.104.115
Last change on this file since 39a9f8e was 39a9f8e, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 12/17/09 at 08:42:17

adapted to new prototype for *_write function

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