aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2007-02-13 15:35:38 -0500
committerPaul Mackerras <paulus@samba.org>2007-02-13 19:50:04 -0500
commit8d38a5b2fab1397d35ba1c92828a91b77ce9f865 (patch)
treed35295a7d137cd851107fff2fc32143ac630a176
parent775aeff44774c6933d8f9c14e1f325d8acd03136 (diff)
[POWERPC] Open Firmware serial port driver
This can be used for serial ports that are connected to an OF platform bus but are not autodetected by the lecacy serial support. It will automatically take over devices that come from the legacy serial detection, which usually is only one device. In some cases, rtas may be set up to use the serial port in the firmware, which allows easier debugging before probing the serial ports. In this case, the "used-by-rtas" property must be set by the firmware. This patch also adds code to the legacy serial driver to check for this. Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r--arch/powerpc/kernel/legacy_serial.c15
-rw-r--r--drivers/serial/Kconfig10
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/of_serial.c143
4 files changed, 169 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c
index 89f46f377922..325f490a10cc 100644
--- a/arch/powerpc/kernel/legacy_serial.c
+++ b/arch/powerpc/kernel/legacy_serial.c
@@ -124,6 +124,10 @@ static int __init add_legacy_soc_port(struct device_node *np,
124 if (get_property(np, "clock-frequency", NULL) == NULL) 124 if (get_property(np, "clock-frequency", NULL) == NULL)
125 return -1; 125 return -1;
126 126
127 /* if rtas uses this device, don't try to use it as well */
128 if (get_property(np, "used-by-rtas", NULL) != NULL)
129 return -1;
130
127 /* Get the address */ 131 /* Get the address */
128 addrp = of_get_address(soc_dev, 0, NULL, NULL); 132 addrp = of_get_address(soc_dev, 0, NULL, NULL);
129 if (addrp == NULL) 133 if (addrp == NULL)
@@ -334,6 +338,17 @@ void __init find_legacy_serial_ports(void)
334 of_node_put(tsi); 338 of_node_put(tsi);
335 } 339 }
336 340
341 /* First fill our array with opb bus ports */
342 for (np = NULL; (np = of_find_compatible_node(np, "serial", "ns16750")) != NULL;) {
343 struct device_node *opb = of_get_parent(np);
344 if (opb && !strcmp(opb->type, "opb")) {
345 index = add_legacy_soc_port(np, np);
346 if (index >= 0 && np == stdout)
347 legacy_serial_console = index;
348 }
349 of_node_put(opb);
350 }
351
337#ifdef CONFIG_PCI 352#ifdef CONFIG_PCI
338 /* Next, try to locate PCI ports */ 353 /* Next, try to locate PCI ports */
339 for (np = NULL; (np = of_find_all_nodes(np));) { 354 for (np = NULL; (np = of_find_all_nodes(np));) {
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 5cc6b91f8408..d0edbaacb1f7 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -999,4 +999,14 @@ config SERIAL_NETX_CONSOLE
999 If you have enabled the serial port on the Motorola IMX 999 If you have enabled the serial port on the Motorola IMX
1000 CPU you can make it the console by answering Y to this option. 1000 CPU you can make it the console by answering Y to this option.
1001 1001
1002config SERIAL_OF_PLATFORM
1003 tristate "Serial port on Open Firmware platform bus"
1004 depends on PPC_OF
1005 depends on SERIAL_8250
1006 help
1007 If you have a PowerPC based system that has serial ports
1008 on a platform specific bus, you should enable this option.
1009 Currently, only 8250 compatible ports are supported, but
1010 others can easily be added.
1011
1002endmenu 1012endmenu
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index df3632cd7df9..f3f82587b5fa 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -58,3 +58,4 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
58obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o 58obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
59obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o 59obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
60obj-$(CONFIG_SERIAL_NETX) += netx-serial.o 60obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
61obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c
new file mode 100644
index 000000000000..09b0b736a751
--- /dev/null
+++ b/drivers/serial/of_serial.c
@@ -0,0 +1,143 @@
1/*
2 * Serial Port driver for Open Firmware platform devices
3 *
4 * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp.
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/module.h>
14#include <linux/serial_core.h>
15#include <linux/serial_8250.h>
16
17#include <asm/of_platform.h>
18#include <asm/prom.h>
19
20/*
21 * Fill a struct uart_port for a given device node
22 */
23static int __devinit of_platform_serial_setup(struct of_device *ofdev,
24 int type, struct uart_port *port)
25{
26 struct resource resource;
27 struct device_node *np = ofdev->node;
28 const unsigned int *clk, *spd;
29 int ret;
30
31 memset(port, 0, sizeof *port);
32 spd = get_property(np, "current-speed", NULL);
33 clk = get_property(np, "clock-frequency", NULL);
34 if (!clk) {
35 dev_warn(&ofdev->dev, "no clock-frequency property set\n");
36 return -ENODEV;
37 }
38
39 ret = of_address_to_resource(np, 0, &resource);
40 if (ret) {
41 dev_warn(&ofdev->dev, "invalid address\n");
42 return ret;
43 }
44
45 spin_lock_init(&port->lock);
46 port->mapbase = resource.start;
47 port->irq = irq_of_parse_and_map(np, 0);
48 port->iotype = UPIO_MEM;
49 port->type = type;
50 port->uartclk = *clk;
51 port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP;
52 port->dev = &ofdev->dev;
53 port->custom_divisor = *clk / (16 * (*spd));
54
55 return 0;
56}
57
58/*
59 * Try to register a serial port
60 */
61static int __devinit of_platform_serial_probe(struct of_device *ofdev,
62 const struct of_device_id *id)
63{
64 struct uart_port port;
65 int port_type;
66 int ret;
67
68 if (of_find_property(ofdev->node, "used-by-rtas", NULL))
69 return -EBUSY;
70
71 port_type = (unsigned long)id->data;
72 ret = of_platform_serial_setup(ofdev, port_type, &port);
73 if (ret)
74 goto out;
75
76 switch (port_type) {
77 case PORT_UNKNOWN:
78 dev_info(&ofdev->dev, "Unknown serial port found, "
79 "attempting to use 8250 driver\n");
80 /* fallthrough */
81 case PORT_8250 ... PORT_MAX_8250:
82 ret = serial8250_register_port(&port);
83 break;
84 default:
85 /* need to add code for these */
86 ret = -ENODEV;
87 break;
88 }
89 if (ret < 0)
90 goto out;
91
92 ofdev->dev.driver_data = (void *)(unsigned long)ret;
93 return 0;
94out:
95 irq_dispose_mapping(port.irq);
96 return ret;
97}
98
99/*
100 * Release a line
101 */
102static int of_platform_serial_remove(struct of_device *ofdev)
103{
104 int line = (unsigned long)ofdev->dev.driver_data;
105 serial8250_unregister_port(line);
106 return 0;
107}
108
109/*
110 * A few common types, add more as needed.
111 */
112static struct of_device_id __devinitdata of_platform_serial_table[] = {
113 { .type = "serial", .compatible = "ns8250", .data = (void *)PORT_8250, },
114 { .type = "serial", .compatible = "ns16450", .data = (void *)PORT_16450, },
115 { .type = "serial", .compatible = "ns16550", .data = (void *)PORT_16550, },
116 { .type = "serial", .compatible = "ns16750", .data = (void *)PORT_16750, },
117 { .type = "serial", .data = (void *)PORT_UNKNOWN, },
118 { /* end of list */ },
119};
120
121static struct of_platform_driver __devinitdata of_platform_serial_driver = {
122 .owner = THIS_MODULE,
123 .name = "of_serial",
124 .probe = of_platform_serial_probe,
125 .remove = of_platform_serial_remove,
126 .match_table = of_platform_serial_table,
127};
128
129static int __init of_platform_serial_init(void)
130{
131 return of_register_platform_driver(&of_platform_serial_driver);
132}
133module_init(of_platform_serial_init);
134
135static void __exit of_platform_serial_exit(void)
136{
137 return of_unregister_platform_driver(&of_platform_serial_driver);
138};
139module_exit(of_platform_serial_exit);
140
141MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>");
142MODULE_LICENSE("GPL");
143MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices");