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