Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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