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