diff options
Diffstat (limited to 'drivers/serial/uartlite.c')
-rw-r--r-- | drivers/serial/uartlite.c | 286 |
1 files changed, 239 insertions, 47 deletions
diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index f5051cf1a0c8..dfef83f14960 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -1,7 +1,8 @@ /* * uartlite.c: Serial driver for Xilinx uartlite serial controller * - * Peter Korsgaard <jacmet@sunsite.dk> + * Copyright (C) 2006 Peter Korsgaard <jacmet@sunsite.dk> + * Copyright (C) 2007 Secret Lab Technologies Ltd. * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any @@ -17,14 +18,23 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <asm/io.h> +#if defined(CONFIG_OF) +#include <linux/of_device.h> +#include <linux/of_platform.h> +#endif +#define ULITE_NAME "ttyUL" #define ULITE_MAJOR 204 #define ULITE_MINOR 187 #define ULITE_NR_UARTS 4 -/* For register details see datasheet: - http://www.xilinx.com/bvdocs/ipcenter/data_sheet/opb_uartlite.pdf -*/ +/* --------------------------------------------------------------------- + * Register definitions + * + * For register details see datasheet: + * http://www.xilinx.com/bvdocs/ipcenter/data_sheet/opb_uartlite.pdf + */ + #define ULITE_RX 0x00 #define ULITE_TX 0x04 #define ULITE_STATUS 0x08 @@ -46,7 +56,11 @@ #define ULITE_CONTROL_IE 0x10 -static struct uart_port ports[ULITE_NR_UARTS]; +static struct uart_port ulite_ports[ULITE_NR_UARTS]; + +/* --------------------------------------------------------------------- + * Core UART driver operations + */ static int ulite_receive(struct uart_port *port, int stat) { @@ -307,6 +321,10 @@ static struct uart_ops ulite_ops = { .verify_port = ulite_verify_port }; +/* --------------------------------------------------------------------- + * Console driver operations + */ + #ifdef CONFIG_SERIAL_UARTLITE_CONSOLE static void ulite_console_wait_tx(struct uart_port *port) { @@ -329,7 +347,7 @@ static void ulite_console_putchar(struct uart_port *port, int ch) static void ulite_console_write(struct console *co, const char *s, unsigned int count) { - struct uart_port *port = &ports[co->index]; + struct uart_port *port = &ulite_ports[co->index]; unsigned long flags; unsigned int ier; int locked = 1; @@ -355,6 +373,31 @@ static void ulite_console_write(struct console *co, const char *s, spin_unlock_irqrestore(&port->lock, flags); } +#if defined(CONFIG_OF) +static inline void __init ulite_console_of_find_device(int id) +{ + struct device_node *np; + struct resource res; + const unsigned int *of_id; + int rc; + + for_each_compatible_node(np, NULL, "xilinx,uartlite") { + of_id = of_get_property(np, "port-number", NULL); + if ((!of_id) || (*of_id != id)) + continue; + + rc = of_address_to_resource(np, 0, &res); + if (rc) + continue; + + ulite_ports[id].mapbase = res.start; + return; + } +} +#else /* CONFIG_OF */ +static inline void __init ulite_console_of_find_device(int id) { /* do nothing */ } +#endif /* CONFIG_OF */ + static int __init ulite_console_setup(struct console *co, char *options) { struct uart_port *port; @@ -366,11 +409,23 @@ static int __init ulite_console_setup(struct console *co, char *options) if (co->index < 0 || co->index >= ULITE_NR_UARTS) return -EINVAL; - port = &ports[co->index]; + port = &ulite_ports[co->index]; - /* not initialized yet? */ - if (!port->membase) + /* Check if it is an OF device */ + if (!port->mapbase) + ulite_console_of_find_device(co->index); + + /* Do we have a device now? */ + if (!port->mapbase) { + pr_debug("console on ttyUL%i not present\n", co->index); return -ENODEV; + } + + /* not initialized yet? */ + if (!port->membase) { + if (ulite_request_port(port)) + return -ENODEV; + } if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); @@ -381,7 +436,7 @@ static int __init ulite_console_setup(struct console *co, char *options) static struct uart_driver ulite_uart_driver; static struct console ulite_console = { - .name = "ttyUL", + .name = ULITE_NAME, .write = ulite_console_write, .device = uart_console_device, .setup = ulite_console_setup, @@ -403,7 +458,7 @@ console_initcall(ulite_console_init); static struct uart_driver ulite_uart_driver = { .owner = THIS_MODULE, .driver_name = "uartlite", - .dev_name = "ttyUL", + .dev_name = ULITE_NAME, .major = ULITE_MAJOR, .minor = ULITE_MINOR, .nr = ULITE_NR_UARTS, @@ -412,59 +467,111 @@ static struct uart_driver ulite_uart_driver = { #endif }; -static int __devinit ulite_probe(struct platform_device *pdev) +/* --------------------------------------------------------------------- + * Port assignment functions (mapping devices to uart_port structures) + */ + +/** ulite_assign: register a uartlite device with the driver + * + * @dev: pointer to device structure + * @id: requested id number. Pass -1 for automatic port assignment + * @base: base address of uartlite registers + * @irq: irq number for uartlite + * + * Returns: 0 on success, <0 otherwise + */ +static int __devinit ulite_assign(struct device *dev, int id, u32 base, int irq) { - struct resource *res, *res2; struct uart_port *port; + int rc; - if (pdev->id < 0 || pdev->id >= ULITE_NR_UARTS) + /* if id = -1; then scan for a free id and use that */ + if (id < 0) { + for (id = 0; id < ULITE_NR_UARTS; id++) + if (ulite_ports[id].mapbase == 0) + break; + } + if (id < 0 || id >= ULITE_NR_UARTS) { + dev_err(dev, "%s%i too large\n", ULITE_NAME, id); return -EINVAL; + } - if (ports[pdev->id].membase) + if ((ulite_ports[id].mapbase) && (ulite_ports[id].mapbase != base)) { + dev_err(dev, "cannot assign to %s%i; it is already in use\n", + ULITE_NAME, id); return -EBUSY; + } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; + port = &ulite_ports[id]; - res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res2) - return -ENODEV; + spin_lock_init(&port->lock); + port->fifosize = 16; + port->regshift = 2; + port->iotype = UPIO_MEM; + port->iobase = 1; /* mark port in use */ + port->mapbase = base; + port->membase = NULL; + port->ops = &ulite_ops; + port->irq = irq; + port->flags = UPF_BOOT_AUTOCONF; + port->dev = dev; + port->type = PORT_UNKNOWN; + port->line = id; + + dev_set_drvdata(dev, port); + + /* Register the port */ + rc = uart_add_one_port(&ulite_uart_driver, port); + if (rc) { + dev_err(dev, "uart_add_one_port() failed; err=%i\n", rc); + port->mapbase = 0; + dev_set_drvdata(dev, NULL); + return rc; + } - port = &ports[pdev->id]; + return 0; +} - port->fifosize = 16; - port->regshift = 2; - port->iotype = UPIO_MEM; - port->iobase = 1; /* mark port in use */ - port->mapbase = res->start; - port->membase = NULL; - port->ops = &ulite_ops; - port->irq = res2->start; - port->flags = UPF_BOOT_AUTOCONF; - port->dev = &pdev->dev; - port->type = PORT_UNKNOWN; - port->line = pdev->id; +/** ulite_release: register a uartlite device with the driver + * + * @dev: pointer to device structure + */ +static int __devinit ulite_release(struct device *dev) +{ + struct uart_port *port = dev_get_drvdata(dev); + int rc = 0; - uart_add_one_port(&ulite_uart_driver, port); - platform_set_drvdata(pdev, port); + if (port) { + rc = uart_remove_one_port(&ulite_uart_driver, port); + dev_set_drvdata(dev, NULL); + port->mapbase = 0; + } - return 0; + return rc; } -static int ulite_remove(struct platform_device *pdev) +/* --------------------------------------------------------------------- + * Platform bus binding + */ + +static int __devinit ulite_probe(struct platform_device *pdev) { - struct uart_port *port = platform_get_drvdata(pdev); + struct resource *res, *res2; - platform_set_drvdata(pdev, NULL); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; - if (port) - uart_remove_one_port(&ulite_uart_driver, port); + res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res2) + return -ENODEV; - /* mark port as free */ - port->membase = NULL; + return ulite_assign(&pdev->dev, pdev->id, res->start, res2->start); +} - return 0; +static int ulite_remove(struct platform_device *pdev) +{ + return ulite_release(&pdev->dev); } static struct platform_driver ulite_platform_driver = { @@ -476,24 +583,109 @@ static struct platform_driver ulite_platform_driver = { }, }; +/* --------------------------------------------------------------------- + * OF bus bindings + */ +#if defined(CONFIG_OF) +static int __devinit +ulite_of_probe(struct of_device *op, const struct of_device_id *match) +{ + struct resource res; + const unsigned int *id; + int irq, rc; + + dev_dbg(&op->dev, "%s(%p, %p)\n", __FUNCTION__, op, match); + + rc = of_address_to_resource(op->node, 0, &res); + if (rc) { + dev_err(&op->dev, "invalid address\n"); + return rc; + } + + irq = irq_of_parse_and_map(op->node, 0); + + id = of_get_property(op->node, "port-number", NULL); + + return ulite_assign(&op->dev, id ? *id : -1, res.start+3, irq); +} + +static int __devexit ulite_of_remove(struct of_device *op) +{ + return ulite_release(&op->dev); +} + +/* Match table for of_platform binding */ +static struct of_device_id __devinit ulite_of_match[] = { + { .type = "serial", .compatible = "xilinx,uartlite", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ulite_of_match); + +static struct of_platform_driver ulite_of_driver = { + .owner = THIS_MODULE, + .name = "uartlite", + .match_table = ulite_of_match, + .probe = ulite_of_probe, + .remove = __devexit_p(ulite_of_remove), + .driver = { + .name = "uartlite", + }, +}; + +/* Registration helpers to keep the number of #ifdefs to a minimum */ +static inline int __init ulite_of_register(void) +{ + pr_debug("uartlite: calling of_register_platform_driver()\n"); + return of_register_platform_driver(&ulite_of_driver); +} + +static inline void __exit ulite_of_unregister(void) +{ + of_unregister_platform_driver(&ulite_of_driver); +} +#else /* CONFIG_OF */ +/* CONFIG_OF not enabled; do nothing helpers */ +static inline int __init ulite_of_register(void) { return 0; } +static inline void __exit ulite_of_unregister(void) { } +#endif /* CONFIG_OF */ + +/* --------------------------------------------------------------------- + * Module setup/teardown + */ + int __init ulite_init(void) { int ret; + pr_debug("uartlite: calling uart_register_driver()\n"); ret = uart_register_driver(&ulite_uart_driver); if (ret) - return ret; + goto err_uart; + ret = ulite_of_register(); + if (ret) + goto err_of; + + pr_debug("uartlite: calling platform_driver_register()\n"); ret = platform_driver_register(&ulite_platform_driver); if (ret) - uart_unregister_driver(&ulite_uart_driver); + goto err_plat; + return 0; + +err_plat: + ulite_of_unregister(); +err_of: + uart_unregister_driver(&ulite_uart_driver); +err_uart: + printk(KERN_ERR "registering uartlite driver failed: err=%i", ret); return ret; } void __exit ulite_exit(void) { platform_driver_unregister(&ulite_platform_driver); + ulite_of_unregister(); uart_unregister_driver(&ulite_uart_driver); } |