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.
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 }