source: rtems/bsps/arm/csb336/console/uart.c @ a3fe23c

5
Last change on this file since a3fe23c was d7d66d7, checked in by Sebastian Huber <sebastian.huber@…>, on Apr 19, 2018 at 4:28:01 AM

bsps: Move console drivers to bsps

This patch is a part of the BSP source reorganization.

Update #3285.

  • Property mode set to 100644
File size: 11.9 KB
Line 
1/*
2 * Console driver for MC9328XML UARTs.
3 *
4 * Written Jay Monkman <jtm@lopingdog.com>
5 * Copyright (c) 2005 by Loping Dog Embedded Systems
6 *
7 * The license and distribution terms for this file may be
8 * found in the file LICENSE in this distribution or at
9 *    http://www.rtems.org/license
10 */
11#include <bsp.h>
12#include <rtems/libio.h>
13#include <libchip/sersupp.h>
14#include <rtems/error.h>
15#include <rtems/bspIo.h>
16#include <assert.h>
17#include <termios.h>
18#include <rtems/irq.h>
19#include <bsp/irq.h>
20#include <mc9328mxl.h>
21#include <rtems/console.h>
22#include <inttypes.h>
23
24/* Define this to use interrupt driver UART driver */
25#define USE_INTERRUPTS 1
26
27/* How many serial ports? */
28#define NUM_DEVS       2
29#define poll_write(c)  imx_uart_poll_write_char(0, c)
30#define poll_read()  imx_uart_poll_read_char(0)
31
32static int imx_uart_first_open(int, int, void *);
33static int imx_uart_last_close(int, int, void *);
34static int imx_uart_poll_read(int);
35static int imx_uart_set_attrs(int, const struct termios *);
36static void imx_uart_init(int minor);
37static void imx_uart_set_baud(int, int);
38static ssize_t imx_uart_poll_write(int, const char *, size_t);
39static int imx_uart_poll_read_char(int minor);
40static void imx_uart_poll_write_char(int minor, char c);
41static void _BSP_output_char(char c);
42static int _BSP_poll_char(void);
43
44#if USE_INTERRUPTS
45static void imx_uart_tx_isr(void *);
46static void imx_uart_rx_isr(void *);
47static void imx_uart_isr_on(rtems_vector_number);
48static void imx_uart_isr_off(rtems_vector_number);
49static ssize_t imx_uart_intr_write(int, const char *, size_t);
50static rtems_vector_number imx_uart_name_transmit(int minor);
51static rtems_vector_number imx_uart_name_receive(int minor);
52#endif
53
54/* TERMIOS callbacks */
55#if USE_INTERRUPTS
56rtems_termios_callbacks imx_uart_cbacks = {
57    .firstOpen            = imx_uart_first_open,
58    .lastClose            = imx_uart_last_close,
59    .pollRead             = NULL,
60    .write                = imx_uart_intr_write,
61    .setAttributes        = imx_uart_set_attrs,
62    .stopRemoteTx         = NULL,
63    .startRemoteTx        = NULL,
64    .outputUsesInterrupts = 1,
65};
66#else
67rtems_termios_callbacks imx_uart_cbacks = {
68    .firstOpen            = imx_uart_first_open,
69    .lastClose            = imx_uart_last_close,
70    .pollRead             = imx_uart_poll_read,
71    .write                = imx_uart_poll_write,
72    .setAttributes        = imx_uart_set_attrs,
73    .stopRemoteTx         = NULL,
74    .startRemoteTx        = NULL,
75    .outputUsesInterrupts = 0,
76};
77#endif
78
79typedef struct {
80    int minor;
81    mc9328mxl_uart_regs_t * regs;
82    volatile const char *buf;
83    volatile int len;
84    volatile int idx;
85    void *tty;
86} imx_uart_data_t;
87
88static imx_uart_data_t imx_uart_data[NUM_DEVS];
89
90rtems_device_driver console_initialize(
91    rtems_device_major_number  major,
92    rtems_device_minor_number  minor,
93    void                      *arg
94)
95{
96    rtems_status_code status;
97    int i;
98
99    for (i = 0; i < NUM_DEVS; i++) {
100        imx_uart_init(i);
101    }
102
103    rtems_termios_initialize();
104
105    /* /dev/console and /dev/tty0 are the same */
106    status = rtems_io_register_name("/dev/console", major, 0);
107    if (status != RTEMS_SUCCESSFUL) {
108        rtems_panic("%s:%d Error registering /dev/console :: %d\n",
109                    __FUNCTION__, __LINE__, status);
110    }
111
112    status = rtems_io_register_name("/dev/tty0", major, 0);
113    if (status != RTEMS_SUCCESSFUL) {
114        rtems_panic("%s:%d Error registering /dev/tty0 :: %d\n",
115                    __FUNCTION__, __LINE__, status);
116    }
117
118    status = rtems_io_register_name("/dev/tty1", major, 1);
119    if (status != RTEMS_SUCCESSFUL) {
120        rtems_panic("%s:%d Error registering /dev/tty1 :: %d\n",
121                    __FUNCTION__, __LINE__, status);
122    }
123    return RTEMS_SUCCESSFUL;
124}
125
126rtems_device_driver console_open(
127    rtems_device_major_number major,
128    rtems_device_minor_number minor,
129    void                    * arg
130)
131{
132    rtems_status_code rc;
133
134    if (minor > (NUM_DEVS - 1)) {
135        return RTEMS_INVALID_NUMBER;
136    }
137
138    rc = rtems_termios_open(major, minor, arg, &imx_uart_cbacks);
139
140    return rc;
141}
142
143rtems_device_driver console_close(
144    rtems_device_major_number major,
145    rtems_device_minor_number minor,
146    void                    * arg
147)
148{
149    return rtems_termios_close(arg);
150}
151
152rtems_device_driver console_read(
153    rtems_device_major_number major,
154    rtems_device_minor_number minor,
155    void                    * arg
156)
157{
158  return rtems_termios_read(arg);
159}
160
161rtems_device_driver console_write(
162    rtems_device_major_number major,
163    rtems_device_minor_number minor,
164    void                    * arg
165)
166{
167  return rtems_termios_write(arg);
168}
169
170rtems_device_driver console_control(
171    rtems_device_major_number major,
172    rtems_device_minor_number minor,
173    void                    * arg
174)
175{
176  return rtems_termios_ioctl(arg);
177}
178
179static void imx_uart_init(int minor)
180{
181    imx_uart_data[minor].minor = minor;
182    imx_uart_data[minor].buf   = NULL;
183    imx_uart_data[minor].len   = 0;
184    imx_uart_data[minor].idx   = 0;
185
186    if (minor == 0) {
187        imx_uart_data[minor].regs =
188            (mc9328mxl_uart_regs_t *) MC9328MXL_UART1_BASE;
189    } else if (minor == 1) {
190        imx_uart_data[minor].regs =
191            (mc9328mxl_uart_regs_t *) MC9328MXL_UART2_BASE;
192    } else {
193        rtems_panic("%s:%d Unknown UART minor number %d\n",
194                    __FUNCTION__, __LINE__, minor);
195    }
196
197    imx_uart_data[minor].regs->cr1 = (
198        MC9328MXL_UART_CR1_UARTCLKEN |
199        MC9328MXL_UART_CR1_UARTEN);
200
201    imx_uart_data[minor].regs->cr2 = (
202        MC9328MXL_UART_CR2_IRTS |
203        MC9328MXL_UART_CR2_WS   |
204        MC9328MXL_UART_CR2_TXEN |
205        MC9328MXL_UART_CR2_RXEN |
206        MC9328MXL_UART_CR2_SRST);
207
208    imx_uart_data[minor].regs->cr3 = 0;
209
210    imx_uart_data[minor].regs->cr4 = 0;
211
212    imx_uart_data[minor].regs->fcr = (
213        MC9328MXL_UART_FCR_TXTL(32) |
214        MC9328MXL_UART_FCR_RFDIV_1 |
215        MC9328MXL_UART_FCR_RXTL(1));
216
217    imx_uart_set_baud(minor, 38400);
218
219}
220
221static int imx_uart_first_open(int major, int minor, void *arg)
222{
223    rtems_libio_open_close_args_t *args = arg;
224    rtems_status_code status = RTEMS_SUCCESSFUL;
225
226    imx_uart_data[minor].tty   = args->iop->data1;
227
228#if USE_INTERRUPTS
229    status = rtems_interrupt_handler_install(
230        imx_uart_name_transmit(minor),
231        "UART",
232        RTEMS_INTERRUPT_UNIQUE,
233        imx_uart_tx_isr,
234        &imx_uart_data[minor]
235    );
236    assert(status == RTEMS_SUCCESSFUL);
237    imx_uart_isr_on(imx_uart_name_transmit(minor));
238
239    status = rtems_interrupt_handler_install(
240        imx_uart_name_receive(minor),
241        "UART",
242        RTEMS_INTERRUPT_UNIQUE,
243        imx_uart_rx_isr,
244        &imx_uart_data[minor]
245    );
246    assert(status == RTEMS_SUCCESSFUL);
247    imx_uart_isr_on(imx_uart_name_receive(minor));
248
249    imx_uart_data[minor].regs->cr1 |= MC9328MXL_UART_CR1_RRDYEN;
250#endif
251
252    return 0;
253}
254
255static int imx_uart_last_close(int major, int minor, void *arg)
256{
257#if USE_INTERRUPTS
258    rtems_status_code status = RTEMS_SUCCESSFUL;
259
260    imx_uart_isr_off(imx_uart_name_transmit(minor));
261    status = rtems_interrupt_handler_remove(
262        imx_uart_name_transmit(minor),
263        imx_uart_tx_isr,
264        &imx_uart_data[minor]
265    );
266    assert(status == RTEMS_SUCCESSFUL);
267
268    imx_uart_isr_off(imx_uart_name_receive(minor));
269    status = rtems_interrupt_handler_remove(
270        imx_uart_name_receive(minor),
271        imx_uart_rx_isr,
272        &imx_uart_data[minor]
273    );
274    assert(status == RTEMS_SUCCESSFUL);
275#endif
276
277    return 0;
278}
279
280static int imx_uart_poll_read(int minor)
281{
282    if (imx_uart_data[minor].regs->sr2 & MC9328MXL_UART_SR2_RDR) {
283        return imx_uart_data[minor].regs->rxd & 0xff;
284    } else {
285        return -1;
286    }
287}
288
289
290static ssize_t imx_uart_poll_write(int minor, const char *buf, size_t len)
291{
292    int i;
293    for (i = 0; i < len; i++) {
294        /* Wait for there to be room in the fifo */
295        while (!(imx_uart_data[minor].regs->sr2 & MC9328MXL_UART_SR2_TXDC)) {
296            continue;
297        }
298
299        imx_uart_data[minor].regs->txd = buf[i];
300    }
301    return 1;
302
303}
304
305#if USE_INTERRUPTS
306static ssize_t imx_uart_intr_write(int minor, const char *buf, size_t len)
307{
308    if (len > 0) {
309        imx_uart_data[minor].buf = buf;
310        imx_uart_data[minor].len = len;
311        imx_uart_data[minor].idx = 0;
312
313        imx_uart_data[minor].regs->cr1 |= MC9328MXL_UART_CR1_TXMPTYEN;
314    }
315
316    return 1;
317}
318#endif
319
320
321/* This is for setting baud rate, bits, etc. */
322static int imx_uart_set_attrs(int minor, const struct termios *t)
323{
324    int baud;
325
326    baud = rtems_termios_baud_to_number(t->c_ospeed);
327    imx_uart_set_baud(minor, baud);
328
329    return 0;
330}
331
332#if USE_INTERRUPTS
333static void imx_uart_isr_on(rtems_vector_number name)
334{
335    MC9328MXL_AITC_INTENNUM = name;
336}
337static void imx_uart_isr_off(rtems_vector_number name)
338{
339    MC9328MXL_AITC_INTDISNUM = name;
340}
341
342static void imx_uart_rx_isr(void * param)
343{
344    imx_uart_data_t *uart_data = param;
345    char buf[32];
346    int i=0;
347
348    while (uart_data->regs->sr2 & MC9328MXL_UART_SR2_RDR) {
349        buf[i] = uart_data->regs->rxd & 0xff;
350        i++;
351    }
352
353    rtems_termios_enqueue_raw_characters(uart_data->tty, buf, i);
354}
355
356static void imx_uart_tx_isr(void * param)
357{
358    imx_uart_data_t *uart_data = param;
359    int len;
360    int minor = uart_data->minor;
361
362
363    if (uart_data->idx < uart_data->len) {
364        while ( (uart_data->regs->sr1 & MC9328MXL_UART_SR1_TRDY) &&
365                (uart_data->idx < uart_data->len)) {
366            uart_data->regs->txd = uart_data->buf[uart_data->idx];
367            uart_data->idx++;
368        }
369    } else {
370        len = uart_data->len;
371        uart_data->len = 0;
372        imx_uart_data[minor].regs->cr1 &= ~MC9328MXL_UART_CR1_TXMPTYEN;
373        rtems_termios_dequeue_characters(uart_data->tty, len);
374    }
375}
376
377static rtems_vector_number imx_uart_name_transmit(int minor)
378{
379    if (minor == 0) {
380        return BSP_INT_UART1_TX;
381    } else if (minor == 1) {
382        return BSP_INT_UART2_TX;
383    }
384
385    assert(0);
386}
387
388static rtems_vector_number imx_uart_name_receive(int minor)
389{
390    if (minor == 0) {
391        return BSP_INT_UART1_RX;
392    } else if (minor == 1) {
393        return BSP_INT_UART2_RX;
394    }
395
396    assert(0);
397}
398#endif
399
400/*
401 * Set the UART's baud rate. The calculation is:
402 *   (baud * 16) / ref_freq  = num/demom
403 *
404 *   ref_freq = perclk1 / RFDIV[2:0]
405 *   BIR = num - 1
406 *   BMR = demom - 1
407 *
408 * Setting 'num' to 16 yields this equation:
409 *    demom = ref_freq / baud
410 */
411static void imx_uart_set_baud(int minor, int baud)
412{
413    unsigned int perclk1;
414    unsigned int denom;
415    unsigned int ref_freq = 0;
416    uint32_t fcr;
417
418    perclk1 = get_perclk1_freq();
419    fcr = imx_uart_data[minor].regs->fcr;
420
421    switch(fcr & MC9328MXL_UART_FCR_RFDIV_MASK) {
422    case MC9328MXL_UART_FCR_RFDIV_1:  ref_freq = perclk1/1; break;
423    case MC9328MXL_UART_FCR_RFDIV_2:  ref_freq = perclk1/2; break;
424    case MC9328MXL_UART_FCR_RFDIV_3:  ref_freq = perclk1/3; break;
425    case MC9328MXL_UART_FCR_RFDIV_4:  ref_freq = perclk1/4; break;
426    case MC9328MXL_UART_FCR_RFDIV_5:  ref_freq = perclk1/5; break;
427    case MC9328MXL_UART_FCR_RFDIV_6:  ref_freq = perclk1/6; break;
428    case MC9328MXL_UART_FCR_RFDIV_7:  ref_freq = perclk1/7; break;
429    default:
430        rtems_panic("%s:%d Unknown RFDIV: 0x%" PRIx32,
431                    __FUNCTION__, __LINE__,
432                    fcr & MC9328MXL_UART_FCR_RFDIV_MASK);
433        break;
434    }
435
436    denom = ref_freq / baud;
437
438    imx_uart_data[minor].regs->bir = 0xf;
439    imx_uart_data[minor].regs->bmr = denom;
440}
441
442
443/*
444 * Polled, non-blocking read from UART
445 */
446static int imx_uart_poll_read_char(int minor)
447{
448    return imx_uart_poll_read(minor);
449}
450
451/*
452 * Polled, blocking write from UART
453 */
454static void  imx_uart_poll_write_char(int minor, char c)
455{
456    imx_uart_poll_write(minor, &c, 1);
457}
458
459/*
460 * Functions for printk() and friends.
461 */
462static void _BSP_output_char(char c)
463{
464    poll_write(c);
465}
466
467BSP_output_char_function_type BSP_output_char = _BSP_output_char;
468
469static int _BSP_poll_char(void)
470{
471    return poll_read();
472}
473
474BSP_polling_getchar_function_type BSP_poll_char = _BSP_poll_char;
475
476
Note: See TracBrowser for help on using the repository browser.