debuggers.hg
view xen/drivers/char/serial.c @ 2651:e1abd8945ded
bitkeeper revision 1.1159.1.213 (416517f3vAbY9ISDviAe0Gjenl6dKw)
Take better care of over time consistency in XenLinux.
Avoid spinning on serial line with interrupts disabled in Xen.
Take better care of over time consistency in XenLinux.
Avoid spinning on serial line with interrupts disabled in Xen.
author | kaf24@freefall.cl.cam.ac.uk |
---|---|
date | Thu Oct 07 10:18:27 2004 +0000 (2004-10-07) |
parents | dae98734f12e |
children | 150d5c49daf1 a5beee8013f5 61a55dee09d8 |
line source
1 /******************************************************************************
2 * serial.c
3 *
4 * Driver for 16550-series UARTs. This driver is to be kept within Xen as
5 * it permits debugging of seriously-toasted machines (e.g., in situations
6 * where a device driver within a guest OS would be inaccessible).
7 *
8 * Copyright (c) 2003-2004, K A Fraser
9 */
11 #include <xen/config.h>
12 #include <xen/irq.h>
13 #include <xen/keyhandler.h>
14 #include <asm/pdb.h>
15 #include <xen/reboot.h>
16 #include <xen/sched.h>
17 #include <xen/serial.h>
18 #include <asm/io.h>
20 /* Register offsets */
21 #define RBR 0x00 /* receive buffer */
22 #define THR 0x00 /* transmit holding */
23 #define IER 0x01 /* interrupt enable */
24 #define IIR 0x02 /* interrupt identity */
25 #define FCR 0x02 /* FIFO control */
26 #define LCR 0x03 /* line control */
27 #define MCR 0x04 /* Modem control */
28 #define LSR 0x05 /* line status */
29 #define MSR 0x06 /* Modem status */
30 #define DLL 0x00 /* divisor latch (ls) ( DLAB=1) */
31 #define DLM 0x01 /* divisor latch (ms) ( DLAB=1) */
33 /* Interrupt Enable Register */
34 #define IER_ERDAI 0x01 /* rx data recv'd */
35 #define IER_ETHREI 0x02 /* tx reg. empty */
36 #define IER_ELSI 0x04 /* rx line status */
37 #define IER_EMSI 0x08 /* MODEM status */
39 /* FIFO control register */
40 #define FCR_ENABLE 0x01 /* enable FIFO */
41 #define FCR_CLRX 0x02 /* clear Rx FIFO */
42 #define FCR_CLTX 0x04 /* clear Tx FIFO */
43 #define FCR_DMA 0x10 /* enter DMA mode */
44 #define FCR_TRG1 0x00 /* Rx FIFO trig lev 1 */
45 #define FCR_TRG4 0x40 /* Rx FIFO trig lev 4 */
46 #define FCR_TRG8 0x80 /* Rx FIFO trig lev 8 */
47 #define FCR_TRG14 0xc0 /* Rx FIFO trig lev 14 */
49 /* Line control register */
50 #define LCR_DLAB 0x80 /* Divisor Latch Access */
52 /* Modem Control Register */
53 #define MCR_DTR 0x01 /* Data Terminal Ready */
54 #define MCR_RTS 0x02 /* Request to Send */
55 #define MCR_OUT2 0x08 /* OUT2: interrupt mask */
57 /* Line Status Register */
58 #define LSR_DR 0x01 /* Data ready */
59 #define LSR_OE 0x02 /* Overrun */
60 #define LSR_PE 0x04 /* Parity error */
61 #define LSR_FE 0x08 /* Framing error */
62 #define LSR_BI 0x10 /* Break */
63 #define LSR_THRE 0x20 /* Xmit hold reg empty */
64 #define LSR_TEMT 0x40 /* Xmitter empty */
65 #define LSR_ERR 0x80 /* Error */
67 /* These parity settings can be ORed directly into the LCR. */
68 #define PARITY_NONE (0<<3)
69 #define PARITY_ODD (1<<3)
70 #define PARITY_EVEN (3<<3)
71 #define PARITY_MARK (5<<3)
72 #define PARITY_SPACE (7<<3)
74 #define RXBUFSZ 32
75 #define MASK_RXBUF_IDX(_i) ((_i)&(RXBUFSZ-1))
76 typedef struct {
77 int baud, data_bits, parity, stop_bits, io_base, irq;
78 serial_rx_fn rx_lo, rx_hi, rx;
79 spinlock_t lock;
80 unsigned char rxbuf[RXBUFSZ];
81 unsigned int rxbufp, rxbufc;
82 struct irqaction irqaction;
83 } uart_t;
85 static uart_t com[2] = {
86 { 0, 0, 0, 0, 0x3f8, 4,
87 NULL, NULL, NULL,
88 SPIN_LOCK_UNLOCKED },
89 { 0, 0, 0, 0, 0x2f8, 3,
90 NULL, NULL, NULL,
91 SPIN_LOCK_UNLOCKED }
92 };
94 #define UART_ENABLED(_u) ((_u)->baud != 0)
95 #define DISABLE_UART(_u) ((_u)->baud = 0)
98 /***********************
99 * PRIVATE FUNCTIONS
100 */
102 static void uart_rx(uart_t *uart, struct pt_regs *regs)
103 {
104 unsigned char c;
106 if ( !UART_ENABLED(uart) )
107 return;
109 /*
110 * No need for the uart spinlock here. Only the uart's own interrupt
111 * handler will read from the RBR and the handler isn't reentrant.
112 * Calls to serial_getc() will disable this handler before proceeding.
113 */
114 while ( inb(uart->io_base + LSR) & LSR_DR )
115 {
116 c = inb(uart->io_base + RBR);
117 if ( uart->rx != NULL )
118 uart->rx(c, regs);
119 else if ( (c & 0x80) && (uart->rx_hi != NULL) )
120 uart->rx_hi(c&0x7f, regs);
121 else if ( !(c & 0x80) && (uart->rx_lo != NULL) )
122 uart->rx_lo(c&0x7f, regs);
123 else if ( (uart->rxbufp - uart->rxbufc) != RXBUFSZ )
124 uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufp++)] = c;
125 }
126 }
128 static void serial_interrupt(int irq, void *dev_id, struct pt_regs *regs)
129 {
130 uart_rx((uart_t *)dev_id, regs);
131 }
133 static inline void __serial_putc(uart_t *uart, int handle, unsigned char c)
134 {
135 unsigned long flags;
136 int space;
138 if ( (c == '\n') && (handle & SERHND_COOKED) )
139 __serial_putc(uart, handle, '\r');
141 if ( handle & SERHND_HI )
142 c |= 0x80;
143 else if ( handle & SERHND_LO )
144 c &= 0x7f;
146 do {
147 spin_lock_irqsave(&uart->lock, flags);
148 if ( (space = (inb(uart->io_base + LSR) & LSR_THRE)) )
149 outb(c, uart->io_base + THR);
150 spin_unlock_irqrestore(&uart->lock, flags);
151 }
152 while ( !space );
153 }
155 #define PARSE_ERR(_f, _a...) \
156 do { \
157 printk( "ERROR: " _f "\n" , ## _a ); \
158 DISABLE_UART(uart); \
159 return; \
160 } while ( 0 )
162 static void parse_port_config(char *conf, uart_t *uart)
163 {
164 if ( *conf == '\0' )
165 return;
167 uart->baud = simple_strtol(conf, &conf, 10);
168 if ( (uart->baud < 1200) || (uart->baud > 115200) )
169 PARSE_ERR("Baud rate %d outside supported range.", uart->baud);
171 if ( *conf != ',' )
172 PARSE_ERR("Missing data/parity/stop specifiers.");
174 conf++;
176 uart->data_bits = simple_strtol(conf, &conf, 10);
177 if ( (uart->data_bits < 5) || (uart->data_bits > 8) )
178 PARSE_ERR("%d data bits are unsupported.", uart->data_bits);
180 switch ( *conf )
181 {
182 case 'n':
183 uart->parity = PARITY_NONE;
184 break;
185 case 'o':
186 uart->parity = PARITY_ODD;
187 break;
188 case 'e':
189 uart->parity = PARITY_EVEN;
190 break;
191 case 'm':
192 uart->parity = PARITY_MARK;
193 break;
194 case 's':
195 uart->parity = PARITY_SPACE;
196 break;
198 default:
199 PARSE_ERR("Invalid parity specifier '%c'.", *conf);
200 }
202 conf++;
204 uart->stop_bits = simple_strtol(conf, &conf, 10);
205 if ( (uart->stop_bits < 1) || (uart->stop_bits > 2) )
206 PARSE_ERR("%d stop bits are unsupported.", uart->stop_bits);
208 if ( *conf == ',' )
209 {
210 conf++;
212 uart->io_base = simple_strtol(conf, &conf, 0);
213 if ( (uart->io_base <= 0x0000) || (uart->io_base > 0xfff0) )
214 PARSE_ERR("I/O port base 0x%x is outside the supported range.",
215 uart->io_base);
217 if ( *conf != ',' )
218 PARSE_ERR("Missing IRQ specifier.");
220 conf++;
222 uart->irq = simple_strtol(conf, &conf, 10);
223 if ( (uart->irq <= 0) || (uart->irq >= 32) )
224 PARSE_ERR("IRQ %d is outside the supported range.", uart->irq);
225 }
226 }
228 static void uart_config_stage1(uart_t *uart)
229 {
230 unsigned char lcr;
232 if ( !UART_ENABLED(uart) )
233 return;
235 lcr = (uart->data_bits - 5) | ((uart->stop_bits - 1) << 2) | uart->parity;
237 /* No interrupts. */
238 outb(0, uart->io_base + IER);
240 /* Line control and baud-rate generator. */
241 outb(lcr | LCR_DLAB, uart->io_base + LCR);
242 outb(115200/uart->baud, uart->io_base + DLL); /* baud lo */
243 outb(0, uart->io_base + DLM); /* baud hi */
244 outb(lcr, uart->io_base + LCR); /* parity, data, stop */
246 /* No flow ctrl: DTR and RTS are both wedged high to keep remote happy. */
247 outb(MCR_DTR | MCR_RTS, uart->io_base + MCR);
249 /* Enable and clear the FIFOs. Set a large trigger threshold. */
250 outb(FCR_ENABLE | FCR_CLRX | FCR_CLTX | FCR_TRG14, uart->io_base + FCR);
251 }
253 static void uart_config_stage2(uart_t *uart)
254 {
255 int rc;
257 if ( !UART_ENABLED(uart) )
258 return;
260 uart->irqaction.handler = serial_interrupt;
261 uart->irqaction.name = "serial";
262 uart->irqaction.dev_id = uart;
263 if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 )
264 printk("ERROR: Failed to allocate serial IRQ %d\n", uart->irq);
266 /* For sanity, clear the receive FIFO. */
267 outb(FCR_ENABLE | FCR_CLRX | FCR_TRG14, uart->io_base + FCR);
269 /* Master interrupt enable; also keep DTR/RTS asserted. */
270 outb(MCR_OUT2 | MCR_DTR | MCR_RTS, uart->io_base + MCR);
272 /* Enable receive interrupts. */
273 outb(IER_ERDAI, uart->io_base + IER);
274 }
277 /***********************
278 * PUBLIC FUNCTIONS
279 */
281 void serial_init_stage1(void)
282 {
283 extern unsigned char opt_com1[], opt_com2[];
285 parse_port_config(opt_com1, &com[0]);
286 parse_port_config(opt_com2, &com[1]);
288 uart_config_stage1(&com[0]);
289 uart_config_stage1(&com[1]);
290 }
292 void serial_init_stage2(void)
293 {
294 uart_config_stage2(&com[0]);
295 uart_config_stage2(&com[1]);
296 }
298 int parse_serial_handle(char *conf)
299 {
300 int handle;
302 /* Silently fail if user has explicitly requested no serial I/O. */
303 if ( strcmp(conf, "none") == 0 )
304 return -1;
306 if ( strncmp(conf, "com", 3) != 0 )
307 goto fail;
309 switch ( conf[3] )
310 {
311 case '1':
312 handle = 0;
313 break;
314 case '2':
315 handle = 1;
316 break;
317 default:
318 goto fail;
319 }
321 if ( !UART_ENABLED(&com[handle]) )
322 {
323 printk("ERROR: cannot use unconfigured serial port COM%d\n", handle+1);
324 return -1;
325 }
327 if ( conf[4] == 'H' )
328 handle |= SERHND_HI;
329 else if ( conf[4] == 'L' )
330 handle |= SERHND_LO;
332 handle |= SERHND_COOKED;
334 return handle;
336 fail:
337 printk("ERROR: bad serial-interface specification '%s'\n", conf);
338 return -1;
339 }
341 void serial_set_rx_handler(int handle, serial_rx_fn fn)
342 {
343 uart_t *uart = &com[handle & SERHND_IDX];
344 unsigned long flags;
346 if ( handle == -1 )
347 return;
349 spin_lock_irqsave(&uart->lock, flags);
351 if ( uart->rx != NULL )
352 goto fail;
354 if ( handle & SERHND_LO )
355 {
356 if ( uart->rx_lo != NULL )
357 goto fail;
358 uart->rx_lo = fn;
359 }
360 else if ( handle & SERHND_HI )
361 {
362 if ( uart->rx_hi != NULL )
363 goto fail;
364 uart->rx_hi = fn;
365 }
366 else
367 {
368 if ( (uart->rx_hi != NULL) || (uart->rx_lo != NULL) )
369 goto fail;
370 uart->rx = fn;
371 }
373 spin_unlock_irqrestore(&uart->lock, flags);
374 return;
376 fail:
377 spin_unlock_irqrestore(&uart->lock, flags);
378 printk("ERROR: Conflicting receive handlers for COM%d\n",
379 handle & SERHND_IDX);
380 }
382 void serial_putc(int handle, unsigned char c)
383 {
384 uart_t *uart = &com[handle & SERHND_IDX];
386 if ( handle == -1 )
387 return;
389 __serial_putc(uart, handle, c);
390 }
392 void serial_puts(int handle, const unsigned char *s)
393 {
394 uart_t *uart = &com[handle & SERHND_IDX];
396 if ( handle == -1 )
397 return;
399 while ( *s != '\0' )
400 __serial_putc(uart, handle, *s++);
401 }
403 /* Returns TRUE if given character (*pc) matches the serial handle. */
404 static int byte_matches(int handle, unsigned char *pc)
405 {
406 if ( !(handle & SERHND_HI) )
407 {
408 if ( !(handle & SERHND_LO) || !(*pc & 0x80) )
409 return 1;
410 }
411 else if ( *pc & 0x80 )
412 {
413 *pc &= 0x7f;
414 return 1;
415 }
416 return 0;
417 }
419 unsigned char serial_getc(int handle)
420 {
421 uart_t *uart = &com[handle & SERHND_IDX];
422 unsigned char c;
423 unsigned long flags;
425 spin_lock_irqsave(&uart->lock, flags);
427 while ( uart->rxbufp != uart->rxbufc )
428 {
429 c = uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufc++)];
430 if ( byte_matches(handle, &c) )
431 goto out;
432 }
434 disable_irq(uart->irq);
436 /* disable_irq() may have raced execution of uart_rx(). */
437 while ( uart->rxbufp != uart->rxbufc )
438 {
439 c = uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufc++)];
440 if ( byte_matches(handle, &c) )
441 goto enable_and_out;
442 }
444 /* We now wait for the UART to receive a suitable character. */
445 do {
446 while ( (inb(uart->io_base + LSR) & LSR_DR) == 0 )
447 barrier();
448 c = inb(uart->io_base + RBR);
449 }
450 while ( !byte_matches(handle, &c) );
452 enable_and_out:
453 enable_irq(uart->irq);
454 out:
455 spin_unlock_irqrestore(&uart->lock, flags);
456 return c;
457 }
459 void serial_force_unlock(int handle)
460 {
461 uart_t *uart = &com[handle & SERHND_IDX];
462 if ( handle != -1 )
463 uart->lock = SPIN_LOCK_UNLOCKED;
464 }