1 /* 2 * Serial Port driver for Aspeed VUART device 3 * 4 * Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp. 5 * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 10 * 2 of the License, or (at your option) any later version. 11 */ 12 #include <linux/device.h> 13 #include <linux/module.h> 14 #include <linux/of_address.h> 15 #include <linux/of_irq.h> 16 #include <linux/of_platform.h> 17 #include <linux/clk.h> 18 19 #include "8250.h" 20 21 #define ASPEED_VUART_GCRA 0x20 22 #define ASPEED_VUART_GCRA_VUART_EN BIT(0) 23 #define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5) 24 #define ASPEED_VUART_GCRB 0x24 25 #define ASPEED_VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4) 26 #define ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT 4 27 #define ASPEED_VUART_ADDRL 0x28 28 #define ASPEED_VUART_ADDRH 0x2c 29 30 struct aspeed_vuart { 31 struct device *dev; 32 void __iomem *regs; 33 struct clk *clk; 34 int line; 35 }; 36 37 /* 38 * The VUART is basically two UART 'front ends' connected by their FIFO 39 * (no actual serial line in between). One is on the BMC side (management 40 * controller) and one is on the host CPU side. 41 * 42 * It allows the BMC to provide to the host a "UART" that pipes into 43 * the BMC itself and can then be turned by the BMC into a network console 44 * of some sort for example. 45 * 46 * This driver is for the BMC side. The sysfs files allow the BMC 47 * userspace which owns the system configuration policy, to specify 48 * at what IO port and interrupt number the host side will appear 49 * to the host on the Host <-> BMC LPC bus. It could be different on a 50 * different system (though most of them use 3f8/4). 51 */ 52 53 static ssize_t lpc_address_show(struct device *dev, 54 struct device_attribute *attr, char *buf) 55 { 56 struct aspeed_vuart *vuart = dev_get_drvdata(dev); 57 u16 addr; 58 59 addr = (readb(vuart->regs + ASPEED_VUART_ADDRH) << 8) | 60 (readb(vuart->regs + ASPEED_VUART_ADDRL)); 61 62 return snprintf(buf, PAGE_SIZE - 1, "0x%x\n", addr); 63 } 64 65 static ssize_t lpc_address_store(struct device *dev, 66 struct device_attribute *attr, 67 const char *buf, size_t count) 68 { 69 struct aspeed_vuart *vuart = dev_get_drvdata(dev); 70 unsigned long val; 71 int err; 72 73 err = kstrtoul(buf, 0, &val); 74 if (err) 75 return err; 76 77 writeb(val >> 8, vuart->regs + ASPEED_VUART_ADDRH); 78 writeb(val >> 0, vuart->regs + ASPEED_VUART_ADDRL); 79 80 return count; 81 } 82 83 static DEVICE_ATTR_RW(lpc_address); 84 85 static ssize_t sirq_show(struct device *dev, 86 struct device_attribute *attr, char *buf) 87 { 88 struct aspeed_vuart *vuart = dev_get_drvdata(dev); 89 u8 reg; 90 91 reg = readb(vuart->regs + ASPEED_VUART_GCRB); 92 reg &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; 93 reg >>= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; 94 95 return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg); 96 } 97 98 static ssize_t sirq_store(struct device *dev, struct device_attribute *attr, 99 const char *buf, size_t count) 100 { 101 struct aspeed_vuart *vuart = dev_get_drvdata(dev); 102 unsigned long val; 103 int err; 104 u8 reg; 105 106 err = kstrtoul(buf, 0, &val); 107 if (err) 108 return err; 109 110 val <<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; 111 val &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; 112 113 reg = readb(vuart->regs + ASPEED_VUART_GCRB); 114 reg &= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK; 115 reg |= val; 116 writeb(reg, vuart->regs + ASPEED_VUART_GCRB); 117 118 return count; 119 } 120 121 static DEVICE_ATTR_RW(sirq); 122 123 static struct attribute *aspeed_vuart_attrs[] = { 124 &dev_attr_sirq.attr, 125 &dev_attr_lpc_address.attr, 126 NULL, 127 }; 128 129 static const struct attribute_group aspeed_vuart_attr_group = { 130 .attrs = aspeed_vuart_attrs, 131 }; 132 133 static void aspeed_vuart_set_enabled(struct aspeed_vuart *vuart, bool enabled) 134 { 135 u8 reg = readb(vuart->regs + ASPEED_VUART_GCRA); 136 137 if (enabled) 138 reg |= ASPEED_VUART_GCRA_VUART_EN; 139 else 140 reg &= ~ASPEED_VUART_GCRA_VUART_EN; 141 142 writeb(reg, vuart->regs + ASPEED_VUART_GCRA); 143 } 144 145 static void aspeed_vuart_set_host_tx_discard(struct aspeed_vuart *vuart, 146 bool discard) 147 { 148 u8 reg; 149 150 reg = readb(vuart->regs + ASPEED_VUART_GCRA); 151 152 /* If the DISABLE_HOST_TX_DISCARD bit is set, discard is disabled */ 153 if (!discard) 154 reg |= ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD; 155 else 156 reg &= ~ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD; 157 158 writeb(reg, vuart->regs + ASPEED_VUART_GCRA); 159 } 160 161 static int aspeed_vuart_startup(struct uart_port *uart_port) 162 { 163 struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port); 164 struct aspeed_vuart *vuart = uart_8250_port->port.private_data; 165 int rc; 166 167 rc = serial8250_do_startup(uart_port); 168 if (rc) 169 return rc; 170 171 aspeed_vuart_set_host_tx_discard(vuart, false); 172 173 return 0; 174 } 175 176 static void aspeed_vuart_shutdown(struct uart_port *uart_port) 177 { 178 struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port); 179 struct aspeed_vuart *vuart = uart_8250_port->port.private_data; 180 181 aspeed_vuart_set_host_tx_discard(vuart, true); 182 183 serial8250_do_shutdown(uart_port); 184 } 185 186 static int aspeed_vuart_probe(struct platform_device *pdev) 187 { 188 struct uart_8250_port port; 189 struct aspeed_vuart *vuart; 190 struct device_node *np; 191 struct resource *res; 192 u32 clk, prop; 193 int rc; 194 195 np = pdev->dev.of_node; 196 197 vuart = devm_kzalloc(&pdev->dev, sizeof(*vuart), GFP_KERNEL); 198 if (!vuart) 199 return -ENOMEM; 200 201 vuart->dev = &pdev->dev; 202 203 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 204 vuart->regs = devm_ioremap_resource(&pdev->dev, res); 205 if (IS_ERR(vuart->regs)) 206 return PTR_ERR(vuart->regs); 207 208 memset(&port, 0, sizeof(port)); 209 port.port.private_data = vuart; 210 port.port.membase = vuart->regs; 211 port.port.mapbase = res->start; 212 port.port.mapsize = resource_size(res); 213 port.port.startup = aspeed_vuart_startup; 214 port.port.shutdown = aspeed_vuart_shutdown; 215 port.port.dev = &pdev->dev; 216 217 rc = sysfs_create_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); 218 if (rc < 0) 219 return rc; 220 221 if (of_property_read_u32(np, "clock-frequency", &clk)) { 222 vuart->clk = devm_clk_get(&pdev->dev, NULL); 223 if (IS_ERR(vuart->clk)) { 224 dev_warn(&pdev->dev, 225 "clk or clock-frequency not defined\n"); 226 rc = PTR_ERR(vuart->clk); 227 goto err_sysfs_remove; 228 } 229 230 rc = clk_prepare_enable(vuart->clk); 231 if (rc < 0) 232 goto err_sysfs_remove; 233 234 clk = clk_get_rate(vuart->clk); 235 } 236 237 /* If current-speed was set, then try not to change it. */ 238 if (of_property_read_u32(np, "current-speed", &prop) == 0) 239 port.port.custom_divisor = clk / (16 * prop); 240 241 /* Check for shifted address mapping */ 242 if (of_property_read_u32(np, "reg-offset", &prop) == 0) 243 port.port.mapbase += prop; 244 245 /* Check for registers offset within the devices address range */ 246 if (of_property_read_u32(np, "reg-shift", &prop) == 0) 247 port.port.regshift = prop; 248 249 /* Check for fifo size */ 250 if (of_property_read_u32(np, "fifo-size", &prop) == 0) 251 port.port.fifosize = prop; 252 253 /* Check for a fixed line number */ 254 rc = of_alias_get_id(np, "serial"); 255 if (rc >= 0) 256 port.port.line = rc; 257 258 port.port.irq = irq_of_parse_and_map(np, 0); 259 port.port.irqflags = IRQF_SHARED; 260 port.port.iotype = UPIO_MEM; 261 port.port.type = PORT_16550A; 262 port.port.uartclk = clk; 263 port.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF 264 | UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_NO_THRE_TEST; 265 266 if (of_property_read_bool(np, "no-loopback-test")) 267 port.port.flags |= UPF_SKIP_TEST; 268 269 if (port.port.fifosize) 270 port.capabilities = UART_CAP_FIFO; 271 272 if (of_property_read_bool(np, "auto-flow-control")) 273 port.capabilities |= UART_CAP_AFE; 274 275 rc = serial8250_register_8250_port(&port); 276 if (rc < 0) 277 goto err_clk_disable; 278 279 vuart->line = rc; 280 281 aspeed_vuart_set_enabled(vuart, true); 282 aspeed_vuart_set_host_tx_discard(vuart, true); 283 platform_set_drvdata(pdev, vuart); 284 285 return 0; 286 287 err_clk_disable: 288 clk_disable_unprepare(vuart->clk); 289 irq_dispose_mapping(port.port.irq); 290 err_sysfs_remove: 291 sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); 292 return rc; 293 } 294 295 static int aspeed_vuart_remove(struct platform_device *pdev) 296 { 297 struct aspeed_vuart *vuart = platform_get_drvdata(pdev); 298 299 aspeed_vuart_set_enabled(vuart, false); 300 serial8250_unregister_port(vuart->line); 301 sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); 302 clk_disable_unprepare(vuart->clk); 303 304 return 0; 305 } 306 307 static const struct of_device_id aspeed_vuart_table[] = { 308 { .compatible = "aspeed,ast2400-vuart" }, 309 { .compatible = "aspeed,ast2500-vuart" }, 310 { }, 311 }; 312 313 static struct platform_driver aspeed_vuart_driver = { 314 .driver = { 315 .name = "aspeed-vuart", 316 .of_match_table = aspeed_vuart_table, 317 }, 318 .probe = aspeed_vuart_probe, 319 .remove = aspeed_vuart_remove, 320 }; 321 322 module_platform_driver(aspeed_vuart_driver); 323 324 MODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>"); 325 MODULE_LICENSE("GPL"); 326 MODULE_DESCRIPTION("Driver for Aspeed VUART device"); 327