Commit 1708d242 authored by David S. Miller's avatar David S. Miller Committed by David S. Miller

[SERIAL] sunsu: Convert to of_driver framework.

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4fa97dcf
...@@ -40,11 +40,8 @@ ...@@ -40,11 +40,8 @@
#include <asm/io.h> #include <asm/io.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/oplib.h> #include <asm/prom.h>
#include <asm/ebus.h> #include <asm/of_device.h>
#ifdef CONFIG_SPARC64
#include <asm/isa.h>
#endif
#if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ #define SUPPORT_SYSRQ
...@@ -94,10 +91,10 @@ struct uart_sunsu_port { ...@@ -94,10 +91,10 @@ struct uart_sunsu_port {
/* Probing information. */ /* Probing information. */
enum su_type su_type; enum su_type su_type;
unsigned int type_probed; /* XXX Stupid */ unsigned int type_probed; /* XXX Stupid */
int port_node; unsigned long reg_size;
#ifdef CONFIG_SERIO #ifdef CONFIG_SERIO
struct serio *serio; struct serio serio;
int serio_open; int serio_open;
#endif #endif
}; };
...@@ -509,7 +506,7 @@ static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *reg ...@@ -509,7 +506,7 @@ static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *reg
/* Stop-A is handled by drivers/char/keyboard.c now. */ /* Stop-A is handled by drivers/char/keyboard.c now. */
if (up->su_type == SU_PORT_KBD) { if (up->su_type == SU_PORT_KBD) {
#ifdef CONFIG_SERIO #ifdef CONFIG_SERIO
serio_interrupt(up->serio, ch, 0, regs); serio_interrupt(&up->serio, ch, 0, regs);
#endif #endif
} else if (up->su_type == SU_PORT_MS) { } else if (up->su_type == SU_PORT_MS) {
int ret = suncore_mouse_baud_detection(ch, is_break); int ret = suncore_mouse_baud_detection(ch, is_break);
...@@ -523,7 +520,7 @@ static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *reg ...@@ -523,7 +520,7 @@ static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *reg
case 0: case 0:
#ifdef CONFIG_SERIO #ifdef CONFIG_SERIO
serio_interrupt(up->serio, ch, 0, regs); serio_interrupt(&up->serio, ch, 0, regs);
#endif #endif
break; break;
}; };
...@@ -1031,99 +1028,14 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up) ...@@ -1031,99 +1028,14 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up)
{ {
unsigned char status1, status2, scratch, scratch2, scratch3; unsigned char status1, status2, scratch, scratch2, scratch3;
unsigned char save_lcr, save_mcr; unsigned char save_lcr, save_mcr;
struct linux_ebus_device *dev = NULL;
struct linux_ebus *ebus;
#ifdef CONFIG_SPARC64
struct sparc_isa_bridge *isa_br;
struct sparc_isa_device *isa_dev;
#endif
#ifndef CONFIG_SPARC64
struct linux_prom_registers reg0;
#endif
unsigned long flags; unsigned long flags;
if (!up->port_node || !up->su_type) if (up->su_type == SU_PORT_NONE)
return; return;
up->type_probed = PORT_UNKNOWN; up->type_probed = PORT_UNKNOWN;
up->port.iotype = UPIO_MEM; up->port.iotype = UPIO_MEM;
/*
* First we look for Ebus-bases su's
*/
for_each_ebus(ebus) {
for_each_ebusdev(dev, ebus) {
if (dev->prom_node->node == up->port_node) {
/*
* The EBus is broken on sparc; it delivers
* virtual addresses in resources. Oh well...
* This is correct on sparc64, though.
*/
up->port.membase = (char *) dev->resource[0].start;
/*
* This is correct on both architectures.
*/
up->port.mapbase = dev->resource[0].start;
up->port.irq = dev->irqs[0];
goto ebus_done;
}
}
}
#ifdef CONFIG_SPARC64
for_each_isa(isa_br) {
for_each_isadev(isa_dev, isa_br) {
if (isa_dev->prom_node->node == up->port_node) {
/* Same on sparc64. Cool architecure... */
up->port.membase = (char *) isa_dev->resource.start;
up->port.mapbase = isa_dev->resource.start;
up->port.irq = isa_dev->irq;
goto ebus_done;
}
}
}
#endif
#ifdef CONFIG_SPARC64
/*
* Not on Ebus, bailing.
*/
return;
#else
/*
* Not on Ebus, must be OBIO.
*/
if (prom_getproperty(up->port_node, "reg",
(char *)&reg0, sizeof(reg0)) == -1) {
prom_printf("sunsu: no \"reg\" property\n");
return;
}
prom_apply_obio_ranges(&reg0, 1);
if (reg0.which_io != 0) { /* Just in case... */
prom_printf("sunsu: bus number nonzero: 0x%x:%x\n",
reg0.which_io, reg0.phys_addr);
return;
}
up->port.mapbase = reg0.phys_addr;
if ((up->port.membase = ioremap(reg0.phys_addr, reg0.reg_size)) == 0) {
prom_printf("sunsu: Cannot map registers.\n");
return;
}
/*
* 0x20 is sun4m thing, Dave Redman heritage.
* See arch/sparc/kernel/irq.c.
*/
#define IRQ_4M(n) ((n)|0x20)
/*
* There is no intr property on MrCoffee, so hardwire it.
*/
up->port.irq = IRQ_4M(13);
#endif
ebus_done:
spin_lock_irqsave(&up->port.lock, flags); spin_lock_irqsave(&up->port.lock, flags);
if (!(up->port.flags & UPF_BUGGY_UART)) { if (!(up->port.flags & UPF_BUGGY_UART)) {
...@@ -1269,18 +1181,13 @@ static struct uart_driver sunsu_reg = { ...@@ -1269,18 +1181,13 @@ static struct uart_driver sunsu_reg = {
.major = TTY_MAJOR, .major = TTY_MAJOR,
}; };
static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up, int channel) static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up)
{ {
int quot, baud; int quot, baud;
#ifdef CONFIG_SERIO #ifdef CONFIG_SERIO
struct serio *serio; struct serio *serio;
#endif #endif
spin_lock_init(&up->port.lock);
up->port.line = channel;
up->port.type = PORT_UNKNOWN;
up->port.uartclk = (SU_BASE_BAUD * 16);
if (up->su_type == SU_PORT_KBD) { if (up->su_type == SU_PORT_KBD) {
up->cflag = B1200 | CS8 | CLOCAL | CREAD; up->cflag = B1200 | CS8 | CLOCAL | CREAD;
baud = 1200; baud = 1200;
...@@ -1292,18 +1199,10 @@ static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up, int channel) ...@@ -1292,18 +1199,10 @@ static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up, int channel)
sunsu_autoconfig(up); sunsu_autoconfig(up);
if (up->port.type == PORT_UNKNOWN) if (up->port.type == PORT_UNKNOWN)
return -1; return -ENODEV;
printk(KERN_INFO "su%d at 0x%p (irq = %d) is a %s\n",
channel,
up->port.membase, up->port.irq,
sunsu_type(&up->port));
#ifdef CONFIG_SERIO #ifdef CONFIG_SERIO
up->serio = serio = kmalloc(sizeof(struct serio), GFP_KERNEL); serio = &up->serio;
if (serio) {
memset(serio, 0, sizeof(*serio));
serio->port_data = up; serio->port_data = up;
serio->id.type = SERIO_RS232; serio->id.type = SERIO_RS232;
...@@ -1315,18 +1214,16 @@ static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up, int channel) ...@@ -1315,18 +1214,16 @@ static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up, int channel)
serio->id.extra = 1; serio->id.extra = 1;
strlcpy(serio->name, "sums", sizeof(serio->name)); strlcpy(serio->name, "sums", sizeof(serio->name));
} }
strlcpy(serio->phys, (channel == 0 ? "su/serio0" : "su/serio1"), strlcpy(serio->phys,
(!(up->port.line & 1) ? "su/serio0" : "su/serio1"),
sizeof(serio->phys)); sizeof(serio->phys));
serio->write = sunsu_serio_write; serio->write = sunsu_serio_write;
serio->open = sunsu_serio_open; serio->open = sunsu_serio_open;
serio->close = sunsu_serio_close; serio->close = sunsu_serio_close;
serio->dev.parent = up->port.dev;
serio_register_port(serio); serio_register_port(serio);
} else {
printk(KERN_WARNING "su%d: not enough memory for serio port\n",
channel);
}
#endif #endif
sunsu_change_speed(&up->port, up->cflag, 0, quot); sunsu_change_speed(&up->port, up->cflag, 0, quot);
...@@ -1458,22 +1355,20 @@ static struct console sunsu_cons = { ...@@ -1458,22 +1355,20 @@ static struct console sunsu_cons = {
* Register console. * Register console.
*/ */
static inline struct console *SUNSU_CONSOLE(void) static inline struct console *SUNSU_CONSOLE(int num_uart)
{ {
int i; int i;
if (con_is_present()) if (con_is_present())
return NULL; return NULL;
for (i = 0; i < UART_NR; i++) { for (i = 0; i < num_uart; i++) {
int this_minor = sunsu_reg.minor + i; int this_minor = sunsu_reg.minor + i;
if ((this_minor - 64) == (serial_console - 1)) if ((this_minor - 64) == (serial_console - 1))
break; break;
} }
if (i == UART_NR) if (i == num_uart)
return NULL;
if (sunsu_ports[i].port_node == 0)
return NULL; return NULL;
sunsu_cons.index = i; sunsu_cons.index = i;
...@@ -1481,252 +1376,180 @@ static inline struct console *SUNSU_CONSOLE(void) ...@@ -1481,252 +1376,180 @@ static inline struct console *SUNSU_CONSOLE(void)
return &sunsu_cons; return &sunsu_cons;
} }
#else #else
#define SUNSU_CONSOLE() (NULL) #define SUNSU_CONSOLE(num_uart) (NULL)
#define sunsu_serial_console_init() do { } while (0) #define sunsu_serial_console_init() do { } while (0)
#endif #endif
static int __init sunsu_serial_init(void) static enum su_type __devinit su_get_type(struct device_node *dp)
{ {
int instance, ret, i; struct device_node *ap = of_find_node_by_path("/aliases");
/* How many instances do we need? */ if (ap) {
instance = 0; char *keyb = of_get_property(ap, "keyboard", NULL);
for (i = 0; i < UART_NR; i++) { char *ms = of_get_property(ap, "mouse", NULL);
struct uart_sunsu_port *up = &sunsu_ports[i];
if (up->su_type == SU_PORT_MS || if (keyb) {
up->su_type == SU_PORT_KBD) if (dp == of_find_node_by_path(keyb))
continue; return SU_PORT_KBD;
}
if (ms) {
if (dp == of_find_node_by_path(ms))
return SU_PORT_MS;
}
}
spin_lock_init(&up->port.lock); return SU_PORT_PORT;
up->port.flags |= UPF_BOOT_AUTOCONF; }
up->port.type = PORT_UNKNOWN;
up->port.uartclk = (SU_BASE_BAUD * 16);
sunsu_autoconfig(up); static int __devinit su_probe(struct of_device *op, const struct of_device_id *match)
if (up->port.type == PORT_UNKNOWN) {
continue; static int inst;
struct device_node *dp = op->node;
struct uart_sunsu_port *up;
struct resource *rp;
int err;
up->port.line = instance++; if (inst >= UART_NR)
up->port.ops = &sunsu_pops; return -EINVAL;
}
sunsu_reg.minor = sunserial_current_minor; up = &sunsu_ports[inst];
up->port.line = inst;
sunsu_reg.nr = instance; spin_lock_init(&up->port.lock);
ret = uart_register_driver(&sunsu_reg); up->su_type = su_get_type(dp);
if (ret < 0)
return ret;
sunsu_reg.tty_driver->name_base = sunsu_reg.minor - 64; rp = &op->resource[0];
up->port.mapbase = op->resource[0].start;
sunserial_current_minor += instance; up->reg_size = (rp->end - rp->start) + 1;
up->port.membase = of_ioremap(rp, 0, up->reg_size, "su");
if (!up->port.membase)
return -ENOMEM;
sunsu_reg.cons = SUNSU_CONSOLE(); up->port.irq = op->irqs[0];
for (i = 0; i < UART_NR; i++) { up->port.dev = &op->dev;
struct uart_sunsu_port *up = &sunsu_ports[i];
/* Do not register Keyboard/Mouse lines with UART up->port.type = PORT_UNKNOWN;
* layer. up->port.uartclk = (SU_BASE_BAUD * 16);
*/
if (up->su_type == SU_PORT_MS ||
up->su_type == SU_PORT_KBD)
continue;
err = 0;
if (up->su_type == SU_PORT_KBD || up->su_type == SU_PORT_MS) {
err = sunsu_kbd_ms_init(up);
if (err)
goto out_unmap;
}
up->port.flags |= UPF_BOOT_AUTOCONF;
sunsu_autoconfig(up);
err = -ENODEV;
if (up->port.type == PORT_UNKNOWN) if (up->port.type == PORT_UNKNOWN)
continue; goto out_unmap;
uart_add_one_port(&sunsu_reg, &up->port); up->port.ops = &sunsu_pops;
}
err = uart_add_one_port(&sunsu_reg, &up->port);
if (err)
goto out_unmap;
dev_set_drvdata(&op->dev, up);
inst++;
return 0; return 0;
out_unmap:
of_iounmap(up->port.membase, up->reg_size);
return err;
} }
static int su_node_ok(int node, char *name, int namelen) static int __devexit su_remove(struct of_device *dev)
{ {
if (strncmp(name, "su", namelen) == 0 || struct uart_sunsu_port *up = dev_get_drvdata(&dev->dev);;
strncmp(name, "su_pnp", namelen) == 0)
return 1; if (up->su_type == SU_PORT_MS ||
up->su_type == SU_PORT_KBD) {
if (strncmp(name, "serial", namelen) == 0) { #ifdef CONFIG_SERIO
char compat[32]; serio_unregister_port(&up->serio);
int clen; #endif
} else if (up->port.type != PORT_UNKNOWN)
/* Is it _really_ a 'su' device? */ uart_remove_one_port(&sunsu_reg, &up->port);
clen = prom_getproperty(node, "compatible", compat, sizeof(compat));
if (clen > 0) {
if (strncmp(compat, "sab82532", 8) == 0) {
/* Nope, Siemens serial, not for us. */
return 0;
}
}
return 1;
}
return 0; return 0;
} }
#define SU_PROPSIZE 128 static struct of_device_id su_match[] = {
{
/* .name = "su",
* Scan status structure. },
* "prop" is a local variable but it eats stack to keep it in each {
* stack frame of a recursive procedure. .name = "su_pnp",
*/ },
struct su_probe_scan { {
int msnode, kbnode; /* PROM nodes for mouse and keyboard */ .name = "serial",
int msx, kbx; /* minors for mouse and keyboard */ .compatible = "su",
int devices; /* scan index */ },
char prop[SU_PROPSIZE]; {},
}; };
MODULE_DEVICE_TABLE(of, su_match);
/* static struct of_platform_driver su_driver = {
* We have several platforms which present 'su' in different parts .name = "su",
* of the device tree. 'su' may be found under obio, ebus, isa and pci. .match_table = su_match,
* We walk over the tree and find them wherever PROM hides them. .probe = su_probe,
*/ .remove = __devexit_p(su_remove),
static void __init su_probe_any(struct su_probe_scan *t, int sunode) };
{
struct uart_sunsu_port *up;
int len;
if (t->devices >= UART_NR) static int num_uart;
return;
for (; sunode != 0; sunode = prom_getsibling(sunode)) { static int __init sunsu_init(void)
len = prom_getproperty(sunode, "name", t->prop, SU_PROPSIZE);
if (len <= 1)
continue; /* Broken PROM node */
if (su_node_ok(sunode, t->prop, len)) {
up = &sunsu_ports[t->devices];
if (t->kbnode != 0 && sunode == t->kbnode) {
t->kbx = t->devices;
up->su_type = SU_PORT_KBD;
} else if (t->msnode != 0 && sunode == t->msnode) {
t->msx = t->devices;
up->su_type = SU_PORT_MS;
} else {
#ifdef CONFIG_SPARC64
/*
* Do not attempt to use the truncated
* keyboard/mouse ports as serial ports
* on Ultras with PC keyboard attached.
*/
if (prom_getbool(sunode, "mouse"))
continue;
if (prom_getbool(sunode, "keyboard"))
continue;
#endif
up->su_type = SU_PORT_PORT;
}
up->port_node = sunode;
++t->devices;
} else {
su_probe_any(t, prom_getchild(sunode));
}
}
}
static int __init sunsu_probe(void)
{ {
int node; struct device_node *dp;
int len; int err;
struct su_probe_scan scan;
/*
* First, we scan the tree.
*/
scan.devices = 0;
scan.msx = -1;
scan.kbx = -1;
scan.kbnode = 0;
scan.msnode = 0;
/* num_uart = 0;
* Get the nodes for keyboard and mouse from 'aliases'... for_each_node_by_name(dp, "su") {
*/ if (su_get_type(dp) == SU_PORT_PORT)
node = prom_getchild(prom_root_node); num_uart++;
node = prom_searchsiblings(node, "aliases");
if (node != 0) {
len = prom_getproperty(node, "keyboard", scan.prop, SU_PROPSIZE);
if (len > 0) {
scan.prop[len] = 0;
scan.kbnode = prom_finddevice(scan.prop);
} }
for_each_node_by_name(dp, "su_pnp") {
len = prom_getproperty(node, "mouse", scan.prop, SU_PROPSIZE); if (su_get_type(dp) == SU_PORT_PORT)
if (len > 0) { num_uart++;
scan.prop[len] = 0;
scan.msnode = prom_finddevice(scan.prop);
} }
for_each_node_by_name(dp, "serial") {
if (of_device_is_compatible(dp, "su")) {
if (su_get_type(dp) == SU_PORT_PORT)
num_uart++;
} }
su_probe_any(&scan, prom_getchild(prom_root_node));
/*
* Second, we process the special case of keyboard and mouse.
*
* Currently if we got keyboard and mouse hooked to "su" ports
* we do not use any possible remaining "su" as a serial port.
* Thus, we ignore values of .msx and .kbx, then compact ports.
*/
if (scan.msx != -1 && scan.kbx != -1) {
sunsu_ports[0].su_type = SU_PORT_MS;
sunsu_ports[0].port_node = scan.msnode;
sunsu_kbd_ms_init(&sunsu_ports[0], 0);
sunsu_ports[1].su_type = SU_PORT_KBD;
sunsu_ports[1].port_node = scan.kbnode;
sunsu_kbd_ms_init(&sunsu_ports[1], 1);
return 0;
} }
if (scan.msx != -1 || scan.kbx != -1) { if (num_uart) {
printk("sunsu_probe: cannot match keyboard and mouse, confused\n"); sunsu_reg.minor = sunserial_current_minor;
return -ENODEV; sunsu_reg.nr = num_uart;
err = uart_register_driver(&sunsu_reg);
if (err)
return err;
sunsu_reg.tty_driver->name_base = sunsu_reg.minor - 64;
sunserial_current_minor += num_uart;
sunsu_reg.cons = SUNSU_CONSOLE(num_uart);
} }
if (scan.devices == 0) err = of_register_driver(&su_driver, &of_bus_type);
return -ENODEV; if (err && num_uart)
uart_unregister_driver(&sunsu_reg);
/*
* Console must be initiated after the generic initialization.
*/
sunsu_serial_init();
return 0; return err;
} }
static void __exit sunsu_exit(void) static void __exit sunsu_exit(void)
{ {
int i, saw_uart; if (num_uart)
saw_uart = 0;
for (i = 0; i < UART_NR; i++) {
struct uart_sunsu_port *up = &sunsu_ports[i];
if (up->su_type == SU_PORT_MS ||
up->su_type == SU_PORT_KBD) {
#ifdef CONFIG_SERIO
if (up->serio) {
serio_unregister_port(up->serio);
up->serio = NULL;
}
#endif
} else if (up->port.type != PORT_UNKNOWN) {
uart_remove_one_port(&sunsu_reg, &up->port);
saw_uart++;
}
}
if (saw_uart)
uart_unregister_driver(&sunsu_reg); uart_unregister_driver(&sunsu_reg);
} }
module_init(sunsu_probe); module_init(sunsu_init);
module_exit(sunsu_exit); module_exit(sunsu_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment