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