source: rtems/c/src/lib/libbsp/sparc/shared/uart/apbuart.c @ 4d3e70f4

4.115
Last change on this file since 4d3e70f4 was 5823bae8, checked in by Daniel Hellstrom <daniel@…>, on 02/27/15 at 13:03:15

LEON: move driver headers to bsp/ directory

  • Property mode set to 100644
File size: 22.8 KB
Line 
1/*
2 *  This file contains the driver for the APBUART serial port.
3 *  No console driver, only char driver.
4 *
5 *  COPYRIGHT (c) 2007.
6 *  Gaisler Research.
7 *
8 *  The license and distribution terms for this file may be
9 *  found in the file LICENSE in this distribution or at
10 *  http://www.rtems.org/license/LICENSE.
11 *
12 *
13 *  2007-07-11, Daniel Hellstrom <daniel@gaisler.com>
14 *    Added ioctl command APBUART_CLR_STATS
15 */
16
17#include <bsp.h>
18#include <rtems/libio.h>
19#include <stdlib.h>
20#include <assert.h>
21#include <rtems/bspIo.h>
22#include <string.h>
23
24#include <ambapp.h>
25#include <grlib.h>
26#include <bsp/apbuart.h>
27
28#ifndef DEFAULT_TXBUF_SIZE
29 #define DEFAULT_TXBUF_SIZE 32
30#endif
31#ifndef DEFAULT_RXBUF_SIZE
32 #define DEFAULT_RXBUF_SIZE 32
33#endif
34
35#ifndef APBUART_PREFIX
36 #define APBUART_PREFIX(name) apbuart##name
37#else
38 #define APBUART_REGISTER_STATIC
39#endif
40
41#if !defined(APBUART_DEVNAME) || !defined(APBUART_DEVNAME_NO)
42 #undef APBUART_DEVNAME
43 #undef APBUART_DEVNAME_NO
44 #define APBUART_DEVNAME "/dev/apbuart0"
45 #define APBUART_DEVNAME_NO(devstr,no) ((devstr)[12]='0'+(no))
46#endif
47
48#ifndef APBUART_REG_INT
49        #define APBUART_REG_INT(handler,irq,arg) set_vector(handler,irq+0x10,1)
50  #undef APBUART_DEFINE_INTHANDLER
51  #define APBUART_DEFINE_INTHANDLER
52#endif
53
54/* Default to 40MHz system clock */
55/*#ifndef SYS_FREQ_HZ
56 #define SYS_FREQ_HZ 40000000
57#endif*/
58
59typedef struct {
60        int size;
61        unsigned char *buf,
62                      *tail,
63                            *head,
64                                                                *max;
65        int full; /* no more place in fifo */
66} apbuart_fifo;
67
68static apbuart_fifo *apbuart_fifo_create(int size);
69static void apbuart_fifo_free(apbuart_fifo *fifo);
70static inline int apbuart_fifo_isFull(apbuart_fifo *fifo);
71static inline int apbuart_fifo_isEmpty(apbuart_fifo *fifo);
72static int apbuart_fifo_put(apbuart_fifo *fifo, unsigned char c);
73static int apbuart_fifo_get(apbuart_fifo *fifo, unsigned char *c);
74static int inline apbuart_fifo_peek(apbuart_fifo *fifo, unsigned char **c);
75static void inline apbuart_fifo_skip(apbuart_fifo *fifo);
76
77static rtems_device_driver apbuart_initialize(rtems_device_major_number  major, rtems_device_minor_number  minor,  void *arg);
78static rtems_device_driver apbuart_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
79static rtems_device_driver apbuart_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
80static rtems_device_driver apbuart_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
81static rtems_device_driver apbuart_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
82static rtems_device_driver apbuart_control(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
83
84typedef struct {
85        struct apbuart_regs *regs;
86        int irq;
87        int minor;
88        int scaler;
89        unsigned int baud;
90
91        int txblk;    /* Make write block until at least 1 char has
92                       * been put into software send fifo
93                                                                 */
94        int tx_flush; /* Set this to block until all data has
95                       * placed into the hardware send fifo
96                                                                 */
97        int rxblk;    /* Make read block until at least 1 char has
98                       * been received (or taken from software fifo).
99                                                                 */
100        int started;  /* Set to 1 when in running mode */
101
102        int ascii_mode; /* Set to 1 to make \n be printed as \r\n */
103
104        /* TX/RX software FIFO Buffers */
105        apbuart_fifo *txfifo;
106        apbuart_fifo *rxfifo;
107
108        apbuart_stats stats;
109
110        rtems_id dev_sem;
111        rtems_id rx_sem;
112        rtems_id tx_sem;
113} apbuart_priv;
114
115static int dev_cnt;
116static apbuart_priv *apbuarts;
117static unsigned int sys_freq_hz;
118
119#define APBUART_DRIVER_TABLE_ENTRY { apbuart_initialize, apbuart_open, apbuart_close, apbuart_read, apbuart_write, apbuart_control }
120
121static rtems_driver_address_table apbuart_driver = APBUART_DRIVER_TABLE_ENTRY;
122static struct ambapp_bus *amba_bus;
123
124static void apbuart_interrupt(apbuart_priv *uart);
125#ifdef APBUART_DEFINE_INTHANDLER
126static void apbuart_interrupt_handler(rtems_vector_number v);
127#endif
128static void apbuart_hw_close(apbuart_priv *uart);
129static void apbuart_hw_open(apbuart_priv *uart);
130
131/* Uncomment for debug output */
132/* #define DEBUG 1
133 #define FUNCDEBUG 1 */
134
135#ifdef DEBUG
136#define DBG(x...) printk(x)
137#else
138#define DBG(x...)
139#endif
140#ifdef FUNCDEBUG
141#define FUNCDBG(x...) printk(x)
142#else
143#define FUNCDBG(x...)
144#endif
145
146#ifndef READ_REG
147        #define READ_REG(address) _APBUART_READ_REG((unsigned int)(address))
148  static __inline__ unsigned int _APBUART_READ_REG(unsigned int addr) {
149        unsigned int tmp;
150        __asm__ (" lda [%1]1, %0 "
151            : "=r"(tmp)
152            : "r"(addr)
153           );
154        return tmp;
155        }
156#endif
157
158#if 0
159static int apbuart_outbyte_try(struct apbuart_regs *regs, unsigned char ch)
160{
161        if ( (READ_REG(&regs->status) & APBUART_STATUS_TE) == 0 )
162                return -1; /* Failed */
163
164        /* There is room in fifo, put ch in it */
165        regs->data = (unsigned int) ch;
166        return 0;
167}
168
169
170static int apbuart_inbyte_try(struct apbuart_regs *regs)
171{
172        unsigned int status;
173        /* Clear errors if any */
174        if ( (status=READ_REG(&regs->status)) & APBUART_STATUS_ERR) {
175                regs->status = status & ~APBUART_STATUS_ERR;
176        }
177
178        /* Is Data available? */
179        if ( (READ_REG(&regs->status) & APBUART_STATUS_DR) == 0 )
180                return -1; /* No data avail */
181
182        /* Return Data */
183        return (int)READ_REG(&regs->data);
184}
185
186static int apbuart_write_support(apbuart_priv *uart, const char *buf, int len)
187{
188        int nwrite = 0;
189
190        while (nwrite < len) {
191                if ( apbuart_outbyte_try(minor, *buf++) ){
192                        /* TX Fifo full */
193
194                }
195                nwrite++;
196        }
197        return nwrite;
198}
199#endif
200
201static void apbuart_hw_open(apbuart_priv *uart){
202        unsigned int scaler;
203
204        /* Calculate Baudrate */
205        if ( uart->scaler > 0 ) {
206                scaler = uart->scaler;
207        }else{
208                scaler = (((sys_freq_hz*10)/(uart->baud*8))-5)/10;
209        }
210
211        /* Set new baud rate */
212        uart->regs->scaler = scaler;
213
214        /* Enable receiver & Transmitter */
215        uart->regs->ctrl = APBUART_CTRL_RE | APBUART_CTRL_RF | APBUART_CTRL_RI | APBUART_CTRL_TI;
216}
217
218static void apbuart_hw_close(apbuart_priv *uart){
219        /* disable receiver & transmitter & all IRQs */
220        uart->regs->ctrl = 0;
221}
222
223#ifdef APBUART_DEFINE_INTHANDLER
224/* interrupt handler */
225static void apbuart_interrupt_handler(rtems_vector_number v){
226        int minor;
227
228        /* convert to */
229  for(minor = 0; minor < dev_cnt; minor++) {
230        if ( v == (apbuarts[minor].irq+0x10) ) {
231                        apbuart_interrupt(&apbuarts[minor]);
232                        return;
233                }
234        }
235}
236#endif
237
238/* The interrupt handler, taking care of the
239 * APBUART hardware
240 */
241static void apbuart_interrupt(apbuart_priv *uart){
242        unsigned int status;
243        int empty;
244        unsigned char c, *next_char = NULL;
245        int signal;
246
247        /* Clear & record any error */
248        status = READ_REG(&uart->regs->status);
249        if ( status & (APBUART_STATUS_OV|APBUART_STATUS_PE|APBUART_STATUS_FE) ){
250                /* Data overrun */
251                if ( status & APBUART_STATUS_OV ){
252                        uart->stats.hw_dovr++;
253                }
254                /* Parity error */
255                if ( status & APBUART_STATUS_PE ){
256                        uart->stats.hw_parity++;
257                }
258                /* Framing error */
259                if ( status & APBUART_STATUS_FE ){
260                        uart->stats.hw_frame++;
261                }
262                uart->regs->status = status & ~(APBUART_STATUS_OV|APBUART_STATUS_PE|APBUART_STATUS_FE);
263        }
264
265        /* Empty RX fifo into software fifo */
266        signal = 0;
267        while ( (status=READ_REG(&uart->regs->status)) & APBUART_STATUS_DR ){
268                c = READ_REG(&uart->regs->data);
269                if ( apbuart_fifo_isFull(uart->rxfifo) ){
270                        uart->stats.sw_dovr++;
271      DBG("]");
272                        break;
273                }
274                /* put into fifo */
275                apbuart_fifo_put(uart->rxfifo,c);
276
277                /* bump RX counter */
278                uart->stats.rx_cnt++;
279
280                signal = 1;
281        }
282
283        /* Wake RX thread if any */
284        if ( signal )
285                rtems_semaphore_release(uart->rx_sem);
286
287        /* If room in HW fifo and we got more chars to be sent */
288        if ( !(status & APBUART_STATUS_TF) ){
289
290                if ( apbuart_fifo_isEmpty(uart->txfifo) ){
291                        /* Turn off TX interrupt when no data is to be sent */
292                        if ( status & APBUART_STATUS_TE ){
293                                uart->regs->ctrl = READ_REG(&uart->regs->ctrl) & ~APBUART_CTRL_TF;
294                                DBG("?");
295                        }
296                        return;
297                }
298
299                /* signal when there will be more room in SW fifo */
300                if ( apbuart_fifo_isFull(uart->txfifo) )
301                        signal = 1;
302
303                do{
304                        /* Put data into HW TX fifo */
305                        apbuart_fifo_peek(uart->txfifo,&next_char);
306                        c = *next_char;
307                        if ( uart->ascii_mode && ( c == '\n') ){
308                                uart->regs->data = '\n';
309                                *next_char = '\r'; /* avoid sending mutiple '\n' or '\r' */
310                        }else{
311                                uart->regs->data = c;
312                                apbuart_fifo_skip(uart->txfifo); /* remove sent char from fifo */
313                        }
314                        uart->regs->ctrl = READ_REG(&uart->regs->ctrl) | APBUART_CTRL_TE | APBUART_CTRL_TF;
315                        DBG("!");
316                }while(!(empty=apbuart_fifo_isEmpty(uart->txfifo)) &&
317                       !((status=READ_REG(&uart->regs->status))&APBUART_STATUS_TF) );
318
319                /* Wake userspace thread, on empty or full fifo
320                 * This makes tx_flush and block work.
321                 */
322                if ( signal || empty ){
323                        rtems_semaphore_release(uart->tx_sem);
324                }
325        }
326}
327
328#ifdef APBUART_REGISTER_STATIC
329static
330#endif
331int APBUART_PREFIX(_register)(struct ambapp_bus *bus) {
332        rtems_status_code r;
333        rtems_device_major_number m;
334
335        amba_bus = bus;
336
337        FUNCDBG("apbuart_register:\n");
338
339        if ((r = rtems_io_register_driver(0, &apbuart_driver, &m)) == RTEMS_SUCCESSFUL) {
340                DBG("APBUART driver successfully registered, major: %d\n", m);
341        } else {
342                switch(r) {
343                case RTEMS_TOO_MANY:
344                        printk("APBUART rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); return -1;
345                case RTEMS_INVALID_NUMBER:
346                        printk("APBUART rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); return -1;
347                case RTEMS_RESOURCE_IN_USE:
348                        printk("APBUART rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); return -1;
349                default:
350                   printk("APBUART rtems_io_register_driver failed\n");
351                         return -1;
352                }
353        }
354        return 0;
355}
356
357static rtems_device_driver apbuart_initialize(rtems_device_major_number  major, rtems_device_minor_number  minor,  void *arg)
358{
359
360        rtems_status_code status;
361        int i;
362        struct ambapp_apb_info dev;
363        char fs_name[20];
364
365        FUNCDBG("apbuart_initialize\n");
366
367        /* Find all APB UART devices */
368        dev_cnt = ambapp_get_number_apbslv_devices(amba_bus, VENDOR_GAISLER,
369                                                   GAISLER_APBUART);
370        if ( dev_cnt < 1 ){
371                /* Failed to find any CAN cores! */
372                printk("APBUART: Failed to find any APBUART cores\n\r");
373                return -1;
374        }
375
376        strcpy(fs_name,APBUART_DEVNAME);
377
378        DBG("Found %d APBUART(s)\n\r",dev_cnt);
379
380        /* Allocate memory for device structures */
381        apbuarts = calloc(dev_cnt, sizeof(*apbuarts));
382        if ( !apbuarts ){
383                printk("APBUART: Failed to allocate SW memory\n\r");
384                return -1;
385        }
386
387        /* Detect System Frequency from initialized timer */
388#ifndef SYS_FREQ_HZ
389#if defined(LEON3)
390        /* LEON3: find timer address via AMBA Plug&Play info */
391        {
392                struct ambapp_apb_info gptimer;
393                struct gptimer_regs *tregs;
394
395                if ( ambapp_find_apbslv(&ambapp_plb, VENDOR_GAISLER,
396                                        GAISLER_GPTIMER, &gptimer) == 1 ){
397                        tregs = (struct gptimer_regs *)gptimer.start;
398                        sys_freq_hz = (tregs->scaler_reload+1)*1000*1000;
399                        DBG("APBUART: detected %dHZ system frequency\n\r",sys_freq_hz);
400                }else{
401                        sys_freq_hz = 40000000; /* Default to 40MHz */
402                        printk("APBUART: Failed to detect system frequency\n\r");
403                }
404
405        }
406#elif defined(LEON2)
407        /* LEON2: use hardcoded address to get to timer */
408        {
409                LEON_Register_Map *regs = (LEON_Register_Map *)0x80000000;
410                sys_freq_hz = (regs->Scaler_Reload+1)*1000*1000;
411        }
412#else
413  #error CPU not supported for OC_CAN driver
414#endif
415#else
416        /* Use hardcoded frequency */
417        sys_freq_hz = SYS_FREQ_HZ;
418#endif
419
420        for(i=0; i<dev_cnt; i++){
421                /* Get AMBA AHB device info from Plug&Play */
422                ambapp_find_apbslv_next(amba_bus,VENDOR_GAISLER,GAISLER_APBUART,&dev,i);
423
424                printk("APBUART[%d]: at 0x%x irq %d (0x%x)\n\r",i,dev.start,dev.irq,(unsigned int)&apbuarts[i]);
425
426                apbuarts[i].regs = (struct apbuart_regs *)dev.start;
427                apbuarts[i].irq = dev.irq;
428                apbuarts[i].minor = i;
429
430                /* Clear HW regs */
431                apbuarts[i].regs->status = 0;
432                apbuarts[i].regs->ctrl = 0;
433
434                /* Allocate default software buffers */
435                apbuarts[i].txfifo = apbuart_fifo_create(DEFAULT_TXBUF_SIZE);
436                apbuarts[i].rxfifo = apbuart_fifo_create(DEFAULT_RXBUF_SIZE);
437                if ( !apbuarts[i].txfifo || !apbuarts[i].rxfifo )
438                        rtems_fatal_error_occurred(RTEMS_NO_MEMORY);
439
440                APBUART_DEVNAME_NO(fs_name,i);
441
442                /* Bind name to device */
443                DBG("APBUART[%d]: binding to name %s\n\r",i,fs_name);
444                status = rtems_io_register_name(fs_name, major, i);
445                if (status != RTEMS_SUCCESSFUL)
446                        rtems_fatal_error_occurred(status);
447
448                /* Setup interrupt handler for each channel */
449        APBUART_REG_INT(APBUART_PREFIX(_interrupt_handler), apbuarts[i].irq, &apbuarts[i]);
450
451                /* Device A Semaphore created with count = 1 */
452                if ( rtems_semaphore_create(rtems_build_name('A', 'U', 'D', '0'+i),
453                     1,
454                     RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING,
455                     0,
456                     &apbuarts[i].dev_sem) != RTEMS_SUCCESSFUL )
457                        return RTEMS_INTERNAL_ERROR;
458
459                if ( rtems_semaphore_create(rtems_build_name('A', 'U', 'T', '0'+i),
460                     1,
461                     RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING,
462                     0,
463                     &apbuarts[i].tx_sem) != RTEMS_SUCCESSFUL )
464                        return RTEMS_INTERNAL_ERROR;
465
466                if ( rtems_semaphore_create(rtems_build_name('A', 'U', 'R', '0'+i),
467                     1,
468                     RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING,
469                     0,
470                     &apbuarts[i].rx_sem) != RTEMS_SUCCESSFUL )
471                        return RTEMS_INTERNAL_ERROR;
472
473        }
474        return RTEMS_SUCCESSFUL;
475}
476
477static rtems_device_driver apbuart_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
478{
479        apbuart_priv *uart;
480
481        FUNCDBG("apbuart_open: major %d, minor %d\n", major, minor);
482
483        if ( (minor < 0) || (minor >= dev_cnt) ) {
484                DBG("Wrong minor %d\n", minor);
485                return RTEMS_INVALID_NAME;
486        }
487
488        uart = &apbuarts[minor];
489
490        if (rtems_semaphore_obtain(uart->dev_sem, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL) {
491                DBG("apbuart_open: resource in use\n");
492                return RTEMS_RESOURCE_IN_USE;
493        }
494
495        /* Clear HW regs */
496        uart->regs->status = 0;
497        uart->regs->ctrl = 0;
498
499        /* Set Defaults */
500
501        /* 38400 baudrate */
502        uart->scaler = 0; /* use uart->baud */
503        uart->baud = 38400;
504
505        /* Default to Blocking mode */
506        uart->txblk = 1;
507        uart->rxblk = 1;
508
509        /* Default to no flush mode */
510        uart->tx_flush = 0;
511
512        /* non-ascii mode */
513        uart->ascii_mode = 0;
514
515  /* not started */
516  uart->started = 0;
517
518        if ( !uart->txfifo || (uart->txfifo->size!=DEFAULT_TXBUF_SIZE) ){
519                apbuart_fifo_free(uart->txfifo);
520                uart->txfifo = apbuart_fifo_create(DEFAULT_TXBUF_SIZE);
521        }
522
523        if ( !uart->rxfifo || (uart->rxfifo->size!=DEFAULT_RXBUF_SIZE) ){
524                apbuart_fifo_free(uart->rxfifo);
525                uart->rxfifo = apbuart_fifo_create(DEFAULT_RXBUF_SIZE);
526        }
527
528        if ( !uart->rxfifo || !uart->txfifo ){
529                /* Failed to get memory */
530                return RTEMS_NO_MEMORY;
531        }
532
533        /* Now user must call ioctl(START,0) to begin */
534
535        return RTEMS_SUCCESSFUL;
536}
537
538static rtems_device_driver apbuart_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
539{
540        apbuart_priv *uart = &apbuarts[minor];
541
542        FUNCDBG("apbuart_close[%d]:\n",minor);
543
544        apbuart_hw_close(uart);
545
546        /* Software state will be set when open is called again */
547        rtems_semaphore_release(uart->rx_sem);
548        rtems_semaphore_release(uart->tx_sem);
549        uart->started = 0;
550
551        rtems_semaphore_release(uart->dev_sem);
552
553        return RTEMS_SUCCESSFUL;
554}
555
556static rtems_device_driver apbuart_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
557{
558        rtems_libio_rw_args_t *rw_args;
559        unsigned int count = 0, oldLevel;
560        unsigned char *buf;
561        apbuart_priv *uart = &apbuarts[minor];
562
563        rw_args = (rtems_libio_rw_args_t *) arg;
564
565        FUNCDBG("apbuart_read\n");
566
567        buf = (unsigned char *)rw_args->buffer;
568        if ( (rw_args->count < 1) || !buf )
569                return RTEMS_INVALID_NAME; /* EINVAL */
570
571        rtems_interrupt_disable(oldLevel);
572        do {
573                if ( (unsigned int)uart < 0x40000000 ) {
574      printk("UART %x is screwed\n",uart);
575    }
576                /* Read from SW fifo */
577                if ( apbuart_fifo_get(uart->rxfifo,&buf[count]) != 0 ){
578                        /* non blocking or read at least 1 byte */
579                        if ( (count > 0) || (!uart->rxblk) )
580                                break; /* Return */
581
582                        rtems_interrupt_enable(oldLevel);
583
584                        /* Block thread until a char is received */
585                        rtems_semaphore_obtain(uart->rx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
586
587                        rtems_interrupt_disable(oldLevel);
588                        continue;
589                }
590
591                /* Got char from SW FIFO */
592                count++;
593
594        } while (count < rw_args->count );
595
596        rtems_interrupt_enable(oldLevel);
597
598        rw_args->bytes_moved = count;
599
600        if (count == 0)
601                return RTEMS_TIMEOUT; /* ETIMEDOUT should be EAGAIN/EWOULDBLOCK */
602
603        return RTEMS_SUCCESSFUL;
604}
605
606static rtems_device_driver apbuart_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
607{
608        rtems_libio_rw_args_t *rw_args;
609        unsigned int count, oldLevel, ctrl;
610        char *buf;
611        apbuart_priv *uart = &apbuarts[minor];
612        int direct=0;
613
614
615        rw_args = (rtems_libio_rw_args_t *) arg;
616
617        FUNCDBG("apbuart_write\n");
618
619        buf = rw_args->buffer;
620
621        if ( rw_args->count < 1 || !buf )
622                return RTEMS_INVALID_NAME; /* EINVAL */
623
624        count = 0;
625        rtems_interrupt_disable(oldLevel);
626        /* Do we need to start to send first char direct via HW
627         * to get IRQ going.
628         */
629
630        ctrl = READ_REG(&uart->regs->ctrl);
631        if ( (ctrl & APBUART_CTRL_TF) == 0 ){
632                /* TX interrupt is disabled ==>
633                 * SW FIFO is empty and,
634                 * HW FIFO empty
635                 */
636                uart->regs->ctrl = ctrl | APBUART_CTRL_TF;
637                if ( uart->ascii_mode && (buf[0] == '\n') ){
638                        uart->regs->data = '\r';
639                }else{
640                        uart->regs->data = buf[0];
641                        count++;
642                }
643                uart->regs->ctrl = ctrl | APBUART_CTRL_TE | APBUART_CTRL_TF;
644                direct = 1;
645        }
646
647        while( count < rw_args->count ) {
648                /* write to HW FIFO direct skipping SW FIFO */
649                if ( direct && ((READ_REG(&uart->regs->status) & APBUART_STATUS_TF) == 0) ){
650                        uart->regs->data = buf[count];
651                }
652                /* write to SW FIFO */
653                else if ( apbuart_fifo_put(uart->txfifo,buf[count]) ){
654                        direct = 0;
655                        DBG("APBUART[%d]: write: SW FIFO Full\n\r",minor);
656
657                        /* is full, block? */
658                        if ( ((count < 1) && uart->txblk) || uart->tx_flush ){
659
660                                rtems_interrupt_enable(oldLevel);
661
662                                rtems_semaphore_obtain(uart->tx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
663
664                                rtems_interrupt_disable(oldLevel);
665
666                                /* Do we need to start to send first char direct via HW
667                                 * to get IRQ going.
668                                 */
669
670                                ctrl = READ_REG(&uart->regs->ctrl);
671                                if ( (ctrl & APBUART_CTRL_TF) == 0 ){
672                                        /* TX interrupt is disabled ==>
673                                         * SW FIFO is empty and,
674                                         * HW FIFO empty
675                                         */
676                                        uart->regs->ctrl = ctrl | APBUART_CTRL_TF;
677                                        if ( uart->ascii_mode && (buf[count] == '\n') ){
678                                                uart->regs->data = '\r';
679                                        }else{
680                                                uart->regs->data = buf[count];
681                                                count++;
682                                        }
683                                        uart->regs->ctrl = ctrl | APBUART_CTRL_TF | APBUART_CTRL_TE;
684                                        direct = 1;
685                                }
686
687                                continue;
688                        }
689                        /* don't block, return current status */
690                        break;
691                }else{
692                        direct = 0;
693                }
694
695                count++;
696
697        }
698
699        rtems_interrupt_enable(oldLevel);
700
701        rw_args->bytes_moved = count;
702
703        if (count == 0)
704                return RTEMS_TIMEOUT; /* ETIMEDOUT should be EAGAIN/EWOULDBLOCK */
705
706        return RTEMS_SUCCESSFUL;
707}
708
709static rtems_device_driver apbuart_control(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
710{
711        rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *)arg;
712        unsigned int *data = ioarg->buffer;
713        apbuart_priv *uart = &apbuarts[minor];
714        int size;
715        unsigned int baudrate, blocking;
716        apbuart_stats *stats;
717
718        FUNCDBG("apbuart_control [%i,%i]\n",major, minor);
719
720        if (!ioarg)
721                return RTEMS_INVALID_NAME;
722
723  ioarg->ioctl_return = 0;
724        switch(ioarg->command) {
725
726        /* Enable Receiver & transmitter */
727        case APBUART_START:
728                if ( uart->started )
729                        return RTEMS_INVALID_NAME;
730                apbuart_hw_open(uart);
731                uart->started = 1;
732                break;
733
734        /* Close Receiver & transmitter */
735        case APBUART_STOP:
736                if ( !uart->started )
737                        return RTEMS_INVALID_NAME;
738                apbuart_hw_close(uart);
739                uart->started = 0;
740                break;
741
742        /* Set RX FIFO Software buffer length
743         * It is only possible to change buffer size in
744         * non-running mode.
745         */
746        case APBUART_SET_RXFIFO_LEN:
747                if ( uart->started )
748                        return RTEMS_RESOURCE_IN_USE; /* EBUSY */
749
750                size = (int)ioarg->buffer;
751                if ( size < 1 )
752                        return RTEMS_INVALID_NAME; /* EINVAL */
753
754                /* Free old buffer */
755                apbuart_fifo_free(uart->rxfifo);
756
757                /* Allocate new buffer & init it */
758                uart->rxfifo = apbuart_fifo_create(size);
759                if ( !uart->rxfifo )
760                        return RTEMS_NO_MEMORY;
761                break;
762
763        /* Set TX FIFO Software buffer length
764         * It is only possible to change buffer size
765         * while in non-running mode.
766         */
767        case APBUART_SET_TXFIFO_LEN:
768                if ( uart->started )
769                        return RTEMS_RESOURCE_IN_USE; /* EBUSY */
770
771                size = (int)ioarg->buffer;
772                if ( size < 1 )
773                        return RTEMS_INVALID_NAME; /* EINVAL */
774
775                /* Free old buffer */
776                apbuart_fifo_free(uart->txfifo);
777
778                /* Allocate new buffer & init it */
779                uart->txfifo = apbuart_fifo_create(size);
780                if ( !uart->txfifo )
781                        return RTEMS_NO_MEMORY;
782                break;
783
784        case APBUART_SET_BAUDRATE:
785                /* Set baud rate of */
786                baudrate = (int)ioarg->buffer;
787                if ( (baudrate < 1) || (baudrate > 115200) ){
788                        return RTEMS_INVALID_NAME;
789                }
790                uart->scaler = 0; /* use uart->baud */
791                uart->baud = baudrate;
792                break;
793
794        case APBUART_SET_SCALER:
795                /* use uart->scaler not uart->baud */
796                uart->scaler = data[0];
797                break;
798
799        case APBUART_SET_BLOCKING:
800                blocking = (unsigned int)ioarg->buffer;
801                uart->rxblk = ( blocking & APBUART_BLK_RX );
802                uart->txblk = ( blocking & APBUART_BLK_TX );
803                uart->tx_flush = ( blocking & APBUART_BLK_FLUSH );
804                break;
805
806        case APBUART_GET_STATS:
807                stats = (void *)ioarg->buffer;
808                if ( !stats )
809                        return RTEMS_INVALID_NAME;
810
811                /* Copy Stats */
812                *stats = uart->stats;
813                break;
814
815  case APBUART_CLR_STATS:
816                /* Clear/reset Stats */
817    memset(&uart->stats,0,sizeof(uart->stats));
818                break;
819
820        case APBUART_SET_ASCII_MODE:
821                uart->ascii_mode = (int)ioarg->buffer;
822                break;
823
824        default:
825                return RTEMS_NOT_DEFINED;
826        }
827        return RTEMS_SUCCESSFUL;
828}
829
830
831/******************* APBUART FIFO implementation ***********************/
832
833static apbuart_fifo *apbuart_fifo_create(int size){
834        apbuart_fifo *fifo;
835        fifo = (apbuart_fifo *) malloc(size + sizeof(apbuart_fifo));
836        if ( fifo ) {
837                /* Init fifo */
838                fifo->size = size;
839                fifo->buf = (unsigned char *)(fifo+1);
840                fifo->tail = fifo->buf;
841                fifo->head = fifo->buf;
842                fifo->max = &fifo->buf[size-1];
843                fifo->full=0;
844        }
845        return fifo;
846}
847
848static void apbuart_fifo_free(apbuart_fifo *fifo){
849        if ( fifo )
850                free(fifo);
851}
852
853static inline int apbuart_fifo_isFull(apbuart_fifo *fifo){
854        return fifo->full;
855}
856
857static inline int apbuart_fifo_isEmpty(apbuart_fifo *fifo){
858        if ( (fifo->head == fifo->tail) && !fifo->full )
859                return -1;
860        return 0;
861}
862
863static int apbuart_fifo_put(apbuart_fifo *fifo, unsigned char c){
864        if ( !fifo->full ){
865                *fifo->head = c;
866                fifo->head = (fifo->head >= fifo->max ) ? fifo->buf : fifo->head+1;
867                if ( fifo->head == fifo->tail )
868                        fifo->full = -1;
869                return 0;
870        }
871        return -1;
872}
873
874static int apbuart_fifo_get(apbuart_fifo *fifo, unsigned char *c){
875        if ( apbuart_fifo_isEmpty(fifo) )
876                return -1;
877        if ( c )
878                *c = *fifo->tail;
879        fifo->tail = (fifo->tail >= fifo->max ) ? fifo->buf : fifo->tail+1;
880        fifo->full = 0;
881        return 0;
882}
883
884static int inline apbuart_fifo_peek(apbuart_fifo *fifo, unsigned char **c){
885        if ( apbuart_fifo_isEmpty(fifo) )
886                return -1;
887        if ( c )
888                *c = fifo->tail;
889        return 0;
890}
891
892static void inline apbuart_fifo_skip(apbuart_fifo *fifo){
893        if ( !apbuart_fifo_isEmpty(fifo) ){
894                fifo->tail = (fifo->tail >= fifo->max ) ? fifo->buf : fifo->tail+1;
895                fifo->full = 0;
896        }
897}
Note: See TracBrowser for help on using the repository browser.