source: rtems/c/src/lib/libcpu/powerpc/mpc55xx/esci/esci.c @ 1c6926c1

Last change on this file since 1c6926c1 was 1c6926c1, checked in by Kevin Kirspel <kevin-kirspel@…>, on Mar 21, 2017 at 7:39:48 PM

termios: Synchronize with latest FreeBSD headers

Adding modified FreeBSD headers to synchronize RTEMS termios with
FreeBSD. Modify termios to support dedicated input and output baud for
termios structure. Updated BSPs to use dedicated input and output baud
in termios structure. Updated tools to use dedicated input and output
baud in termios structure. Updated termios testsuites to use dedicated
input and output baud in termios structure.

Close #2897.

  • Property mode set to 100644
File size: 15.1 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup mpc55xx_esci
5 *
6 * @brief Source file for the Enhanced Serial Communication Interface (eSCI).
7 */
8
9/*
10 * Copyright (c) 2008-2011 embedded brains GmbH.  All rights reserved.
11 *
12 *  embedded brains GmbH
13 *  Obere Lagerstr. 30
14 *  82178 Puchheim
15 *  Germany
16 *  <rtems@embedded-brains.de>
17 *
18 * The license and distribution terms for this file may be
19 * found in the file LICENSE in this distribution or at
20 * http://www.rtems.org/license/LICENSE.
21 */
22
23/* Include order is important */
24#include <mpc55xx/regs.h>
25#include <mpc55xx/esci.h>
26#include <bsp/irq.h>
27
28#include <assert.h>
29#include <unistd.h>
30#include <termios.h>
31
32#include <rtems.h>
33#include <rtems/libio.h>
34#include <rtems/console.h>
35#include <rtems/bspIo.h>
36
37#include <bspopts.h>
38
39/* Evil define conflicts */
40#define TERMIOS_CR1 CR1
41#undef CR1
42#define TERMIOS_CR2 CR2
43#undef CR2
44
45#define MPC55XX_ESCI_IRQ_PRIORITY MPC55XX_INTC_DEFAULT_PRIORITY
46
47#define MPC55XX_ESCI_IS_MINOR_INVALD(minor) ((minor) < 0 || (minor) >= MPC55XX_ESCI_NUMBER)
48
49/**
50 * @brief eSCI driver table.
51 */
52mpc55xx_esci_driver_entry mpc55xx_esci_driver_table [MPC55XX_ESCI_NUMBER] = { {
53                .regs = &ESCI_A,
54                .device_name = "/dev/ttyS0",
55                .use_termios = 1,
56                .use_interrupts = MPC55XX_ESCI_USE_INTERRUPTS,
57                .tty = NULL,
58                .irq_number = MPC55XX_IRQ_ESCI(0)
59        }, {
60                .regs = &ESCI_B,
61                .device_name = "/dev/ttyS1",
62                .use_termios = 1,
63                .use_interrupts = MPC55XX_ESCI_USE_INTERRUPTS,
64                .tty = NULL,
65                .irq_number = MPC55XX_IRQ_ESCI(1)
66        }
67};
68
69/**
70 * @brief Default termios configuration.
71 */
72static const struct termios mpc55xx_esci_termios_default = {
73        .c_cflag = CS8 | CREAD | CLOCAL | B115200
74};
75
76/**
77 * @name Low-Level
78 * @{
79 */
80
81/**
82 * @brief Reads one character from the receive register.
83 *
84 * @note Waits for the receive data register full flag.
85 */
86static inline uint8_t mpc55xx_esci_read_char( mpc55xx_esci_driver_entry *e)
87{
88        volatile union ESCI_SR_tag *status = &e->regs->SR;
89        volatile union ESCI_DR_tag *data = &e->regs->DR;
90        union ESCI_SR_tag sr = MPC55XX_ZERO_FLAGS;
91
92        while (status->B.RDRF == 0) {
93                /* Wait */
94        }
95
96        /* Clear flag */
97        sr.B.RDRF = 1;
98        status->R = sr.R;
99
100        /* Read */
101        return data->B.D;
102}
103
104static inline void mpc55xx_esci_write_char(mpc55xx_esci_driver_entry *e, char c)
105{
106        static const union ESCI_SR_tag clear_tdre = { .B = { .TDRE = 1 } };
107        volatile struct ESCI_tag *esci = e->regs;
108        rtems_interrupt_level level;
109        bool done = false;
110
111        rtems_interrupt_disable(level);
112        if (e->transmit_nest_level == 0) {
113                union ESCI_CR1_tag cr1 = { .R = esci->CR1.R };
114
115                if (cr1.B.TIE != 0) {
116                        cr1.B.TIE = 0;
117                        e->transmit_nest_level = 1;
118                        esci->CR1.R = cr1.R;
119                }
120        } else {
121                ++e->transmit_nest_level;
122        }
123        rtems_interrupt_enable(level);
124
125        while (!done) {
126                rtems_interrupt_disable(level);
127                bool tx = e->transmit_in_progress;
128                if (!tx || (tx && esci->SR.B.TDRE)) {
129                        esci->SR.R = clear_tdre.R;
130                        esci->DR.B.D = c;
131                        e->transmit_in_progress = true;
132                        done = true;
133                }
134                rtems_interrupt_enable(level);
135        }
136
137        rtems_interrupt_disable(level);
138        if (e->transmit_nest_level > 0) {
139                --e->transmit_nest_level;
140
141                if (e->transmit_nest_level == 0) {
142                        union ESCI_CR1_tag cr1 = { .R = esci->CR1.R };
143
144                        cr1.B.TIE = 1;
145                        esci->CR1.R = cr1.R;
146                }
147        }
148        rtems_interrupt_enable(level);
149}
150
151static inline void mpc55xx_esci_interrupts_enable( mpc55xx_esci_driver_entry *e)
152{
153        union ESCI_CR1_tag cr1 = MPC55XX_ZERO_FLAGS;
154        rtems_interrupt_level level;
155
156        rtems_interrupt_disable( level);
157        cr1.R = e->regs->CR1.R;
158        cr1.B.RIE = 1;
159        cr1.B.TIE = 1;
160        e->regs->CR1.R = cr1.R;
161        rtems_interrupt_enable( level);
162}
163
164static inline void mpc55xx_esci_interrupts_disable( mpc55xx_esci_driver_entry *e)
165{
166        union ESCI_CR1_tag cr1 = MPC55XX_ZERO_FLAGS;
167        rtems_interrupt_level level;
168
169        rtems_interrupt_disable( level);
170        cr1.R = e->regs->CR1.R;
171        cr1.B.RIE = 0;
172        cr1.B.TIE = 0;
173        e->regs->CR1.R = cr1.R;
174        rtems_interrupt_enable( level);
175}
176
177/** @} */
178
179/**
180 * @name Termios Support
181 * @{
182 */
183
184/**
185 * @brief Opens port @a minor.
186 *
187 * @return Status code.
188 */
189static int mpc55xx_esci_termios_first_open( int major, int minor, void *arg)
190{
191        int rv = 0;
192        mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
193        struct rtems_termios_tty *tty = ((rtems_libio_open_close_args_t *) arg)->iop->data1;
194
195        /* Check minor number */
196        if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
197                return RTEMS_INVALID_ID;
198        }
199
200        /* Connect TTY */
201        e->tty = tty;
202
203        /* Enable interrupts */
204        if (e->use_interrupts) {
205                mpc55xx_esci_interrupts_enable( e);
206        }
207
208        rv = rtems_termios_set_initial_baud( e->tty, 115200);
209        assert(rv == 0);
210
211        return 0;
212}
213
214/**
215 * @brief Closes port @a minor.
216 *
217 * @return Status code.
218 */
219static int mpc55xx_esci_termios_last_close( int major, int minor, void* arg)
220{
221        mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
222
223        /* Check minor number */
224        if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
225                return RTEMS_INVALID_ID;
226        }
227
228        /* Disable interrupts */
229        mpc55xx_esci_interrupts_disable( e);
230
231        /* Disconnect TTY */
232        e->tty = NULL;
233
234        return RTEMS_SUCCESSFUL;
235}
236
237/**
238 * @brief Reads one character from port @a minor.
239 *
240 * @return Returns the character or -1 on error.
241 */
242static int mpc55xx_esci_termios_poll_read( int minor)
243{
244        mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
245        volatile union ESCI_SR_tag *status = &e->regs->SR;
246        volatile union ESCI_DR_tag *data = &e->regs->DR;
247        union ESCI_SR_tag sr = MPC55XX_ZERO_FLAGS;
248
249        if (status->B.RDRF == 0) {
250                return -1;
251        }
252
253        /* Clear flag */
254        sr.B.RDRF = 1;
255        status->R = sr.R;
256
257        /* Read */
258        return data->B.D;
259}
260
261/**
262 * @brief Writes @a n characters from @a out to port @a minor.
263 *
264 * @return Returns number of chars sent on success or -1 otherwise.
265 */
266static int mpc55xx_esci_termios_poll_write( int minor, const char *out,
267                                            size_t n)
268{
269        mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
270        size_t i = 0;
271
272        /* Check minor number */
273        if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
274                return -1;
275        }
276
277        /* Write */
278        for (i = 0; i < n; ++i) {
279                mpc55xx_esci_write_char( e, out [i]);
280        }
281
282        return n;
283}
284
285/**
286 * @brief Writes one character from @a out to port @a minor.
287 *
288 * @return (always 0).
289 *
290 * @note The buffer @a out has to provide at least one character.
291 * This function assumes that the transmit data register is empty.
292 */
293static int mpc55xx_esci_termios_write( int minor, const char *out, size_t n)
294{
295        if (n > 0) {
296                mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
297
298                e->regs->DR.B.D = out [0];
299                e->transmit_in_progress = true;
300        }
301
302        return 0;
303}
304
305/* FIXME, TODO */
306extern uint32_t bsp_clock_speed;
307
308/**
309 * @brief Sets attributes of port @a minor according to termios attributes @a t.
310 *
311 * @return Status code.
312 */
313static int mpc55xx_esci_termios_set_attributes( int minor, const struct termios *t)
314{
315        mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
316        volatile struct ESCI_tag *regs = e->regs;
317        union ESCI_CR1_tag cr1 = MPC55XX_ZERO_FLAGS;
318        union ESCI_CR2_tag cr2 = MPC55XX_ZERO_FLAGS;
319        unsigned br = 0;
320
321        /* Check minor number */
322        if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
323                return RTEMS_INVALID_ID;
324        }
325
326        /* Enable module */
327        cr2.B.MDIS = 0;
328
329        /* Interrupts */
330        if (e->use_interrupts && e->tty != NULL) {
331                cr1.B.RIE = 1;
332                cr1.B.TIE = 1;
333        } else {
334                cr1.B.RIE = 0;
335                cr1.B.TIE = 0;
336        }
337        cr1.B.TCIE = 0;
338        cr1.B.ILIE = 0;
339        cr2.B.IEBERR = 0;
340        cr2.B.ORIE = 0;
341        cr2.B.NFIE = 0;
342        cr2.B.FEIE = 0;
343        cr2.B.PFIE = 0;
344
345        /* Disable receiver wake-up standby */
346        cr1.B.RWU = 0;
347
348        /* Disable DMA channels */
349        cr2.B.RXDMA = 0;
350        cr2.B.TXDMA = 0;
351
352        /* Idle line type */
353        cr1.B.ILT = 0;
354
355        /* Disable loops */
356        cr1.B.LOOPS = 0;
357
358        /* Enable or disable receiver */
359        cr1.B.RE = (t->c_cflag & CREAD) ? 1 : 0;
360
361        /* Enable transmitter */
362        cr1.B.TE = 1;
363
364        /* Baud rate */
365  switch (t->c_ospeed) {
366                case B50: br = 50; break;
367                case B75: br = 75; break;
368                case B110: br = 110; break;
369                case B134: br = 134; break;
370                case B150: br = 150; break;
371                case B200: br = 200; break;
372                case B300: br = 300; break;
373                case B600: br = 600; break;
374                case B1200: br = 1200; break;
375                case B1800: br = 1800; break;
376                case B2400: br = 2400; break;
377                case B4800: br = 4800; break;
378                case B9600: br = 9600; break;
379                case B19200: br = 19200; break;
380                case B38400: br = 38400; break;
381                case B57600: br = 57600; break;
382                case B115200: br = 115200; break;
383                case B230400: br = 230400; break;
384                case B460800: br = 460800; break;
385                default: br = 0; break;
386        }
387        if (br > 0) {
388                br = bsp_clock_speed / (16 * br);
389                br = (br > 8191) ? 8191 : br;
390        } else {
391                br = 0;
392        }
393        cr1.B.SBR = br;
394
395        /* Number of data bits */
396        if ((t->c_cflag & CSIZE) != CS8) {
397                return RTEMS_IO_ERROR;
398        }
399        cr1.B.M = 0;
400
401        /* Parity */
402        cr1.B.PE = (t->c_cflag & PARENB) ? 1 : 0;
403        cr1.B.PT = (t->c_cflag & PARODD) ? 1 : 0;
404
405        /* Stop bits */
406        if ( t->c_cflag & CSTOPB ) {
407                /* Two stop bits */
408                return RTEMS_IO_ERROR;
409        }
410
411        /* Disable LIN */
412        regs->LCR.R = 0;
413
414        /* Set control registers */
415        regs->CR2.R = cr2.R;
416        regs->CR1.R = cr1.R;
417
418        return RTEMS_SUCCESSFUL;
419}
420
421/**
422 * @brief Interrupt handler.
423 */
424static void mpc55xx_esci_termios_interrupt_handler( void *arg)
425{
426        mpc55xx_esci_driver_entry *e = (mpc55xx_esci_driver_entry *) arg;
427        volatile union ESCI_SR_tag *status = &e->regs->SR;
428        volatile union ESCI_DR_tag *data = &e->regs->DR;
429        union ESCI_SR_tag sr = MPC55XX_ZERO_FLAGS;
430        union ESCI_SR_tag active = MPC55XX_ZERO_FLAGS;
431        rtems_interrupt_level level;
432
433        /* Status */
434        sr.R = status->R;
435
436        /* Receive data register full? */
437        if (sr.B.RDRF != 0) {
438                active.B.RDRF = 1;
439        }
440
441        /* Transmit data register empty? */
442        if (sr.B.TDRE != 0) {
443                active.B.TDRE = 1;
444        }
445
446        /* Clear flags */
447        rtems_interrupt_disable(level);
448        status->R = active.R;
449        e->transmit_in_progress = false;
450        rtems_interrupt_enable(level);
451
452        /* Enqueue */
453        if (active.B.RDRF != 0) {
454                char c = data->B.D;
455                rtems_termios_enqueue_raw_characters( e->tty, &c, 1);
456        }
457
458        /* Dequeue */
459        if (active.B.TDRE != 0) {
460                rtems_termios_dequeue_characters( e->tty, 1);
461        }
462}
463
464/** @} */
465
466/**
467 * @brief Termios callbacks with interrupt support.
468 */
469static const rtems_termios_callbacks mpc55xx_esci_termios_callbacks = {
470        .firstOpen = mpc55xx_esci_termios_first_open,
471        .lastClose = mpc55xx_esci_termios_last_close,
472        .pollRead = NULL,
473        .write = mpc55xx_esci_termios_write,
474        .setAttributes = mpc55xx_esci_termios_set_attributes,
475        .stopRemoteTx = NULL,
476        .startRemoteTx = NULL,
477        .outputUsesInterrupts = TERMIOS_IRQ_DRIVEN
478};
479
480/**
481 * @brief Termios callbacks with polling functions (no interrupts).
482 */
483static const rtems_termios_callbacks mpc55xx_esci_termios_callbacks_polled = {
484        .firstOpen = mpc55xx_esci_termios_first_open,
485        .lastClose = mpc55xx_esci_termios_last_close,
486        .pollRead = mpc55xx_esci_termios_poll_read,
487        .write = mpc55xx_esci_termios_poll_write,
488        .setAttributes = mpc55xx_esci_termios_set_attributes,
489        .stopRemoteTx = NULL,
490        .startRemoteTx = NULL,
491        .outputUsesInterrupts = TERMIOS_POLLED
492};
493
494/**
495 * @name Console Driver
496 * @{
497 */
498
499rtems_device_driver console_initialize( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
500{
501        rtems_status_code sc = RTEMS_SUCCESSFUL;
502        int termios_do_init = 1;
503        rtems_device_minor_number i = 0;
504        mpc55xx_esci_driver_entry *e = NULL;
505
506        for (i = 0; i < MPC55XX_ESCI_NUMBER; ++i) {
507                e = &mpc55xx_esci_driver_table [i];
508                sc = rtems_io_register_name ( e->device_name, major, i);
509                if (sc != RTEMS_SUCCESSFUL) {
510                        /* FIXME */
511                        rtems_fatal_error_occurred(0xdeadbeef);
512                }
513                if (i == MPC55XX_ESCI_CONSOLE_MINOR) {
514                        sc = rtems_io_register_name( CONSOLE_DEVICE_NAME, major, i);
515                        if (sc != RTEMS_SUCCESSFUL) {
516                                /* FIXME */
517                                rtems_fatal_error_occurred(0xdeadbeef);
518                        }
519                }
520                if (e->use_termios && termios_do_init) {
521                        if (termios_do_init) {
522                                termios_do_init = 0;
523                                rtems_termios_initialize();
524                        }
525                        if (e->use_interrupts) {
526                                sc = mpc55xx_interrupt_handler_install(
527                                        e->irq_number,
528                                        "eSCI",
529                                        RTEMS_INTERRUPT_UNIQUE,
530                                        MPC55XX_ESCI_IRQ_PRIORITY,
531                                        mpc55xx_esci_termios_interrupt_handler,
532                                        e
533                                );
534                                if (sc != RTEMS_SUCCESSFUL) {
535                                        /* FIXME */
536                                        rtems_fatal_error_occurred(0xdeadbeef);
537                                }
538                        }
539                }
540                mpc55xx_esci_termios_set_attributes( (int) i, &mpc55xx_esci_termios_default);
541        }
542
543        return RTEMS_SUCCESSFUL;
544}
545
546rtems_device_driver console_open( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
547{
548        rtems_status_code sc = RTEMS_SUCCESSFUL;
549        mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
550
551        /* Check minor number */
552        if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
553                return RTEMS_INVALID_ID;
554        }
555
556        if (e->use_termios) {
557                if (e->use_interrupts) {
558                        sc =  rtems_termios_open( major, minor, arg, &mpc55xx_esci_termios_callbacks);
559                } else {
560                        sc =  rtems_termios_open( major, minor, arg, &mpc55xx_esci_termios_callbacks_polled);
561                }
562        }
563
564        return sc;
565}
566
567rtems_device_driver console_close( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
568{
569        mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
570
571        /* Check minor number */
572        if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
573                return RTEMS_INVALID_ID;
574        }
575
576        if (e->use_termios) {
577                return rtems_termios_close( arg);
578        }
579
580        return RTEMS_SUCCESSFUL;
581}
582
583rtems_device_driver console_read( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
584{
585        mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
586
587        /* Check minor number */
588        if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
589                return RTEMS_INVALID_ID;
590        }
591
592        if (e->use_termios) {
593                return rtems_termios_read( arg);
594        } else {
595                rtems_libio_rw_args_t *rw = (rtems_libio_rw_args_t *) arg;
596                uint32_t i = 0;
597                while (i < rw->count) {
598                        rw->buffer [i] = mpc55xx_esci_read_char( e);
599                        ++i;
600                }
601                rw->bytes_moved = i;
602        }
603
604        return RTEMS_SUCCESSFUL;
605}
606
607rtems_device_driver console_write( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
608{
609        mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
610
611        /* Check minor number */
612        if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
613                return RTEMS_INVALID_ID;
614        }
615
616        if (e->use_termios) {
617                return rtems_termios_write( arg);
618        } else {
619                rtems_libio_rw_args_t *rw = (rtems_libio_rw_args_t *) arg;
620                uint32_t i = 0;
621                while (i < rw->count) {
622                        if (rw->buffer [i] == '\n') {
623                                mpc55xx_esci_write_char( e, '\r');
624                        }
625                        mpc55xx_esci_write_char( e, rw->buffer [i]);
626                        ++i;
627                }
628                rw->bytes_moved = i;
629        }
630
631        return RTEMS_SUCCESSFUL;
632}
633
634rtems_device_driver console_control( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
635{
636        mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
637
638        /* Check minor number */
639        if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
640                return RTEMS_INVALID_ID;
641        }
642
643        if (e->use_termios) {
644                return rtems_termios_ioctl( arg);
645        }
646
647        return RTEMS_NOT_DEFINED;
648}
649
650/** @} */
651
652/**
653 * @name BSP Character Output
654 * @{
655 */
656
657static void mpc55xx_esci_output_char( char c)
658{
659        mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [MPC55XX_ESCI_CONSOLE_MINOR];
660
661        mpc55xx_esci_interrupts_disable( e);
662        if (c == '\n') {
663                mpc55xx_esci_write_char( e, '\r');
664        }
665        mpc55xx_esci_write_char( e, c);
666        mpc55xx_esci_interrupts_enable( e);
667}
668
669static void mpc55xx_esci_output_char_init( char c)
670{
671        mpc55xx_esci_termios_set_attributes( MPC55XX_ESCI_CONSOLE_MINOR, &mpc55xx_esci_termios_default);
672        mpc55xx_esci_output_char( c);
673        BSP_output_char = mpc55xx_esci_output_char;
674}
675
676/** @} */
677
678BSP_output_char_function_type BSP_output_char = mpc55xx_esci_output_char_init;
679BSP_polling_getchar_function_type BSP_poll_char = NULL;
Note: See TracBrowser for help on using the repository browser.