at v2.6.27 177 lines 4.6 kB view raw
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#include <linux/of_platform.h> 17 18#include <asm/prom.h> 19 20struct of_serial_info { 21 int type; 22 int line; 23}; 24 25/* 26 * Fill a struct uart_port for a given device node 27 */ 28static int __devinit of_platform_serial_setup(struct of_device *ofdev, 29 int type, struct uart_port *port) 30{ 31 struct resource resource; 32 struct device_node *np = ofdev->node; 33 const unsigned int *clk, *spd; 34 const u32 *prop; 35 int ret, prop_size; 36 37 memset(port, 0, sizeof *port); 38 spd = of_get_property(np, "current-speed", NULL); 39 clk = of_get_property(np, "clock-frequency", NULL); 40 if (!clk) { 41 dev_warn(&ofdev->dev, "no clock-frequency property set\n"); 42 return -ENODEV; 43 } 44 45 ret = of_address_to_resource(np, 0, &resource); 46 if (ret) { 47 dev_warn(&ofdev->dev, "invalid address\n"); 48 return ret; 49 } 50 51 spin_lock_init(&port->lock); 52 port->mapbase = resource.start; 53 54 /* Check for shifted address mapping */ 55 prop = of_get_property(np, "reg-offset", &prop_size); 56 if (prop && (prop_size == sizeof(u32))) 57 port->mapbase += *prop; 58 59 /* Check for registers offset within the devices address range */ 60 prop = of_get_property(np, "reg-shift", &prop_size); 61 if (prop && (prop_size == sizeof(u32))) 62 port->regshift = *prop; 63 64 port->irq = irq_of_parse_and_map(np, 0); 65 port->iotype = UPIO_MEM; 66 port->type = type; 67 port->uartclk = *clk; 68 port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP 69 | UPF_FIXED_PORT; 70 port->dev = &ofdev->dev; 71 /* If current-speed was set, then try not to change it. */ 72 if (spd) 73 port->custom_divisor = *clk / (16 * (*spd)); 74 75 return 0; 76} 77 78/* 79 * Try to register a serial port 80 */ 81static int __devinit of_platform_serial_probe(struct of_device *ofdev, 82 const struct of_device_id *id) 83{ 84 struct of_serial_info *info; 85 struct uart_port port; 86 int port_type; 87 int ret; 88 89 if (of_find_property(ofdev->node, "used-by-rtas", NULL)) 90 return -EBUSY; 91 92 info = kmalloc(sizeof(*info), GFP_KERNEL); 93 if (info == NULL) 94 return -ENOMEM; 95 96 port_type = (unsigned long)id->data; 97 ret = of_platform_serial_setup(ofdev, port_type, &port); 98 if (ret) 99 goto out; 100 101 switch (port_type) { 102 case PORT_8250 ... PORT_MAX_8250: 103 ret = serial8250_register_port(&port); 104 break; 105 default: 106 /* need to add code for these */ 107 case PORT_UNKNOWN: 108 dev_info(&ofdev->dev, "Unknown serial port found, ignored\n"); 109 ret = -ENODEV; 110 break; 111 } 112 if (ret < 0) 113 goto out; 114 115 info->type = port_type; 116 info->line = ret; 117 ofdev->dev.driver_data = info; 118 return 0; 119out: 120 kfree(info); 121 irq_dispose_mapping(port.irq); 122 return ret; 123} 124 125/* 126 * Release a line 127 */ 128static int of_platform_serial_remove(struct of_device *ofdev) 129{ 130 struct of_serial_info *info = ofdev->dev.driver_data; 131 switch (info->type) { 132 case PORT_8250 ... PORT_MAX_8250: 133 serial8250_unregister_port(info->line); 134 break; 135 default: 136 /* need to add code for these */ 137 break; 138 } 139 kfree(info); 140 return 0; 141} 142 143/* 144 * A few common types, add more as needed. 145 */ 146static struct of_device_id __devinitdata of_platform_serial_table[] = { 147 { .type = "serial", .compatible = "ns8250", .data = (void *)PORT_8250, }, 148 { .type = "serial", .compatible = "ns16450", .data = (void *)PORT_16450, }, 149 { .type = "serial", .compatible = "ns16550", .data = (void *)PORT_16550, }, 150 { .type = "serial", .compatible = "ns16750", .data = (void *)PORT_16750, }, 151 { .type = "serial", .data = (void *)PORT_UNKNOWN, }, 152 { /* end of list */ }, 153}; 154 155static struct of_platform_driver of_platform_serial_driver = { 156 .owner = THIS_MODULE, 157 .name = "of_serial", 158 .probe = of_platform_serial_probe, 159 .remove = of_platform_serial_remove, 160 .match_table = of_platform_serial_table, 161}; 162 163static int __init of_platform_serial_init(void) 164{ 165 return of_register_platform_driver(&of_platform_serial_driver); 166} 167module_init(of_platform_serial_init); 168 169static void __exit of_platform_serial_exit(void) 170{ 171 return of_unregister_platform_driver(&of_platform_serial_driver); 172}; 173module_exit(of_platform_serial_exit); 174 175MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>"); 176MODULE_LICENSE("GPL"); 177MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices");