aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/serial
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/serial')
-rw-r--r--drivers/serial/Kconfig21
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/nwpserial.c475
-rw-r--r--drivers/serial/of_serial.c19
4 files changed, 514 insertions, 2 deletions
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index b695ab3142d8..3e525e38a5d9 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -457,7 +457,7 @@ config SERIAL_SAMSUNG
457 457
458config SERIAL_SAMSUNG_UARTS 458config SERIAL_SAMSUNG_UARTS
459 int 459 int
460 depends on SERIAL_SAMSUNG 460 depends on ARM && PLAT_S3C
461 default 2 if ARCH_S3C2400 461 default 2 if ARCH_S3C2400
462 default 4 if ARCH_S3C64XX || CPU_S3C2443 462 default 4 if ARCH_S3C64XX || CPU_S3C2443
463 default 3 463 default 3
@@ -1320,13 +1320,30 @@ config SERIAL_NETX_CONSOLE
1320config SERIAL_OF_PLATFORM 1320config SERIAL_OF_PLATFORM
1321 tristate "Serial port on Open Firmware platform bus" 1321 tristate "Serial port on Open Firmware platform bus"
1322 depends on PPC_OF 1322 depends on PPC_OF
1323 depends on SERIAL_8250 1323 depends on SERIAL_8250 || SERIAL_OF_PLATFORM_NWPSERIAL
1324 help 1324 help
1325 If you have a PowerPC based system that has serial ports 1325 If you have a PowerPC based system that has serial ports
1326 on a platform specific bus, you should enable this option. 1326 on a platform specific bus, you should enable this option.
1327 Currently, only 8250 compatible ports are supported, but 1327 Currently, only 8250 compatible ports are supported, but
1328 others can easily be added. 1328 others can easily be added.
1329 1329
1330config SERIAL_OF_PLATFORM_NWPSERIAL
1331 tristate "NWP serial port driver"
1332 depends on PPC_OF && PPC_DCR
1333 select SERIAL_OF_PLATFORM
1334 select SERIAL_CORE_CONSOLE
1335 select SERIAL_CORE
1336 help
1337 This driver supports the cell network processor nwp serial
1338 device.
1339
1340config SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE
1341 bool "Console on NWP serial port"
1342 depends on SERIAL_OF_PLATFORM_NWPSERIAL=y
1343 select SERIAL_CORE_CONSOLE
1344 help
1345 Support for Console on the NWP serial ports.
1346
1330config SERIAL_QE 1347config SERIAL_QE
1331 tristate "Freescale QUICC Engine serial port support" 1348 tristate "Freescale QUICC Engine serial port support"
1332 depends on QUICC_ENGINE 1349 depends on QUICC_ENGINE
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index dfe775ac45b2..8844c0a03929 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
72obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o 72obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
73obj-$(CONFIG_SERIAL_NETX) += netx-serial.o 73obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
74obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o 74obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
75obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o
75obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o 76obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
76obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o 77obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
77obj-$(CONFIG_SERIAL_QE) += ucc_uart.o 78obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
diff --git a/drivers/serial/nwpserial.c b/drivers/serial/nwpserial.c
new file mode 100644
index 000000000000..32f3eaf0d262
--- /dev/null
+++ b/drivers/serial/nwpserial.c
@@ -0,0 +1,475 @@
1/*
2 * Serial Port driver for a NWP uart device
3 *
4 * Copyright (C) 2008 IBM Corp., Benjamin Krill <ben@codiert.org>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12#include <linux/init.h>
13#include <linux/console.h>
14#include <linux/serial.h>
15#include <linux/serial_reg.h>
16#include <linux/serial_core.h>
17#include <linux/tty.h>
18#include <linux/irqreturn.h>
19#include <linux/mutex.h>
20#include <linux/of_platform.h>
21#include <linux/of_device.h>
22#include <linux/nwpserial.h>
23#include <asm/prom.h>
24#include <asm/dcr.h>
25
26#define NWPSERIAL_NR 2
27
28#define NWPSERIAL_STATUS_RXVALID 0x1
29#define NWPSERIAL_STATUS_TXFULL 0x2
30
31struct nwpserial_port {
32 struct uart_port port;
33 dcr_host_t dcr_host;
34 unsigned int ier;
35 unsigned int mcr;
36};
37
38static DEFINE_MUTEX(nwpserial_mutex);
39static struct nwpserial_port nwpserial_ports[NWPSERIAL_NR];
40
41static void wait_for_bits(struct nwpserial_port *up, int bits)
42{
43 unsigned int status, tmout = 10000;
44
45 /* Wait up to 10ms for the character(s) to be sent. */
46 do {
47 status = dcr_read(up->dcr_host, UART_LSR);
48
49 if (--tmout == 0)
50 break;
51 udelay(1);
52 } while ((status & bits) != bits);
53}
54
55#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE
56static void nwpserial_console_putchar(struct uart_port *port, int c)
57{
58 struct nwpserial_port *up;
59 up = container_of(port, struct nwpserial_port, port);
60 /* check if tx buffer is full */
61 wait_for_bits(up, UART_LSR_THRE);
62 dcr_write(up->dcr_host, UART_TX, c);
63 up->port.icount.tx++;
64}
65
66static void
67nwpserial_console_write(struct console *co, const char *s, unsigned int count)
68{
69 struct nwpserial_port *up = &nwpserial_ports[co->index];
70 unsigned long flags;
71 int locked = 1;
72
73 if (oops_in_progress)
74 locked = spin_trylock_irqsave(&up->port.lock, flags);
75 else
76 spin_lock_irqsave(&up->port.lock, flags);
77
78 /* save and disable interrupt */
79 up->ier = dcr_read(up->dcr_host, UART_IER);
80 dcr_write(up->dcr_host, UART_IER, up->ier & ~UART_IER_RDI);
81
82 uart_console_write(&up->port, s, count, nwpserial_console_putchar);
83
84 /* wait for transmitter to become emtpy */
85 while ((dcr_read(up->dcr_host, UART_LSR) & UART_LSR_THRE) == 0)
86 cpu_relax();
87
88 /* restore interrupt state */
89 dcr_write(up->dcr_host, UART_IER, up->ier);
90
91 if (locked)
92 spin_unlock_irqrestore(&up->port.lock, flags);
93}
94
95static struct uart_driver nwpserial_reg;
96static struct console nwpserial_console = {
97 .name = "ttySQ",
98 .write = nwpserial_console_write,
99 .device = uart_console_device,
100 .flags = CON_PRINTBUFFER,
101 .index = -1,
102 .data = &nwpserial_reg,
103};
104#define NWPSERIAL_CONSOLE (&nwpserial_console)
105#else
106#define NWPSERIAL_CONSOLE NULL
107#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */
108
109/**************************************************************************/
110
111static int nwpserial_request_port(struct uart_port *port)
112{
113 return 0;
114}
115
116static void nwpserial_release_port(struct uart_port *port)
117{
118 /* N/A */
119}
120
121static void nwpserial_config_port(struct uart_port *port, int flags)
122{
123 port->type = PORT_NWPSERIAL;
124}
125
126static irqreturn_t nwpserial_interrupt(int irq, void *dev_id)
127{
128 struct nwpserial_port *up = dev_id;
129 struct tty_struct *tty = up->port.info->port.tty;
130 irqreturn_t ret;
131 unsigned int iir;
132 unsigned char ch;
133
134 spin_lock(&up->port.lock);
135
136 /* check if the uart was the interrupt source. */
137 iir = dcr_read(up->dcr_host, UART_IIR);
138 if (!iir) {
139 ret = IRQ_NONE;
140 goto out;
141 }
142
143 do {
144 up->port.icount.rx++;
145 ch = dcr_read(up->dcr_host, UART_RX);
146 if (up->port.ignore_status_mask != NWPSERIAL_STATUS_RXVALID)
147 tty_insert_flip_char(tty, ch, TTY_NORMAL);
148 } while (dcr_read(up->dcr_host, UART_RX) & UART_LSR_DR);
149
150 tty_flip_buffer_push(tty);
151 ret = IRQ_HANDLED;
152
153out:
154 spin_unlock(&up->port.lock);
155 return ret;
156}
157
158static int nwpserial_startup(struct uart_port *port)
159{
160 struct nwpserial_port *up;
161 int err;
162
163 up = container_of(port, struct nwpserial_port, port);
164
165 /* disable flow control by default */
166 up->mcr = dcr_read(up->dcr_host, UART_MCR) & ~UART_MCR_AFE;
167 dcr_write(up->dcr_host, UART_MCR, up->mcr);
168
169 /* register interrupt handler */
170 err = request_irq(up->port.irq, nwpserial_interrupt,
171 IRQF_SHARED, "nwpserial", up);
172 if (err)
173 return err;
174
175 /* enable interrupts */
176 up->ier = UART_IER_RDI;
177 dcr_write(up->dcr_host, UART_IER, up->ier);
178
179 /* enable receiving */
180 up->port.ignore_status_mask &= ~NWPSERIAL_STATUS_RXVALID;
181
182 return 0;
183}
184
185static void nwpserial_shutdown(struct uart_port *port)
186{
187 struct nwpserial_port *up;
188 up = container_of(port, struct nwpserial_port, port);
189
190 /* disable receiving */
191 up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID;
192
193 /* disable interrupts from this port */
194 up->ier = 0;
195 dcr_write(up->dcr_host, UART_IER, up->ier);
196
197 /* free irq */
198 free_irq(up->port.irq, port);
199}
200
201static int nwpserial_verify_port(struct uart_port *port,
202 struct serial_struct *ser)
203{
204 return -EINVAL;
205}
206
207static const char *nwpserial_type(struct uart_port *port)
208{
209 return port->type == PORT_NWPSERIAL ? "nwpserial" : NULL;
210}
211
212static void nwpserial_set_termios(struct uart_port *port,
213 struct ktermios *termios, struct ktermios *old)
214{
215 struct nwpserial_port *up;
216 up = container_of(port, struct nwpserial_port, port);
217
218 up->port.read_status_mask = NWPSERIAL_STATUS_RXVALID
219 | NWPSERIAL_STATUS_TXFULL;
220
221 up->port.ignore_status_mask = 0;
222 /* ignore all characters if CREAD is not set */
223 if ((termios->c_cflag & CREAD) == 0)
224 up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID;
225
226 /* Copy back the old hardware settings */
227 if (old)
228 tty_termios_copy_hw(termios, old);
229}
230
231static void nwpserial_break_ctl(struct uart_port *port, int ctl)
232{
233 /* N/A */
234}
235
236static void nwpserial_enable_ms(struct uart_port *port)
237{
238 /* N/A */
239}
240
241static void nwpserial_stop_rx(struct uart_port *port)
242{
243 struct nwpserial_port *up;
244 up = container_of(port, struct nwpserial_port, port);
245 /* don't forward any more data (like !CREAD) */
246 up->port.ignore_status_mask = NWPSERIAL_STATUS_RXVALID;
247}
248
249static void nwpserial_putchar(struct nwpserial_port *up, unsigned char c)
250{
251 /* check if tx buffer is full */
252 wait_for_bits(up, UART_LSR_THRE);
253 dcr_write(up->dcr_host, UART_TX, c);
254 up->port.icount.tx++;
255}
256
257static void nwpserial_start_tx(struct uart_port *port)
258{
259 struct nwpserial_port *up;
260 struct circ_buf *xmit;
261 up = container_of(port, struct nwpserial_port, port);
262 xmit = &up->port.info->xmit;
263
264 if (port->x_char) {
265 nwpserial_putchar(up, up->port.x_char);
266 port->x_char = 0;
267 }
268
269 while (!(uart_circ_empty(xmit) || uart_tx_stopped(&up->port))) {
270 nwpserial_putchar(up, xmit->buf[xmit->tail]);
271 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
272 }
273}
274
275static unsigned int nwpserial_get_mctrl(struct uart_port *port)
276{
277 return 0;
278}
279
280static void nwpserial_set_mctrl(struct uart_port *port, unsigned int mctrl)
281{
282 /* N/A */
283}
284
285static void nwpserial_stop_tx(struct uart_port *port)
286{
287 /* N/A */
288}
289
290static unsigned int nwpserial_tx_empty(struct uart_port *port)
291{
292 struct nwpserial_port *up;
293 unsigned long flags;
294 int ret;
295 up = container_of(port, struct nwpserial_port, port);
296
297 spin_lock_irqsave(&up->port.lock, flags);
298 ret = dcr_read(up->dcr_host, UART_LSR);
299 spin_unlock_irqrestore(&up->port.lock, flags);
300
301 return ret & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
302}
303
304static struct uart_ops nwpserial_pops = {
305 .tx_empty = nwpserial_tx_empty,
306 .set_mctrl = nwpserial_set_mctrl,
307 .get_mctrl = nwpserial_get_mctrl,
308 .stop_tx = nwpserial_stop_tx,
309 .start_tx = nwpserial_start_tx,
310 .stop_rx = nwpserial_stop_rx,
311 .enable_ms = nwpserial_enable_ms,
312 .break_ctl = nwpserial_break_ctl,
313 .startup = nwpserial_startup,
314 .shutdown = nwpserial_shutdown,
315 .set_termios = nwpserial_set_termios,
316 .type = nwpserial_type,
317 .release_port = nwpserial_release_port,
318 .request_port = nwpserial_request_port,
319 .config_port = nwpserial_config_port,
320 .verify_port = nwpserial_verify_port,
321};
322
323static struct uart_driver nwpserial_reg = {
324 .owner = THIS_MODULE,
325 .driver_name = "nwpserial",
326 .dev_name = "ttySQ",
327 .major = TTY_MAJOR,
328 .minor = 68,
329 .nr = NWPSERIAL_NR,
330 .cons = NWPSERIAL_CONSOLE,
331};
332
333int nwpserial_register_port(struct uart_port *port)
334{
335 struct nwpserial_port *up = NULL;
336 int ret = -1;
337 int i;
338 static int first = 1;
339 int dcr_len;
340 int dcr_base;
341 struct device_node *dn;
342
343 mutex_lock(&nwpserial_mutex);
344
345 dn = to_of_device(port->dev)->node;
346 if (dn == NULL)
347 goto out;
348
349 /* get dcr base. */
350 dcr_base = dcr_resource_start(dn, 0);
351
352 /* find matching entry */
353 for (i = 0; i < NWPSERIAL_NR; i++)
354 if (nwpserial_ports[i].port.iobase == dcr_base) {
355 up = &nwpserial_ports[i];
356 break;
357 }
358
359 /* we didn't find a mtching entry, search for a free port */
360 if (up == NULL)
361 for (i = 0; i < NWPSERIAL_NR; i++)
362 if (nwpserial_ports[i].port.type == PORT_UNKNOWN &&
363 nwpserial_ports[i].port.iobase == 0) {
364 up = &nwpserial_ports[i];
365 break;
366 }
367
368 if (up == NULL) {
369 ret = -EBUSY;
370 goto out;
371 }
372
373 if (first)
374 uart_register_driver(&nwpserial_reg);
375 first = 0;
376
377 up->port.membase = port->membase;
378 up->port.irq = port->irq;
379 up->port.uartclk = port->uartclk;
380 up->port.fifosize = port->fifosize;
381 up->port.regshift = port->regshift;
382 up->port.iotype = port->iotype;
383 up->port.flags = port->flags;
384 up->port.mapbase = port->mapbase;
385 up->port.private_data = port->private_data;
386
387 if (port->dev)
388 up->port.dev = port->dev;
389
390 if (up->port.iobase != dcr_base) {
391 up->port.ops = &nwpserial_pops;
392 up->port.fifosize = 16;
393
394 spin_lock_init(&up->port.lock);
395
396 up->port.iobase = dcr_base;
397 dcr_len = dcr_resource_len(dn, 0);
398
399 up->dcr_host = dcr_map(dn, dcr_base, dcr_len);
400 if (!DCR_MAP_OK(up->dcr_host)) {
401 printk(KERN_ERR "Cannot map DCR resources for NWPSERIAL");
402 goto out;
403 }
404 }
405
406 ret = uart_add_one_port(&nwpserial_reg, &up->port);
407 if (ret == 0)
408 ret = up->port.line;
409
410out:
411 mutex_unlock(&nwpserial_mutex);
412
413 return ret;
414}
415EXPORT_SYMBOL(nwpserial_register_port);
416
417void nwpserial_unregister_port(int line)
418{
419 struct nwpserial_port *up = &nwpserial_ports[line];
420 mutex_lock(&nwpserial_mutex);
421 uart_remove_one_port(&nwpserial_reg, &up->port);
422
423 up->port.type = PORT_UNKNOWN;
424
425 mutex_unlock(&nwpserial_mutex);
426}
427EXPORT_SYMBOL(nwpserial_unregister_port);
428
429#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE
430static int __init nwpserial_console_init(void)
431{
432 struct nwpserial_port *up = NULL;
433 struct device_node *dn;
434 const char *name;
435 int dcr_base;
436 int dcr_len;
437 int i;
438
439 /* search for a free port */
440 for (i = 0; i < NWPSERIAL_NR; i++)
441 if (nwpserial_ports[i].port.type == PORT_UNKNOWN) {
442 up = &nwpserial_ports[i];
443 break;
444 }
445
446 if (up == NULL)
447 return -1;
448
449 name = of_get_property(of_chosen, "linux,stdout-path", NULL);
450 if (name == NULL)
451 return -1;
452
453 dn = of_find_node_by_path(name);
454 if (!dn)
455 return -1;
456
457 spin_lock_init(&up->port.lock);
458 up->port.ops = &nwpserial_pops;
459 up->port.type = PORT_NWPSERIAL;
460 up->port.fifosize = 16;
461
462 dcr_base = dcr_resource_start(dn, 0);
463 dcr_len = dcr_resource_len(dn, 0);
464 up->port.iobase = dcr_base;
465
466 up->dcr_host = dcr_map(dn, dcr_base, dcr_len);
467 if (!DCR_MAP_OK(up->dcr_host)) {
468 printk("Cannot map DCR resources for SERIAL");
469 return -1;
470 }
471 register_console(&nwpserial_console);
472 return 0;
473}
474console_initcall(nwpserial_console_init);
475#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */
diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c
index 8fa0ff561e9f..a821e3a3d664 100644
--- a/drivers/serial/of_serial.c
+++ b/drivers/serial/of_serial.c
@@ -14,6 +14,7 @@
14#include <linux/serial_core.h> 14#include <linux/serial_core.h>
15#include <linux/serial_8250.h> 15#include <linux/serial_8250.h>
16#include <linux/of_platform.h> 16#include <linux/of_platform.h>
17#include <linux/nwpserial.h>
17 18
18#include <asm/prom.h> 19#include <asm/prom.h>
19 20
@@ -99,9 +100,16 @@ static int __devinit of_platform_serial_probe(struct of_device *ofdev,
99 goto out; 100 goto out;
100 101
101 switch (port_type) { 102 switch (port_type) {
103#ifdef CONFIG_SERIAL_8250
102 case PORT_8250 ... PORT_MAX_8250: 104 case PORT_8250 ... PORT_MAX_8250:
103 ret = serial8250_register_port(&port); 105 ret = serial8250_register_port(&port);
104 break; 106 break;
107#endif
108#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
109 case PORT_NWPSERIAL:
110 ret = nwpserial_register_port(&port);
111 break;
112#endif
105 default: 113 default:
106 /* need to add code for these */ 114 /* need to add code for these */
107 case PORT_UNKNOWN: 115 case PORT_UNKNOWN:
@@ -129,9 +137,16 @@ static int of_platform_serial_remove(struct of_device *ofdev)
129{ 137{
130 struct of_serial_info *info = ofdev->dev.driver_data; 138 struct of_serial_info *info = ofdev->dev.driver_data;
131 switch (info->type) { 139 switch (info->type) {
140#ifdef CONFIG_SERIAL_8250
132 case PORT_8250 ... PORT_MAX_8250: 141 case PORT_8250 ... PORT_MAX_8250:
133 serial8250_unregister_port(info->line); 142 serial8250_unregister_port(info->line);
134 break; 143 break;
144#endif
145#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
146 case PORT_NWPSERIAL:
147 nwpserial_unregister_port(info->line);
148 break;
149#endif
135 default: 150 default:
136 /* need to add code for these */ 151 /* need to add code for these */
137 break; 152 break;
@@ -148,6 +163,10 @@ static struct of_device_id __devinitdata of_platform_serial_table[] = {
148 { .type = "serial", .compatible = "ns16450", .data = (void *)PORT_16450, }, 163 { .type = "serial", .compatible = "ns16450", .data = (void *)PORT_16450, },
149 { .type = "serial", .compatible = "ns16550", .data = (void *)PORT_16550, }, 164 { .type = "serial", .compatible = "ns16550", .data = (void *)PORT_16550, },
150 { .type = "serial", .compatible = "ns16750", .data = (void *)PORT_16750, }, 165 { .type = "serial", .compatible = "ns16750", .data = (void *)PORT_16750, },
166#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
167 { .type = "serial", .compatible = "ibm,qpace-nwp-serial",
168 .data = (void *)PORT_NWPSERIAL, },
169#endif
151 { .type = "serial", .data = (void *)PORT_UNKNOWN, }, 170 { .type = "serial", .data = (void *)PORT_UNKNOWN, },
152 { /* end of list */ }, 171 { /* end of list */ },
153}; 172};