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