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 v3.7-rc8 245 lines 6.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/delay.h> 16#include <linux/serial_core.h> 17#include <linux/serial_8250.h> 18#include <linux/serial_reg.h> 19#include <linux/of_address.h> 20#include <linux/of_irq.h> 21#include <linux/of_serial.h> 22#include <linux/of_platform.h> 23#include <linux/nwpserial.h> 24 25struct of_serial_info { 26 int type; 27 int line; 28}; 29 30#ifdef CONFIG_ARCH_TEGRA 31void tegra_serial_handle_break(struct uart_port *p) 32{ 33 unsigned int status, tmout = 10000; 34 35 do { 36 status = p->serial_in(p, UART_LSR); 37 if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) 38 status = p->serial_in(p, UART_RX); 39 else 40 break; 41 if (--tmout == 0) 42 break; 43 udelay(1); 44 } while (1); 45} 46/* FIXME remove this export when tegra finishes conversion to open firmware */ 47EXPORT_SYMBOL_GPL(tegra_serial_handle_break); 48#endif 49 50/* 51 * Fill a struct uart_port for a given device node 52 */ 53static int __devinit of_platform_serial_setup(struct platform_device *ofdev, 54 int type, struct uart_port *port) 55{ 56 struct resource resource; 57 struct device_node *np = ofdev->dev.of_node; 58 u32 clk, spd, prop; 59 int ret; 60 61 memset(port, 0, sizeof *port); 62 if (of_property_read_u32(np, "clock-frequency", &clk)) { 63 dev_warn(&ofdev->dev, "no clock-frequency property set\n"); 64 return -ENODEV; 65 } 66 /* If current-speed was set, then try not to change it. */ 67 if (of_property_read_u32(np, "current-speed", &spd) == 0) 68 port->custom_divisor = clk / (16 * spd); 69 70 ret = of_address_to_resource(np, 0, &resource); 71 if (ret) { 72 dev_warn(&ofdev->dev, "invalid address\n"); 73 return ret; 74 } 75 76 spin_lock_init(&port->lock); 77 port->mapbase = resource.start; 78 79 /* Check for shifted address mapping */ 80 if (of_property_read_u32(np, "reg-offset", &prop) == 0) 81 port->mapbase += prop; 82 83 /* Check for registers offset within the devices address range */ 84 if (of_property_read_u32(np, "reg-shift", &prop) == 0) 85 port->regshift = prop; 86 87 port->irq = irq_of_parse_and_map(np, 0); 88 port->iotype = UPIO_MEM; 89 if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { 90 switch (prop) { 91 case 1: 92 port->iotype = UPIO_MEM; 93 break; 94 case 4: 95 port->iotype = UPIO_MEM32; 96 break; 97 default: 98 dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n", 99 prop); 100 return -EINVAL; 101 } 102 } 103 104 port->type = type; 105 port->uartclk = clk; 106 port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP 107 | UPF_FIXED_PORT | UPF_FIXED_TYPE; 108 109 if (of_find_property(np, "no-loopback-test", NULL)) 110 port->flags |= UPF_SKIP_TEST; 111 112 port->dev = &ofdev->dev; 113 114 if (type == PORT_TEGRA) 115 port->handle_break = tegra_serial_handle_break; 116 117 return 0; 118} 119 120/* 121 * Try to register a serial port 122 */ 123static struct of_device_id of_platform_serial_table[]; 124static int __devinit of_platform_serial_probe(struct platform_device *ofdev) 125{ 126 const struct of_device_id *match; 127 struct of_serial_info *info; 128 struct uart_port port; 129 int port_type; 130 int ret; 131 132 match = of_match_device(of_platform_serial_table, &ofdev->dev); 133 if (!match) 134 return -EINVAL; 135 136 if (of_find_property(ofdev->dev.of_node, "used-by-rtas", NULL)) 137 return -EBUSY; 138 139 info = kmalloc(sizeof(*info), GFP_KERNEL); 140 if (info == NULL) 141 return -ENOMEM; 142 143 port_type = (unsigned long)match->data; 144 ret = of_platform_serial_setup(ofdev, port_type, &port); 145 if (ret) 146 goto out; 147 148 switch (port_type) { 149#ifdef CONFIG_SERIAL_8250 150 case PORT_8250 ... PORT_MAX_8250: 151 { 152 /* For now the of bindings don't support the extra 153 8250 specific bits */ 154 struct uart_8250_port port8250; 155 memset(&port8250, 0, sizeof(port8250)); 156 port8250.port = port; 157 ret = serial8250_register_8250_port(&port8250); 158 break; 159 } 160#endif 161#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL 162 case PORT_NWPSERIAL: 163 ret = nwpserial_register_port(&port); 164 break; 165#endif 166 default: 167 /* need to add code for these */ 168 case PORT_UNKNOWN: 169 dev_info(&ofdev->dev, "Unknown serial port found, ignored\n"); 170 ret = -ENODEV; 171 break; 172 } 173 if (ret < 0) 174 goto out; 175 176 info->type = port_type; 177 info->line = ret; 178 dev_set_drvdata(&ofdev->dev, info); 179 return 0; 180out: 181 kfree(info); 182 irq_dispose_mapping(port.irq); 183 return ret; 184} 185 186/* 187 * Release a line 188 */ 189static int of_platform_serial_remove(struct platform_device *ofdev) 190{ 191 struct of_serial_info *info = dev_get_drvdata(&ofdev->dev); 192 switch (info->type) { 193#ifdef CONFIG_SERIAL_8250 194 case PORT_8250 ... PORT_MAX_8250: 195 serial8250_unregister_port(info->line); 196 break; 197#endif 198#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL 199 case PORT_NWPSERIAL: 200 nwpserial_unregister_port(info->line); 201 break; 202#endif 203 default: 204 /* need to add code for these */ 205 break; 206 } 207 kfree(info); 208 return 0; 209} 210 211/* 212 * A few common types, add more as needed. 213 */ 214static struct of_device_id __devinitdata of_platform_serial_table[] = { 215 { .compatible = "ns8250", .data = (void *)PORT_8250, }, 216 { .compatible = "ns16450", .data = (void *)PORT_16450, }, 217 { .compatible = "ns16550a", .data = (void *)PORT_16550A, }, 218 { .compatible = "ns16550", .data = (void *)PORT_16550, }, 219 { .compatible = "ns16750", .data = (void *)PORT_16750, }, 220 { .compatible = "ns16850", .data = (void *)PORT_16850, }, 221 { .compatible = "nvidia,tegra20-uart", .data = (void *)PORT_TEGRA, }, 222 { .compatible = "nxp,lpc3220-uart", .data = (void *)PORT_LPC3220, }, 223#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL 224 { .compatible = "ibm,qpace-nwp-serial", 225 .data = (void *)PORT_NWPSERIAL, }, 226#endif 227 { .type = "serial", .data = (void *)PORT_UNKNOWN, }, 228 { /* end of list */ }, 229}; 230 231static struct platform_driver of_platform_serial_driver = { 232 .driver = { 233 .name = "of_serial", 234 .owner = THIS_MODULE, 235 .of_match_table = of_platform_serial_table, 236 }, 237 .probe = of_platform_serial_probe, 238 .remove = of_platform_serial_remove, 239}; 240 241module_platform_driver(of_platform_serial_driver); 242 243MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>"); 244MODULE_LICENSE("GPL"); 245MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices");