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

4.104.11
Last change on this file since 602aee20 was 602aee20, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on Oct 10, 2008 at 3:50:15 PM

shared/include/utility.h: Removed file.

shared/include/powerpc-utility.h: Use constraint "b" for address
base registers in inline assembler statements.
Update for status-checks.h changes.

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