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