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

4.11
Last change on this file since c499856 was c499856, checked in by Chris Johns <chrisj@…>, on Mar 20, 2014 at 9:10:47 PM

Change all references of rtems.com to rtems.org.

  • 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_cflag & CBAUD) {
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.