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