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

4.104.115
Last change on this file since 29313369 was 29313369, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 04/07/10 at 06:45:59

changes to support GW_LCFM

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