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 | |
---|
59 | typedef 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 | |
---|
68 | static apbuart_fifo *apbuart_fifo_create(int size); |
---|
69 | static void apbuart_fifo_free(apbuart_fifo *fifo); |
---|
70 | static inline int apbuart_fifo_isFull(apbuart_fifo *fifo); |
---|
71 | static inline int apbuart_fifo_isEmpty(apbuart_fifo *fifo); |
---|
72 | static int apbuart_fifo_put(apbuart_fifo *fifo, unsigned char c); |
---|
73 | static int apbuart_fifo_get(apbuart_fifo *fifo, unsigned char *c); |
---|
74 | static int inline apbuart_fifo_peek(apbuart_fifo *fifo, unsigned char **c); |
---|
75 | static void inline apbuart_fifo_skip(apbuart_fifo *fifo); |
---|
76 | |
---|
77 | static rtems_device_driver apbuart_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); |
---|
78 | static rtems_device_driver apbuart_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); |
---|
79 | static rtems_device_driver apbuart_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); |
---|
80 | static rtems_device_driver apbuart_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); |
---|
81 | static rtems_device_driver apbuart_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); |
---|
82 | static rtems_device_driver apbuart_control(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); |
---|
83 | |
---|
84 | typedef 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 | |
---|
115 | static int dev_cnt; |
---|
116 | static apbuart_priv *apbuarts; |
---|
117 | static 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 | |
---|
121 | static rtems_driver_address_table apbuart_driver = APBUART_DRIVER_TABLE_ENTRY; |
---|
122 | static struct ambapp_bus *amba_bus; |
---|
123 | |
---|
124 | static void apbuart_interrupt(apbuart_priv *uart); |
---|
125 | #ifdef APBUART_DEFINE_INTHANDLER |
---|
126 | static void apbuart_interrupt_handler(rtems_vector_number v); |
---|
127 | #endif |
---|
128 | static void apbuart_hw_close(apbuart_priv *uart); |
---|
129 | static 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 |
---|
159 | static int apbuart_outbyte_try(struct apbuart_regs *regs, unsigned char ch) |
---|
160 | { |
---|
161 | if ( (READ_REG(®s->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 | |
---|
170 | static int apbuart_inbyte_try(struct apbuart_regs *regs) |
---|
171 | { |
---|
172 | unsigned int status; |
---|
173 | /* Clear errors if any */ |
---|
174 | if ( (status=READ_REG(®s->status)) & APBUART_STATUS_ERR) { |
---|
175 | regs->status = status & ~APBUART_STATUS_ERR; |
---|
176 | } |
---|
177 | |
---|
178 | /* Is Data available? */ |
---|
179 | if ( (READ_REG(®s->status) & APBUART_STATUS_DR) == 0 ) |
---|
180 | return -1; /* No data avail */ |
---|
181 | |
---|
182 | /* Return Data */ |
---|
183 | return (int)READ_REG(®s->data); |
---|
184 | } |
---|
185 | |
---|
186 | static 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 | |
---|
201 | static 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 | |
---|
218 | static 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 */ |
---|
225 | static 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 | */ |
---|
241 | static 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 |
---|
329 | static |
---|
330 | #endif |
---|
331 | int 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 | |
---|
357 | static 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 | |
---|
477 | static 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 | |
---|
538 | static 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 | |
---|
556 | static 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 | |
---|
606 | static 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 | |
---|
709 | static 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 | |
---|
833 | static 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 | |
---|
848 | static void apbuart_fifo_free(apbuart_fifo *fifo){ |
---|
849 | if ( fifo ) |
---|
850 | free(fifo); |
---|
851 | } |
---|
852 | |
---|
853 | static inline int apbuart_fifo_isFull(apbuart_fifo *fifo){ |
---|
854 | return fifo->full; |
---|
855 | } |
---|
856 | |
---|
857 | static inline int apbuart_fifo_isEmpty(apbuart_fifo *fifo){ |
---|
858 | if ( (fifo->head == fifo->tail) && !fifo->full ) |
---|
859 | return -1; |
---|
860 | return 0; |
---|
861 | } |
---|
862 | |
---|
863 | static 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 | |
---|
874 | static 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 | |
---|
884 | static 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 | |
---|
892 | static 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 | } |
---|