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

4.104.11
Last change on this file since d374492 was d374492, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on Jul 21, 2009 at 8:38:04 AM

Update for MPC55XX changes

  • 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 0 on success or -1 otherwise.
257 */
258static int mpc55xx_esci_termios_poll_write( int minor, const char *out, int n)
259{
260        mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
261        int i = 0;
262
263        /* Check minor number */
264        if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
265                return -1;
266        }
267
268        /* Write */
269        for (i = 0; i < n; ++i) {
270                mpc55xx_esci_write_char( e, out [i]);
271        }
272
273        return 0;
274}
275
276/**
277 * @brief Writes one character from @a out to port @a minor.
278 *
279 * @return Returns always 0.
280 *
281 * @note The buffer @a out has to provide at least one character.
282 * This function assumes that the transmit data register is empty.
283 */
284static int mpc55xx_esci_termios_write( int minor, const char *out, int n)
285{
286        mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
287
288        /* Write */
289        e->regs->DR.B.D = out [0];
290
291        return 0;
292}
293
294/* FIXME, TODO */
295extern uint32_t bsp_clock_speed;
296
297/**
298 * @brief Sets attributes of port @a minor according to termios attributes @a t.
299 *
300 * @return Status code.
301 */
302static int mpc55xx_esci_termios_set_attributes( int minor, const struct termios *t)
303{
304        mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
305        volatile struct ESCI_tag *regs = e->regs;
306        union ESCI_CR1_tag cr1 = MPC55XX_ZERO_FLAGS;
307        union ESCI_CR2_tag cr2 = MPC55XX_ZERO_FLAGS;
308        unsigned br = 0;
309
310        /* Check minor number */
311        if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
312                return RTEMS_INVALID_NUMBER;
313        }
314
315        /* Enable module */
316        cr2.B.MDIS = 0;
317
318        /* Interrupts */
319        if (MPC55XX_ESCI_USE_INTERRUPTS( e) && e->tty != NULL) {
320                cr1.B.RIE = 1;
321                cr1.B.TIE = 1;
322        } else {
323                cr1.B.RIE = 0;
324                cr1.B.TIE = 0;
325        }
326        cr1.B.TCIE = 0;
327        cr1.B.ILIE = 0;
328        cr2.B.IEBERR = 0;
329        cr2.B.ORIE = 0;
330        cr2.B.NFIE = 0;
331        cr2.B.FEIE = 0;
332        cr2.B.PFIE = 0;
333
334        /* Disable receiver wake-up standby */
335        cr1.B.RWU = 0;
336
337        /* Disable DMA channels */
338        cr2.B.RXDMA = 0;
339        cr2.B.TXDMA = 0;
340
341        /* Idle line type */
342        cr1.B.ILT = 0;
343
344        /* Disable loops */
345        cr1.B.LOOPS = 0;
346
347        /* Enable or disable receiver */
348        cr1.B.RE = (t->c_cflag & CREAD) ? 1 : 0;
349
350        /* Enable transmitter */
351        cr1.B.TE = 1;
352
353        /* Baud rate */
354        switch (t->c_cflag & CBAUD) {
355                case B50: br = 50; break;
356                case B75: br = 75; break;
357                case B110: br = 110; break;
358                case B134: br = 134; break;
359                case B150: br = 150; break;
360                case B200: br = 200; break;
361                case B300: br = 300; break;
362                case B600: br = 600; break;
363                case B1200: br = 1200; break;
364                case B1800: br = 1800; break;
365                case B2400: br = 2400; break;
366                case B4800: br = 4800; break;
367                case B9600: br = 9600; break;
368                case B19200: br = 19200; break;
369                case B38400: br = 38400; break;
370                case B57600: br = 57600; break;
371                case B115200: br = 115200; break;
372                case B230400: br = 230400; break;
373                case B460800: br = 460800; break;
374                default: br = 0; break;
375        }
376        if (br > 0) {
377                br = bsp_clock_speed / (16 * br);
378                br = (br > 8191) ? 8191 : br;
379        } else {
380                br = 0;
381        }
382        cr1.B.SBR = br;
383                 
384        /* Number of data bits */
385        if ((t->c_cflag & CSIZE) != CS8) {
386                return RTEMS_IO_ERROR;
387        }
388        cr1.B.M = 0;
389
390        /* Parity */
391        cr1.B.PE = (t->c_cflag & PARENB) ? 1 : 0;
392        cr1.B.PT = (t->c_cflag & PARODD) ? 1 : 0;
393       
394        /* Stop bits */
395        if ( t->c_cflag & CSTOPB ) {
396                /* Two stop bits */
397                return RTEMS_IO_ERROR;
398        }
399
400        /* Set control registers */
401        regs->CR1.R = cr1.R;
402        regs->CR2.R = cr2.R;
403
404        /* Disable LIN */
405        regs->LCR.R = 0;
406
407        return RTEMS_SUCCESSFUL;
408}
409
410/**
411 * @brief Interrupt handler.
412 */
413static void mpc55xx_esci_termios_interrupt_handler( rtems_vector_number vector, void *arg)
414{
415        mpc55xx_esci_driver_entry *e = (mpc55xx_esci_driver_entry *) arg;
416        volatile union ESCI_SR_tag *status = &e->regs->SR;
417        volatile union ESCI_DR_tag *data = &e->regs->DR;
418        union ESCI_SR_tag sr = MPC55XX_ZERO_FLAGS;
419        union ESCI_SR_tag active = MPC55XX_ZERO_FLAGS;
420
421        /* Status */
422        sr.R = status->R;
423
424        /* Receive data register full? */
425        if (sr.B.RDRF != 0) {
426                active.B.RDRF = 1;
427        }
428
429        /* Transmit data register empty? */
430        if (sr.B.TDRE != 0) {
431                active.B.TDRE = 1;
432        }
433
434        /* Clear flags */
435        status->R = active.R;
436
437        /* Enqueue */
438        if (active.B.RDRF != 0) {
439                char c = data->B.D;
440                rtems_termios_enqueue_raw_characters( e->tty, &c, 1);
441        }
442
443        /* Dequeue */
444        if (active.B.TDRE != 0) {
445                rtems_termios_dequeue_characters( e->tty, 1);
446        }
447}
448
449/** @} */
450
451/**
452 * @brief Termios callbacks with interrupt support.
453 */
454static const rtems_termios_callbacks mpc55xx_esci_termios_callbacks = {
455        .firstOpen = mpc55xx_esci_termios_first_open,
456        .lastClose = mpc55xx_esci_termios_last_close,
457        .pollRead = NULL,
458        .write = mpc55xx_esci_termios_write,
459        .setAttributes = mpc55xx_esci_termios_set_attributes,
460        .stopRemoteTx = NULL,
461        .startRemoteTx = NULL,
462        .outputUsesInterrupts = TERMIOS_IRQ_DRIVEN
463};
464
465/**
466 * @brief Termios callbacks with polling functions (no interrupts).
467 */
468static const rtems_termios_callbacks mpc55xx_esci_termios_callbacks_polled = {
469        .firstOpen = mpc55xx_esci_termios_first_open,
470        .lastClose = mpc55xx_esci_termios_last_close,
471        .pollRead = mpc55xx_esci_termios_poll_read,
472        .write = mpc55xx_esci_termios_poll_write,
473        .setAttributes = mpc55xx_esci_termios_set_attributes,
474        .stopRemoteTx = NULL,
475        .startRemoteTx = NULL,
476        .outputUsesInterrupts = TERMIOS_POLLED
477};
478
479/**
480 * @name Console Driver
481 * @{
482 */
483
484rtems_device_driver console_initialize( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
485{
486        rtems_status_code sc = RTEMS_SUCCESSFUL;
487        int console_done = 0; 
488        int termios_do_init = 1; 
489        rtems_device_minor_number i = 0;
490        mpc55xx_esci_driver_entry *e = NULL;
491       
492        for (i = 0; i < MPC55XX_ESCI_NUMBER; ++i) {
493                e = &mpc55xx_esci_driver_table [i];
494                sc = rtems_io_register_name ( e->device_name, major, i);
495                RTEMS_CHECK_SC( sc, "Register IO device");
496                if (e->console) {
497                        if (console_done) {
498                                RTEMS_SYSLOG_WARNING( "Multiple console ports defined\n");
499                        } else {
500                                console_done = 1;
501                                if (e->use_interrupts) {
502                                        RTEMS_SYSLOG_WARNING( "Cannot use interrupts for console port\n");
503                                }
504                                sc = rtems_io_register_name( CONSOLE_DEVICE_NAME, major, i);
505                                RTEMS_CHECK_SC( sc, "Register IO device");
506                        }
507                }
508                if (e->use_termios && termios_do_init) {
509                        if (termios_do_init) {
510                                termios_do_init = 0;
511                                rtems_termios_initialize();
512                        }
513                        if (MPC55XX_ESCI_USE_INTERRUPTS( e)) {
514                                sc = mpc55xx_interrupt_handler_install(
515                                        e->irq_number,
516                                        "eSCI",
517                                        RTEMS_INTERRUPT_UNIQUE,
518                                        MPC55XX_ESCI_IRQ_PRIORITY,
519                                        mpc55xx_esci_termios_interrupt_handler,
520                                        e
521                                );
522                                RTEMS_CHECK_SC( sc, "Install IRQ handler");
523                        }
524                }
525                mpc55xx_esci_termios_set_attributes( (int) i, &mpc55xx_esci_termios_default);
526        }
527
528        return RTEMS_SUCCESSFUL;
529}
530
531rtems_device_driver console_open( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
532{
533        rtems_status_code sc = RTEMS_SUCCESSFUL;
534        int rv = 0;
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.