1
2
3
4
5
6
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
26struct aspeed_vuart {
27 struct device *dev;
28 void __iomem *regs;
29 struct clk *clk;
30 int line;
31};
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49static 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
61static 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
79static DEVICE_ATTR_RW(lpc_address);
80
81static 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
94static 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
117static DEVICE_ATTR_RW(sirq);
118
119static struct attribute *aspeed_vuart_attrs[] = {
120 &dev_attr_sirq.attr,
121 &dev_attr_lpc_address.attr,
122 NULL,
123};
124
125static const struct attribute_group aspeed_vuart_attr_group = {
126 .attrs = aspeed_vuart_attrs,
127};
128
129static 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
141static 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
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
157static 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
172static 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
182static 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
234 if (of_property_read_u32(np, "current-speed", &prop) == 0)
235 port.port.custom_divisor = clk / (16 * prop);
236
237
238 if (of_property_read_u32(np, "reg-offset", &prop) == 0)
239 port.port.mapbase += prop;
240
241
242 if (of_property_read_u32(np, "reg-shift", &prop) == 0)
243 port.port.regshift = prop;
244
245
246 if (of_property_read_u32(np, "fifo-size", &prop) == 0)
247 port.port.fifosize = prop;
248
249
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
283err_clk_disable:
284 clk_disable_unprepare(vuart->clk);
285 irq_dispose_mapping(port.port.irq);
286err_sysfs_remove:
287 sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group);
288 return rc;
289}
290
291static 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
303static const struct of_device_id aspeed_vuart_table[] = {
304 { .compatible = "aspeed,ast2400-vuart" },
305 { .compatible = "aspeed,ast2500-vuart" },
306 { },
307};
308
309static 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
318module_platform_driver(aspeed_vuart_driver);
319
320MODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>");
321MODULE_LICENSE("GPL");
322MODULE_DESCRIPTION("Driver for Aspeed VUART device");
323