Commit | Line | Data |
---|---|---|
e3b3d0f5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
7fbcf3af JK |
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. | |
7fbcf3af JK |
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> | |
8d310c91 OS |
13 | #include <linux/regmap.h> |
14 | #include <linux/mfd/syscon.h> | |
5909c0bf JK |
15 | #include <linux/tty.h> |
16 | #include <linux/tty_flip.h> | |
7fbcf3af JK |
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) | |
8d310c91 | 23 | #define ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY BIT(1) |
7fbcf3af JK |
24 | #define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5) |
25 | #define ASPEED_VUART_GCRB 0x24 | |
26 | #define ASPEED_VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4) | |
27 | #define ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT 4 | |
28 | #define ASPEED_VUART_ADDRL 0x28 | |
29 | #define ASPEED_VUART_ADDRH 0x2c | |
30 | ||
ca03042f ZW |
31 | #define ASPEED_VUART_DEFAULT_LPC_ADDR 0x3f8 |
32 | #define ASPEED_VUART_DEFAULT_SIRQ 4 | |
33 | #define ASPEED_VUART_DEFAULT_SIRQ_POLARITY IRQ_TYPE_LEVEL_LOW | |
34 | ||
7fbcf3af JK |
35 | struct aspeed_vuart { |
36 | struct device *dev; | |
7fbcf3af | 37 | int line; |
5909c0bf JK |
38 | struct timer_list unthrottle_timer; |
39 | struct uart_8250_port *port; | |
7fbcf3af JK |
40 | }; |
41 | ||
5909c0bf JK |
42 | /* |
43 | * If we fill the tty flip buffers, we throttle the data ready interrupt | |
44 | * to prevent dropped characters. This timeout defines how long we wait | |
45 | * to (conditionally, depending on buffer state) unthrottle. | |
46 | */ | |
47 | static const int unthrottle_timeout = HZ/10; | |
48 | ||
7fbcf3af JK |
49 | /* |
50 | * The VUART is basically two UART 'front ends' connected by their FIFO | |
51 | * (no actual serial line in between). One is on the BMC side (management | |
52 | * controller) and one is on the host CPU side. | |
53 | * | |
54 | * It allows the BMC to provide to the host a "UART" that pipes into | |
55 | * the BMC itself and can then be turned by the BMC into a network console | |
56 | * of some sort for example. | |
57 | * | |
58 | * This driver is for the BMC side. The sysfs files allow the BMC | |
59 | * userspace which owns the system configuration policy, to specify | |
60 | * at what IO port and interrupt number the host side will appear | |
61 | * to the host on the Host <-> BMC LPC bus. It could be different on a | |
62 | * different system (though most of them use 3f8/4). | |
63 | */ | |
64 | ||
991a350d ZW |
65 | static inline u8 aspeed_vuart_readb(struct aspeed_vuart *vuart, u8 reg) |
66 | { | |
54da3e38 | 67 | return readb(vuart->port->port.membase + reg); |
991a350d ZW |
68 | } |
69 | ||
70 | static inline void aspeed_vuart_writeb(struct aspeed_vuart *vuart, u8 val, u8 reg) | |
71 | { | |
54da3e38 | 72 | writeb(val, vuart->port->port.membase + reg); |
991a350d ZW |
73 | } |
74 | ||
7fbcf3af JK |
75 | static ssize_t lpc_address_show(struct device *dev, |
76 | struct device_attribute *attr, char *buf) | |
77 | { | |
78 | struct aspeed_vuart *vuart = dev_get_drvdata(dev); | |
79 | u16 addr; | |
80 | ||
991a350d ZW |
81 | addr = (aspeed_vuart_readb(vuart, ASPEED_VUART_ADDRH) << 8) | |
82 | (aspeed_vuart_readb(vuart, ASPEED_VUART_ADDRL)); | |
7fbcf3af | 83 | |
c5e453f9 | 84 | return sysfs_emit(buf, "0x%x\n", addr); |
7fbcf3af JK |
85 | } |
86 | ||
3b44af4f ZW |
87 | static int aspeed_vuart_set_lpc_address(struct aspeed_vuart *vuart, u32 addr) |
88 | { | |
89 | if (addr > U16_MAX) | |
90 | return -EINVAL; | |
91 | ||
991a350d ZW |
92 | aspeed_vuart_writeb(vuart, addr >> 8, ASPEED_VUART_ADDRH); |
93 | aspeed_vuart_writeb(vuart, addr >> 0, ASPEED_VUART_ADDRL); | |
3b44af4f ZW |
94 | |
95 | return 0; | |
96 | } | |
97 | ||
7fbcf3af JK |
98 | static ssize_t lpc_address_store(struct device *dev, |
99 | struct device_attribute *attr, | |
100 | const char *buf, size_t count) | |
101 | { | |
102 | struct aspeed_vuart *vuart = dev_get_drvdata(dev); | |
3b44af4f | 103 | u32 val; |
7fbcf3af JK |
104 | int err; |
105 | ||
3b44af4f | 106 | err = kstrtou32(buf, 0, &val); |
7fbcf3af JK |
107 | if (err) |
108 | return err; | |
109 | ||
3b44af4f ZW |
110 | err = aspeed_vuart_set_lpc_address(vuart, val); |
111 | return err ? : count; | |
7fbcf3af JK |
112 | } |
113 | ||
114 | static DEVICE_ATTR_RW(lpc_address); | |
115 | ||
116 | static ssize_t sirq_show(struct device *dev, | |
117 | struct device_attribute *attr, char *buf) | |
118 | { | |
119 | struct aspeed_vuart *vuart = dev_get_drvdata(dev); | |
120 | u8 reg; | |
121 | ||
991a350d | 122 | reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRB); |
7fbcf3af JK |
123 | reg &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; |
124 | reg >>= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; | |
125 | ||
c5e453f9 | 126 | return sysfs_emit(buf, "%u\n", reg); |
7fbcf3af JK |
127 | } |
128 | ||
3b44af4f ZW |
129 | static int aspeed_vuart_set_sirq(struct aspeed_vuart *vuart, u32 sirq) |
130 | { | |
131 | u8 reg; | |
132 | ||
133 | if (sirq > (ASPEED_VUART_GCRB_HOST_SIRQ_MASK >> ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT)) | |
134 | return -EINVAL; | |
135 | ||
136 | sirq <<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; | |
137 | sirq &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; | |
138 | ||
991a350d | 139 | reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRB); |
3b44af4f ZW |
140 | reg &= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK; |
141 | reg |= sirq; | |
991a350d | 142 | aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRB); |
3b44af4f ZW |
143 | |
144 | return 0; | |
145 | } | |
146 | ||
7fbcf3af JK |
147 | static ssize_t sirq_store(struct device *dev, struct device_attribute *attr, |
148 | const char *buf, size_t count) | |
149 | { | |
150 | struct aspeed_vuart *vuart = dev_get_drvdata(dev); | |
151 | unsigned long val; | |
152 | int err; | |
7fbcf3af JK |
153 | |
154 | err = kstrtoul(buf, 0, &val); | |
155 | if (err) | |
156 | return err; | |
157 | ||
3b44af4f ZW |
158 | err = aspeed_vuart_set_sirq(vuart, val); |
159 | return err ? : count; | |
7fbcf3af JK |
160 | } |
161 | ||
162 | static DEVICE_ATTR_RW(sirq); | |
163 | ||
8d310c91 OS |
164 | static ssize_t sirq_polarity_show(struct device *dev, |
165 | struct device_attribute *attr, char *buf) | |
166 | { | |
167 | struct aspeed_vuart *vuart = dev_get_drvdata(dev); | |
168 | u8 reg; | |
169 | ||
991a350d | 170 | reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA); |
8d310c91 OS |
171 | reg &= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY; |
172 | ||
c5e453f9 | 173 | return sysfs_emit(buf, "%u\n", reg ? 1 : 0); |
8d310c91 OS |
174 | } |
175 | ||
176 | static void aspeed_vuart_set_sirq_polarity(struct aspeed_vuart *vuart, | |
177 | bool polarity) | |
178 | { | |
991a350d | 179 | u8 reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA); |
8d310c91 OS |
180 | |
181 | if (polarity) | |
182 | reg |= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY; | |
183 | else | |
184 | reg &= ~ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY; | |
185 | ||
991a350d | 186 | aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRA); |
8d310c91 OS |
187 | } |
188 | ||
189 | static ssize_t sirq_polarity_store(struct device *dev, | |
190 | struct device_attribute *attr, | |
191 | const char *buf, size_t count) | |
192 | { | |
193 | struct aspeed_vuart *vuart = dev_get_drvdata(dev); | |
194 | unsigned long val; | |
195 | int err; | |
196 | ||
197 | err = kstrtoul(buf, 0, &val); | |
198 | if (err) | |
199 | return err; | |
200 | ||
201 | aspeed_vuart_set_sirq_polarity(vuart, val != 0); | |
202 | ||
203 | return count; | |
204 | } | |
205 | ||
206 | static DEVICE_ATTR_RW(sirq_polarity); | |
207 | ||
7fbcf3af JK |
208 | static struct attribute *aspeed_vuart_attrs[] = { |
209 | &dev_attr_sirq.attr, | |
8d310c91 | 210 | &dev_attr_sirq_polarity.attr, |
7fbcf3af JK |
211 | &dev_attr_lpc_address.attr, |
212 | NULL, | |
213 | }; | |
214 | ||
215 | static const struct attribute_group aspeed_vuart_attr_group = { | |
216 | .attrs = aspeed_vuart_attrs, | |
217 | }; | |
218 | ||
219 | static void aspeed_vuart_set_enabled(struct aspeed_vuart *vuart, bool enabled) | |
220 | { | |
991a350d | 221 | u8 reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA); |
7fbcf3af JK |
222 | |
223 | if (enabled) | |
224 | reg |= ASPEED_VUART_GCRA_VUART_EN; | |
225 | else | |
226 | reg &= ~ASPEED_VUART_GCRA_VUART_EN; | |
227 | ||
991a350d | 228 | aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRA); |
7fbcf3af JK |
229 | } |
230 | ||
231 | static void aspeed_vuart_set_host_tx_discard(struct aspeed_vuart *vuart, | |
232 | bool discard) | |
233 | { | |
234 | u8 reg; | |
235 | ||
991a350d | 236 | reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA); |
7fbcf3af JK |
237 | |
238 | /* If the DISABLE_HOST_TX_DISCARD bit is set, discard is disabled */ | |
239 | if (!discard) | |
240 | reg |= ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD; | |
241 | else | |
242 | reg &= ~ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD; | |
243 | ||
991a350d | 244 | aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRA); |
7fbcf3af JK |
245 | } |
246 | ||
247 | static int aspeed_vuart_startup(struct uart_port *uart_port) | |
248 | { | |
249 | struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port); | |
250 | struct aspeed_vuart *vuart = uart_8250_port->port.private_data; | |
251 | int rc; | |
252 | ||
253 | rc = serial8250_do_startup(uart_port); | |
254 | if (rc) | |
255 | return rc; | |
256 | ||
257 | aspeed_vuart_set_host_tx_discard(vuart, false); | |
258 | ||
259 | return 0; | |
260 | } | |
261 | ||
262 | static void aspeed_vuart_shutdown(struct uart_port *uart_port) | |
263 | { | |
264 | struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port); | |
265 | struct aspeed_vuart *vuart = uart_8250_port->port.private_data; | |
266 | ||
267 | aspeed_vuart_set_host_tx_discard(vuart, true); | |
268 | ||
269 | serial8250_do_shutdown(uart_port); | |
270 | } | |
271 | ||
5909c0bf JK |
272 | static void __aspeed_vuart_set_throttle(struct uart_8250_port *up, |
273 | bool throttle) | |
989983ea JK |
274 | { |
275 | unsigned char irqs = UART_IER_RLSI | UART_IER_RDI; | |
989983ea | 276 | |
d0b309a5 JO |
277 | /* Port locked to synchronize UART_IER access against the console. */ |
278 | lockdep_assert_held_once(&up->port.lock); | |
279 | ||
989983ea JK |
280 | up->ier &= ~irqs; |
281 | if (!throttle) | |
282 | up->ier |= irqs; | |
283 | serial_out(up, UART_IER, up->ier); | |
5909c0bf JK |
284 | } |
285 | static void aspeed_vuart_set_throttle(struct uart_port *port, bool throttle) | |
286 | { | |
287 | struct uart_8250_port *up = up_to_u8250p(port); | |
288 | unsigned long flags; | |
289 | ||
40c06912 | 290 | uart_port_lock_irqsave(port, &flags); |
5909c0bf | 291 | __aspeed_vuart_set_throttle(up, throttle); |
40c06912 | 292 | uart_port_unlock_irqrestore(port, flags); |
989983ea JK |
293 | } |
294 | ||
295 | static void aspeed_vuart_throttle(struct uart_port *port) | |
296 | { | |
297 | aspeed_vuart_set_throttle(port, true); | |
298 | } | |
299 | ||
300 | static void aspeed_vuart_unthrottle(struct uart_port *port) | |
301 | { | |
302 | aspeed_vuart_set_throttle(port, false); | |
303 | } | |
304 | ||
5909c0bf JK |
305 | static void aspeed_vuart_unthrottle_exp(struct timer_list *timer) |
306 | { | |
307 | struct aspeed_vuart *vuart = from_timer(vuart, timer, unthrottle_timer); | |
308 | struct uart_8250_port *up = vuart->port; | |
309 | ||
310 | if (!tty_buffer_space_avail(&up->port.state->port)) { | |
a451debb DC |
311 | mod_timer(&vuart->unthrottle_timer, |
312 | jiffies + unthrottle_timeout); | |
5909c0bf JK |
313 | return; |
314 | } | |
315 | ||
316 | aspeed_vuart_unthrottle(&up->port); | |
317 | } | |
318 | ||
319 | /* | |
320 | * Custom interrupt handler to manage finer-grained flow control. Although we | |
321 | * have throttle/unthrottle callbacks, we've seen that the VUART device can | |
322 | * deliver characters faster than the ldisc has a chance to check buffer space | |
323 | * against the throttle threshold. This results in dropped characters before | |
324 | * the throttle. | |
325 | * | |
326 | * We do this by checking for flip buffer space before RX. If we have no space, | |
327 | * throttle now and schedule an unthrottle for later, once the ldisc has had | |
328 | * a chance to drain the buffers. | |
329 | */ | |
330 | static int aspeed_vuart_handle_irq(struct uart_port *port) | |
331 | { | |
332 | struct uart_8250_port *up = up_to_u8250p(port); | |
333 | unsigned int iir, lsr; | |
853a9ae2 | 334 | unsigned long flags; |
9a33fbf9 | 335 | unsigned int space, count; |
5909c0bf JK |
336 | |
337 | iir = serial_port_in(port, UART_IIR); | |
338 | ||
339 | if (iir & UART_IIR_NO_INT) | |
340 | return 0; | |
341 | ||
40c06912 | 342 | uart_port_lock_irqsave(port, &flags); |
5909c0bf JK |
343 | |
344 | lsr = serial_port_in(port, UART_LSR); | |
345 | ||
346 | if (lsr & (UART_LSR_DR | UART_LSR_BI)) { | |
347 | space = tty_buffer_space_avail(&port->state->port); | |
348 | ||
349 | if (!space) { | |
350 | /* throttle and schedule an unthrottle later */ | |
351 | struct aspeed_vuart *vuart = port->private_data; | |
352 | __aspeed_vuart_set_throttle(up, true); | |
353 | ||
c9805fbf | 354 | if (!timer_pending(&vuart->unthrottle_timer)) |
5909c0bf | 355 | mod_timer(&vuart->unthrottle_timer, |
a451debb | 356 | jiffies + unthrottle_timeout); |
5909c0bf JK |
357 | |
358 | } else { | |
9a33fbf9 | 359 | count = min(space, 256U); |
5909c0bf JK |
360 | |
361 | do { | |
362 | serial8250_read_char(up, lsr); | |
363 | lsr = serial_in(up, UART_LSR); | |
364 | if (--count == 0) | |
365 | break; | |
366 | } while (lsr & (UART_LSR_DR | UART_LSR_BI)); | |
367 | ||
368 | tty_flip_buffer_push(&port->state->port); | |
369 | } | |
370 | } | |
371 | ||
372 | serial8250_modem_status(up); | |
373 | if (lsr & UART_LSR_THRE) | |
374 | serial8250_tx_chars(up); | |
375 | ||
853a9ae2 | 376 | uart_unlock_and_check_sysrq_irqrestore(port, flags); |
5909c0bf JK |
377 | |
378 | return 1; | |
379 | } | |
380 | ||
8d310c91 OS |
381 | static void aspeed_vuart_auto_configure_sirq_polarity( |
382 | struct aspeed_vuart *vuart, struct device_node *syscon_np, | |
383 | u32 reg_offset, u32 reg_mask) | |
384 | { | |
385 | struct regmap *regmap; | |
386 | u32 value; | |
387 | ||
388 | regmap = syscon_node_to_regmap(syscon_np); | |
389 | if (IS_ERR(regmap)) { | |
390 | dev_warn(vuart->dev, | |
391 | "could not get regmap for aspeed,sirq-polarity-sense\n"); | |
392 | return; | |
393 | } | |
394 | if (regmap_read(regmap, reg_offset, &value)) { | |
395 | dev_warn(vuart->dev, "could not read hw strap table\n"); | |
396 | return; | |
397 | } | |
398 | ||
399 | aspeed_vuart_set_sirq_polarity(vuart, (value & reg_mask) == 0); | |
400 | } | |
401 | ||
ca03042f ZW |
402 | static int aspeed_vuart_map_irq_polarity(u32 dt) |
403 | { | |
404 | switch (dt) { | |
405 | case IRQ_TYPE_LEVEL_LOW: | |
406 | return 0; | |
407 | case IRQ_TYPE_LEVEL_HIGH: | |
408 | return 1; | |
409 | default: | |
410 | return -EINVAL; | |
411 | } | |
412 | } | |
413 | ||
7fbcf3af JK |
414 | static int aspeed_vuart_probe(struct platform_device *pdev) |
415 | { | |
8d310c91 | 416 | struct of_phandle_args sirq_polarity_sense_args; |
e8bbaeac | 417 | struct device *dev = &pdev->dev; |
7fbcf3af JK |
418 | struct uart_8250_port port; |
419 | struct aspeed_vuart *vuart; | |
420 | struct device_node *np; | |
421 | struct resource *res; | |
ca03042f | 422 | int rc, sirq_polarity; |
dcdc7e09 | 423 | u32 prop, sirq[2]; |
182fb83d | 424 | struct clk *vclk; |
7fbcf3af JK |
425 | |
426 | np = pdev->dev.of_node; | |
427 | ||
428 | vuart = devm_kzalloc(&pdev->dev, sizeof(*vuart), GFP_KERNEL); | |
429 | if (!vuart) | |
430 | return -ENOMEM; | |
431 | ||
432 | vuart->dev = &pdev->dev; | |
5909c0bf | 433 | timer_setup(&vuart->unthrottle_timer, aspeed_vuart_unthrottle_exp, 0); |
7fbcf3af JK |
434 | |
435 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
0e0fd557 ML |
436 | if (!res) |
437 | return -EINVAL; | |
7fbcf3af JK |
438 | |
439 | memset(&port, 0, sizeof(port)); | |
440 | port.port.private_data = vuart; | |
7fbcf3af JK |
441 | port.port.mapbase = res->start; |
442 | port.port.mapsize = resource_size(res); | |
443 | port.port.startup = aspeed_vuart_startup; | |
444 | port.port.shutdown = aspeed_vuart_shutdown; | |
989983ea JK |
445 | port.port.throttle = aspeed_vuart_throttle; |
446 | port.port.unthrottle = aspeed_vuart_unthrottle; | |
447 | port.port.status = UPSTAT_SYNC_FIFO; | |
7fbcf3af | 448 | port.port.dev = &pdev->dev; |
9b614afe | 449 | port.port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE); |
dcdc7e09 AS |
450 | port.port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_FIXED_PORT | UPF_FIXED_TYPE | |
451 | UPF_NO_THRE_TEST; | |
df8f2be2 | 452 | port.bugs |= UART_BUG_TXRACE; |
7fbcf3af JK |
453 | |
454 | rc = sysfs_create_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); | |
455 | if (rc < 0) | |
456 | return rc; | |
457 | ||
dcdc7e09 AS |
458 | rc = uart_read_port_properties(&port.port); |
459 | if (rc) | |
460 | goto err_sysfs_remove; | |
461 | ||
462 | /* Get clk rate through clk driver if present */ | |
463 | if (!port.port.uartclk) { | |
182fb83d AS |
464 | vclk = devm_clk_get_enabled(dev, NULL); |
465 | if (IS_ERR(vclk)) { | |
466 | rc = dev_err_probe(dev, PTR_ERR(vclk), "clk or clock-frequency not defined\n"); | |
cbafe9d5 | 467 | goto err_sysfs_remove; |
7fbcf3af JK |
468 | } |
469 | ||
dcdc7e09 | 470 | port.port.uartclk = clk_get_rate(vclk); |
7fbcf3af JK |
471 | } |
472 | ||
473 | /* If current-speed was set, then try not to change it. */ | |
474 | if (of_property_read_u32(np, "current-speed", &prop) == 0) | |
dcdc7e09 | 475 | port.port.custom_divisor = port.port.uartclk / (16 * prop); |
7fbcf3af | 476 | |
5909c0bf | 477 | port.port.handle_irq = aspeed_vuart_handle_irq; |
a603ca60 | 478 | port.port.type = PORT_ASPEED_VUART; |
7fbcf3af JK |
479 | |
480 | if (port.port.fifosize) | |
481 | port.capabilities = UART_CAP_FIFO; | |
482 | ||
483 | if (of_property_read_bool(np, "auto-flow-control")) | |
484 | port.capabilities |= UART_CAP_AFE; | |
485 | ||
486 | rc = serial8250_register_8250_port(&port); | |
487 | if (rc < 0) | |
dcdc7e09 | 488 | goto err_sysfs_remove; |
7fbcf3af JK |
489 | |
490 | vuart->line = rc; | |
c9805fbf | 491 | vuart->port = serial8250_get_port(vuart->line); |
7fbcf3af | 492 | |
8d310c91 OS |
493 | rc = of_parse_phandle_with_fixed_args( |
494 | np, "aspeed,sirq-polarity-sense", 2, 0, | |
495 | &sirq_polarity_sense_args); | |
496 | if (rc < 0) { | |
497 | dev_dbg(&pdev->dev, | |
498 | "aspeed,sirq-polarity-sense property not found\n"); | |
499 | } else { | |
500 | aspeed_vuart_auto_configure_sirq_polarity( | |
501 | vuart, sirq_polarity_sense_args.np, | |
502 | sirq_polarity_sense_args.args[0], | |
503 | BIT(sirq_polarity_sense_args.args[1])); | |
504 | of_node_put(sirq_polarity_sense_args.np); | |
505 | } | |
506 | ||
ca03042f ZW |
507 | rc = of_property_read_u32(np, "aspeed,lpc-io-reg", &prop); |
508 | if (rc < 0) | |
509 | prop = ASPEED_VUART_DEFAULT_LPC_ADDR; | |
510 | ||
511 | rc = aspeed_vuart_set_lpc_address(vuart, prop); | |
512 | if (rc < 0) { | |
e8bbaeac | 513 | dev_err_probe(dev, rc, "invalid value in aspeed,lpc-io-reg property\n"); |
dcdc7e09 | 514 | goto err_sysfs_remove; |
ca03042f ZW |
515 | } |
516 | ||
517 | rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts", sirq, 2); | |
518 | if (rc < 0) { | |
519 | sirq[0] = ASPEED_VUART_DEFAULT_SIRQ; | |
520 | sirq[1] = ASPEED_VUART_DEFAULT_SIRQ_POLARITY; | |
521 | } | |
522 | ||
523 | rc = aspeed_vuart_set_sirq(vuart, sirq[0]); | |
524 | if (rc < 0) { | |
e8bbaeac | 525 | dev_err_probe(dev, rc, "invalid sirq number in aspeed,lpc-interrupts property\n"); |
dcdc7e09 | 526 | goto err_sysfs_remove; |
ca03042f ZW |
527 | } |
528 | ||
529 | sirq_polarity = aspeed_vuart_map_irq_polarity(sirq[1]); | |
530 | if (sirq_polarity < 0) { | |
e8bbaeac AS |
531 | rc = dev_err_probe(dev, sirq_polarity, |
532 | "invalid sirq polarity in aspeed,lpc-interrupts property\n"); | |
dcdc7e09 | 533 | goto err_sysfs_remove; |
ca03042f ZW |
534 | } |
535 | ||
536 | aspeed_vuart_set_sirq_polarity(vuart, sirq_polarity); | |
537 | ||
7fbcf3af JK |
538 | aspeed_vuart_set_enabled(vuart, true); |
539 | aspeed_vuart_set_host_tx_discard(vuart, true); | |
540 | platform_set_drvdata(pdev, vuart); | |
541 | ||
542 | return 0; | |
543 | ||
cbafe9d5 AK |
544 | err_sysfs_remove: |
545 | sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); | |
7fbcf3af JK |
546 | return rc; |
547 | } | |
548 | ||
7e1efdf8 | 549 | static void aspeed_vuart_remove(struct platform_device *pdev) |
7fbcf3af JK |
550 | { |
551 | struct aspeed_vuart *vuart = platform_get_drvdata(pdev); | |
552 | ||
5909c0bf | 553 | del_timer_sync(&vuart->unthrottle_timer); |
7fbcf3af JK |
554 | aspeed_vuart_set_enabled(vuart, false); |
555 | serial8250_unregister_port(vuart->line); | |
556 | sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); | |
7fbcf3af JK |
557 | } |
558 | ||
559 | static const struct of_device_id aspeed_vuart_table[] = { | |
560 | { .compatible = "aspeed,ast2400-vuart" }, | |
561 | { .compatible = "aspeed,ast2500-vuart" }, | |
562 | { }, | |
563 | }; | |
564 | ||
565 | static struct platform_driver aspeed_vuart_driver = { | |
566 | .driver = { | |
567 | .name = "aspeed-vuart", | |
568 | .of_match_table = aspeed_vuart_table, | |
569 | }, | |
570 | .probe = aspeed_vuart_probe, | |
7e1efdf8 | 571 | .remove_new = aspeed_vuart_remove, |
7fbcf3af JK |
572 | }; |
573 | ||
574 | module_platform_driver(aspeed_vuart_driver); | |
575 | ||
576 | MODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>"); | |
577 | MODULE_LICENSE("GPL"); | |
578 | MODULE_DESCRIPTION("Driver for Aspeed VUART device"); |