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 u32 clk, spd, prop; 36 int ret; 37 38 memset(port, 0, sizeof *port); 39 if (of_property_read_u32(np, "clock-frequency", &clk)) { 40 dev_warn(&ofdev->dev, "no clock-frequency property set\n"); 41 return -ENODEV; 42 } 43 /* If current-speed was set, then try not to change it. */ 44 if (of_property_read_u32(np, "current-speed", &spd) == 0) 45 port->custom_divisor = clk / (16 * spd); 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 if (of_property_read_u32(np, "reg-offset", &prop) == 0) 58 port->mapbase += prop; 59 60 /* Check for registers offset within the devices address range */ 61 if (of_property_read_u32(np, "reg-shift", &prop) == 0) 62 port->regshift = prop; 63 64 port->irq = irq_of_parse_and_map(np, 0); 65 port->iotype = UPIO_MEM; 66 if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { 67 switch (prop) { 68 case 1: 69 port->iotype = UPIO_MEM; 70 break; 71 case 4: 72 port->iotype = UPIO_MEM32; 73 break; 74 default: 75 dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n", 76 prop); 77 return -EINVAL; 78 } 79 } 80 81 port->type = type; 82 port->uartclk = clk; 83 port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP 84 | UPF_FIXED_PORT | UPF_FIXED_TYPE; 85 port->dev = &ofdev->dev; 86 87 return 0; 88} 89 90/* 91 * Try to register a serial port 92 */ 93static struct of_device_id of_platform_serial_table[]; 94static int __devinit of_platform_serial_probe(struct platform_device *ofdev) 95{ 96 const struct of_device_id *match; 97 struct of_serial_info *info; 98 struct uart_port port; 99 int port_type; 100 int ret; 101 102 match = of_match_device(of_platform_serial_table, &ofdev->dev); 103 if (!match) 104 return -EINVAL; 105 106 if (of_find_property(ofdev->dev.of_node, "used-by-rtas", NULL)) 107 return -EBUSY; 108 109 info = kmalloc(sizeof(*info), GFP_KERNEL); 110 if (info == NULL) 111 return -ENOMEM; 112 113 port_type = (unsigned long)match->data; 114 ret = of_platform_serial_setup(ofdev, port_type, &port); 115 if (ret) 116 goto out; 117 118 switch (port_type) { 119#ifdef CONFIG_SERIAL_8250 120 case PORT_8250 ... PORT_MAX_8250: 121 ret = serial8250_register_port(&port); 122 break; 123#endif 124#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL 125 case PORT_NWPSERIAL: 126 ret = nwpserial_register_port(&port); 127 break; 128#endif 129 default: 130 /* need to add code for these */ 131 case PORT_UNKNOWN: 132 dev_info(&ofdev->dev, "Unknown serial port found, ignored\n"); 133 ret = -ENODEV; 134 break; 135 } 136 if (ret < 0) 137 goto out; 138 139 info->type = port_type; 140 info->line = ret; 141 dev_set_drvdata(&ofdev->dev, info); 142 return 0; 143out: 144 kfree(info); 145 irq_dispose_mapping(port.irq); 146 return ret; 147} 148 149/* 150 * Release a line 151 */ 152static int of_platform_serial_remove(struct platform_device *ofdev) 153{ 154 struct of_serial_info *info = dev_get_drvdata(&ofdev->dev); 155 switch (info->type) { 156#ifdef CONFIG_SERIAL_8250 157 case PORT_8250 ... PORT_MAX_8250: 158 serial8250_unregister_port(info->line); 159 break; 160#endif 161#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL 162 case PORT_NWPSERIAL: 163 nwpserial_unregister_port(info->line); 164 break; 165#endif 166 default: 167 /* need to add code for these */ 168 break; 169 } 170 kfree(info); 171 return 0; 172} 173 174/* 175 * A few common types, add more as needed. 176 */ 177static struct of_device_id __devinitdata of_platform_serial_table[] = { 178 { .compatible = "ns8250", .data = (void *)PORT_8250, }, 179 { .compatible = "ns16450", .data = (void *)PORT_16450, }, 180 { .compatible = "ns16550a", .data = (void *)PORT_16550A, }, 181 { .compatible = "ns16550", .data = (void *)PORT_16550, }, 182 { .compatible = "ns16750", .data = (void *)PORT_16750, }, 183 { .compatible = "ns16850", .data = (void *)PORT_16850, }, 184 { .compatible = "nvidia,tegra20-uart", .data = (void *)PORT_TEGRA, }, 185#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL 186 { .compatible = "ibm,qpace-nwp-serial", 187 .data = (void *)PORT_NWPSERIAL, }, 188#endif 189 { .type = "serial", .data = (void *)PORT_UNKNOWN, }, 190 { /* end of list */ }, 191}; 192 193static struct platform_driver of_platform_serial_driver = { 194 .driver = { 195 .name = "of_serial", 196 .owner = THIS_MODULE, 197 .of_match_table = of_platform_serial_table, 198 }, 199 .probe = of_platform_serial_probe, 200 .remove = of_platform_serial_remove, 201}; 202 203module_platform_driver(of_platform_serial_driver); 204 205MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>"); 206MODULE_LICENSE("GPL"); 207MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices"); 208