ia64/xen-unstable

view xen/drivers/char/ns16550.c @ 6552:a9873d384da4

Merge.
author adsharma@los-vmm.sc.intel.com
date Thu Aug 25 12:24:48 2005 -0700 (2005-08-25)
parents 112d44270733 fa0754a9f64f
children dfaf788ab18c
line source
1 /******************************************************************************
2 * ns16550.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-2005, K A Fraser
9 */
11 #include <xen/config.h>
12 #include <xen/init.h>
13 #include <xen/irq.h>
14 #include <xen/sched.h>
15 #include <xen/serial.h>
16 #include <asm/io.h>
18 /*
19 * Configure serial port with a string <baud>,DPS,<io-base>,<irq>.
20 * The tail of the string can be omitted if platform defaults are sufficient.
21 * If the baud rate is pre-configured, perhaps by a bootloader, then 'auto'
22 * can be specified in place of a numeric baud rate.
23 */
24 static char opt_com1[30] = "", opt_com2[30] = "";
25 string_param("com1", opt_com1);
26 string_param("com2", opt_com2);
28 static struct ns16550 {
29 int baud, data_bits, parity, stop_bits, irq;
30 unsigned long io_base; /* I/O port or memory-mapped I/O address. */
31 char *remapped_io_base; /* Remapped virtual address of mmap I/O. */
32 struct irqaction irqaction;
33 } ns16550_com[2] = { { 0 } };
35 /* Register offsets */
36 #define RBR 0x00 /* receive buffer */
37 #define THR 0x00 /* transmit holding */
38 #define IER 0x01 /* interrupt enable */
39 #define IIR 0x02 /* interrupt identity */
40 #define FCR 0x02 /* FIFO control */
41 #define LCR 0x03 /* line control */
42 #define MCR 0x04 /* Modem control */
43 #define LSR 0x05 /* line status */
44 #define MSR 0x06 /* Modem status */
45 #define DLL 0x00 /* divisor latch (ls) (DLAB=1) */
46 #define DLM 0x01 /* divisor latch (ms) (DLAB=1) */
48 /* Interrupt Enable Register */
49 #define IER_ERDAI 0x01 /* rx data recv'd */
50 #define IER_ETHREI 0x02 /* tx reg. empty */
51 #define IER_ELSI 0x04 /* rx line status */
52 #define IER_EMSI 0x08 /* MODEM status */
54 /* Interrupt Identification Register */
55 #define IIR_NOINT 0x01 /* no interrupt pending */
56 #define IIR_IMASK 0x06 /* interrupt identity: */
57 #define IIR_LSI 0x06 /* - rx line status */
58 #define IIR_RDAI 0x04 /* - rx data recv'd */
59 #define IIR_THREI 0x02 /* - tx reg. empty */
60 #define IIR_MSI 0x00 /* - MODEM status */
62 /* FIFO Control Register */
63 #define FCR_ENABLE 0x01 /* enable FIFO */
64 #define FCR_CLRX 0x02 /* clear Rx FIFO */
65 #define FCR_CLTX 0x04 /* clear Tx FIFO */
66 #define FCR_DMA 0x10 /* enter DMA mode */
67 #define FCR_TRG1 0x00 /* Rx FIFO trig lev 1 */
68 #define FCR_TRG4 0x40 /* Rx FIFO trig lev 4 */
69 #define FCR_TRG8 0x80 /* Rx FIFO trig lev 8 */
70 #define FCR_TRG14 0xc0 /* Rx FIFO trig lev 14 */
72 /* Line Control Register */
73 #define LCR_DLAB 0x80 /* Divisor Latch Access */
75 /* Modem Control Register */
76 #define MCR_DTR 0x01 /* Data Terminal Ready */
77 #define MCR_RTS 0x02 /* Request to Send */
78 #define MCR_OUT2 0x08 /* OUT2: interrupt mask */
80 /* Line Status Register */
81 #define LSR_DR 0x01 /* Data ready */
82 #define LSR_OE 0x02 /* Overrun */
83 #define LSR_PE 0x04 /* Parity error */
84 #define LSR_FE 0x08 /* Framing error */
85 #define LSR_BI 0x10 /* Break */
86 #define LSR_THRE 0x20 /* Xmit hold reg empty */
87 #define LSR_TEMT 0x40 /* Xmitter empty */
88 #define LSR_ERR 0x80 /* Error */
90 /* These parity settings can be ORed directly into the LCR. */
91 #define PARITY_NONE (0<<3)
92 #define PARITY_ODD (1<<3)
93 #define PARITY_EVEN (3<<3)
94 #define PARITY_MARK (5<<3)
95 #define PARITY_SPACE (7<<3)
97 static char ns_read_reg(struct ns16550 *uart, int reg)
98 {
99 if ( uart->remapped_io_base == NULL )
100 return inb(uart->io_base + reg);
101 return readb(uart->remapped_io_base + reg);
102 }
104 static void ns_write_reg(struct ns16550 *uart, int reg, char c)
105 {
106 if ( uart->remapped_io_base == NULL )
107 return outb(c, uart->io_base + reg);
108 writeb(c, uart->remapped_io_base + reg);
109 }
111 static void ns16550_interrupt(
112 int irq, void *dev_id, struct cpu_user_regs *regs)
113 {
114 struct serial_port *port = dev_id;
115 struct ns16550 *uart = port->uart;
117 while ( !(ns_read_reg(uart, IIR) & IIR_NOINT) )
118 {
119 serial_tx_interrupt(port, regs);
120 serial_rx_interrupt(port, regs);
121 }
122 }
124 static int ns16550_tx_empty(struct serial_port *port)
125 {
126 struct ns16550 *uart = port->uart;
127 return !!(ns_read_reg(uart, LSR) & LSR_THRE);
128 }
130 static void ns16550_putc(struct serial_port *port, char c)
131 {
132 struct ns16550 *uart = port->uart;
133 ns_write_reg(uart, THR, c);
134 }
136 static int ns16550_getc(struct serial_port *port, char *pc)
137 {
138 struct ns16550 *uart = port->uart;
140 if ( !(ns_read_reg(uart, LSR) & LSR_DR) )
141 return 0;
143 *pc = ns_read_reg(uart, RBR);
144 return 1;
145 }
147 static void ns16550_init_preirq(struct serial_port *port)
148 {
149 struct ns16550 *uart = port->uart;
150 unsigned char lcr;
152 /* I/O ports are distinguished by their size (16 bits). */
153 if ( uart->io_base >= 0x10000 )
154 uart->remapped_io_base = (char *)ioremap(uart->io_base, 8);
156 lcr = (uart->data_bits - 5) | ((uart->stop_bits - 1) << 2) | uart->parity;
158 /* No interrupts. */
159 ns_write_reg(uart, IER, 0);
161 /* Line control and baud-rate generator. */
162 if ( uart->baud != BAUD_AUTO )
163 {
164 ns_write_reg(uart, LCR, lcr | LCR_DLAB);
165 ns_write_reg(uart, DLL, 115200/uart->baud); /* baud lo */
166 ns_write_reg(uart, DLM, 0); /* baud hi */
167 }
168 ns_write_reg(uart, LCR, lcr); /* parity, data, stop */
170 /* No flow ctrl: DTR and RTS are both wedged high to keep remote happy. */
171 ns_write_reg(uart, MCR, MCR_DTR | MCR_RTS);
173 /* Enable and clear the FIFOs. Set a large trigger threshold. */
174 ns_write_reg(uart, FCR, FCR_ENABLE | FCR_CLRX | FCR_CLTX | FCR_TRG14);
176 /* Check this really is a 16550+. Otherwise we have no FIFOs. */
177 if ( (ns_read_reg(uart, IIR) & 0xc0) == 0xc0 )
178 port->tx_fifo_size = 16;
179 }
181 static void ns16550_init_postirq(struct serial_port *port)
182 {
183 struct ns16550 *uart = port->uart;
184 int rc;
186 if ( uart->irq <= 0 )
187 return;
189 serial_async_transmit(port);
191 uart->irqaction.handler = ns16550_interrupt;
192 uart->irqaction.name = "ns16550";
193 uart->irqaction.dev_id = port;
194 if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 )
195 printk("ERROR: Failed to allocate na16550 IRQ %d\n", uart->irq);
197 /* Master interrupt enable; also keep DTR/RTS asserted. */
198 ns_write_reg(uart, MCR, MCR_OUT2 | MCR_DTR | MCR_RTS);
200 /* Enable receive and transmit interrupts. */
201 ns_write_reg(uart, IER, IER_ERDAI | IER_ETHREI);
202 }
204 #ifdef CONFIG_X86
205 #include <asm/physdev.h>
206 static void ns16550_endboot(struct serial_port *port)
207 {
208 struct ns16550 *uart = port->uart;
209 physdev_modify_ioport_access_range(dom0, 0, uart->io_base, 8);
210 }
211 #else
212 #define ns16550_endboot NULL
213 #endif
215 static struct uart_driver ns16550_driver = {
216 .init_preirq = ns16550_init_preirq,
217 .init_postirq = ns16550_init_postirq,
218 .endboot = ns16550_endboot,
219 .tx_empty = ns16550_tx_empty,
220 .putc = ns16550_putc,
221 .getc = ns16550_getc
222 };
224 static int parse_parity_char(int c)
225 {
226 switch ( c )
227 {
228 case 'n':
229 return PARITY_NONE;
230 case 'o':
231 return PARITY_ODD;
232 case 'e':
233 return PARITY_EVEN;
234 case 'm':
235 return PARITY_MARK;
236 case 's':
237 return PARITY_SPACE;
238 }
239 return 0;
240 }
242 #define PARSE_ERR(_f, _a...) \
243 do { \
244 printk( "ERROR: " _f "\n" , ## _a ); \
245 return; \
246 } while ( 0 )
248 static void ns16550_parse_port_config(struct ns16550 *uart, char *conf)
249 {
250 int baud;
252 /* No user-specified configuration? */
253 if ( (conf == NULL) || (*conf == '\0') )
254 {
255 /* Some platforms may automatically probe the UART configuartion. */
256 if ( uart->baud != 0 )
257 goto config_parsed;
258 return;
259 }
261 if ( strncmp(conf, "auto", 4) == 0 )
262 {
263 uart->baud = BAUD_AUTO;
264 conf += 4;
265 }
266 else if ( (baud = simple_strtoul(conf, &conf, 10)) != 0 )
267 uart->baud = baud;
269 if ( *conf != ',' )
270 goto config_parsed;
271 conf++;
273 uart->data_bits = simple_strtoul(conf, &conf, 10);
275 uart->parity = parse_parity_char(*conf);
276 conf++;
278 uart->stop_bits = simple_strtoul(conf, &conf, 10);
280 if ( *conf == ',' )
281 {
282 conf++;
283 uart->io_base = simple_strtoul(conf, &conf, 0);
285 if ( *conf == ',' )
286 {
287 conf++;
288 uart->irq = simple_strtoul(conf, &conf, 10);
289 }
290 }
292 config_parsed:
293 /* Sanity checks. */
294 if ( (uart->baud != BAUD_AUTO) &&
295 ((uart->baud < 1200) || (uart->baud > 115200)) )
296 PARSE_ERR("Baud rate %d outside supported range.", uart->baud);
297 if ( (uart->data_bits < 5) || (uart->data_bits > 8) )
298 PARSE_ERR("%d data bits are unsupported.", uart->data_bits);
299 if ( (uart->stop_bits < 1) || (uart->stop_bits > 2) )
300 PARSE_ERR("%d stop bits are unsupported.", uart->stop_bits);
301 if ( uart->io_base == 0 )
302 PARSE_ERR("I/O base address must be specified.");
304 /* Register with generic serial driver. */
305 serial_register_uart(uart - ns16550_com, &ns16550_driver, uart);
306 }
308 void ns16550_init(int index, struct ns16550_defaults *defaults)
309 {
310 struct ns16550 *uart = &ns16550_com[index];
312 if ( (index < 0) || (index > 1) )
313 return;
315 if ( defaults != NULL )
316 {
317 uart->baud = defaults->baud;
318 uart->data_bits = defaults->data_bits;
319 uart->parity = parse_parity_char(defaults->parity);
320 uart->stop_bits = defaults->stop_bits;
321 uart->irq = defaults->irq;
322 uart->io_base = defaults->io_base;
323 }
325 ns16550_parse_port_config(uart, (index == 0) ? opt_com1 : opt_com2);
326 }
328 /*
329 * Local variables:
330 * mode: C
331 * c-set-style: "BSD"
332 * c-basic-offset: 4
333 * tab-width: 4
334 * indent-tabs-mode: nil
335 * End:
336 */