source: rtems/c/src/lib/libbsp/arm/csb336/console/uart.c @ 0afac6a

4.115
Last change on this file since 0afac6a was c499856, checked in by Chris Johns <chrisj@…>, on 03/20/14 at 21:10:47

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

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