Commit | Line | Data |
---|---|---|
e3b3d0f5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
8d38a5b2 AB |
2 | /* |
3 | * Serial Port driver for Open Firmware platform devices | |
4 | * | |
5 | * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp. | |
8d38a5b2 | 6 | */ |
8ad3b135 | 7 | #include <linux/console.h> |
8d38a5b2 | 8 | #include <linux/module.h> |
5a0e3ad6 | 9 | #include <linux/slab.h> |
bf03f65b | 10 | #include <linux/delay.h> |
8d38a5b2 | 11 | #include <linux/serial_core.h> |
bf03f65b | 12 | #include <linux/serial_reg.h> |
f1ca09b2 | 13 | #include <linux/of_address.h> |
73930a85 | 14 | #include <linux/of_irq.h> |
c401b044 | 15 | #include <linux/of_platform.h> |
a2d23eda | 16 | #include <linux/pm_runtime.h> |
0bbeb3c3 | 17 | #include <linux/clk.h> |
e2860e1f | 18 | #include <linux/reset.h> |
8d38a5b2 | 19 | |
c93a5993 | 20 | #include "8250.h" |
b0b8c84c | 21 | |
e34b9c94 | 22 | struct of_serial_info { |
0bbeb3c3 | 23 | struct clk *clk; |
e2860e1f | 24 | struct reset_control *rst; |
e34b9c94 IK |
25 | int type; |
26 | int line; | |
27 | }; | |
28 | ||
bf03f65b | 29 | #ifdef CONFIG_ARCH_TEGRA |
28264eb6 | 30 | static void tegra_serial_handle_break(struct uart_port *p) |
bf03f65b DW |
31 | { |
32 | unsigned int status, tmout = 10000; | |
33 | ||
34 | do { | |
35 | status = p->serial_in(p, UART_LSR); | |
36 | if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) | |
37 | status = p->serial_in(p, UART_RX); | |
38 | else | |
39 | break; | |
40 | if (--tmout == 0) | |
41 | break; | |
42 | udelay(1); | |
43 | } while (1); | |
44 | } | |
f26402e8 SW |
45 | #else |
46 | static inline void tegra_serial_handle_break(struct uart_port *port) | |
47 | { | |
48 | } | |
bf03f65b DW |
49 | #endif |
50 | ||
0c11b888 HS |
51 | static int of_8250_rs485_config(struct uart_port *port, |
52 | struct serial_rs485 *rs485) | |
53 | { | |
54 | struct uart_8250_port *up = up_to_u8250p(port); | |
55 | ||
56 | /* Clamp the delays to [0, 100ms] */ | |
57 | rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U); | |
58 | rs485->delay_rts_after_send = min(rs485->delay_rts_after_send, 100U); | |
59 | ||
60 | port->rs485 = *rs485; | |
61 | ||
62 | /* | |
63 | * Both serial8250_em485_init and serial8250_em485_destroy | |
64 | * are idempotent | |
65 | */ | |
66 | if (rs485->flags & SER_RS485_ENABLED) { | |
67 | int ret = serial8250_em485_init(up); | |
68 | ||
69 | if (ret) { | |
70 | rs485->flags &= ~SER_RS485_ENABLED; | |
71 | port->rs485.flags &= ~SER_RS485_ENABLED; | |
72 | } | |
73 | return ret; | |
74 | } | |
75 | ||
76 | serial8250_em485_destroy(up); | |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
8d38a5b2 AB |
81 | /* |
82 | * Fill a struct uart_port for a given device node | |
83 | */ | |
9671f099 | 84 | static int of_platform_serial_setup(struct platform_device *ofdev, |
0bbeb3c3 MK |
85 | int type, struct uart_port *port, |
86 | struct of_serial_info *info) | |
8d38a5b2 AB |
87 | { |
88 | struct resource resource; | |
61c7a080 | 89 | struct device_node *np = ofdev->dev.of_node; |
b84e7731 | 90 | u32 clk, spd, prop; |
a27d9382 | 91 | int ret, irq; |
8d38a5b2 AB |
92 | |
93 | memset(port, 0, sizeof *port); | |
a2d23eda FCJ |
94 | |
95 | pm_runtime_enable(&ofdev->dev); | |
96 | pm_runtime_get_sync(&ofdev->dev); | |
97 | ||
b84e7731 | 98 | if (of_property_read_u32(np, "clock-frequency", &clk)) { |
0bbeb3c3 MK |
99 | |
100 | /* Get clk rate through clk driver if present */ | |
3a63d224 | 101 | info->clk = devm_clk_get(&ofdev->dev, NULL); |
76cc4386 | 102 | if (IS_ERR(info->clk)) { |
a2d23eda | 103 | ret = PTR_ERR(info->clk); |
87bb008c TR |
104 | if (ret != -EPROBE_DEFER) |
105 | dev_warn(&ofdev->dev, | |
106 | "failed to get clock: %d\n", ret); | |
a2d23eda | 107 | goto err_pmruntime; |
0bbeb3c3 MK |
108 | } |
109 | ||
6f0c3091 MY |
110 | ret = clk_prepare_enable(info->clk); |
111 | if (ret < 0) | |
a2d23eda | 112 | goto err_pmruntime; |
6f0c3091 | 113 | |
0bbeb3c3 | 114 | clk = clk_get_rate(info->clk); |
8d38a5b2 | 115 | } |
b84e7731 GL |
116 | /* If current-speed was set, then try not to change it. */ |
117 | if (of_property_read_u32(np, "current-speed", &spd) == 0) | |
118 | port->custom_divisor = clk / (16 * spd); | |
8d38a5b2 AB |
119 | |
120 | ret = of_address_to_resource(np, 0, &resource); | |
121 | if (ret) { | |
122 | dev_warn(&ofdev->dev, "invalid address\n"); | |
fa9ba3ac | 123 | goto err_unprepare; |
8d38a5b2 AB |
124 | } |
125 | ||
aa959474 JG |
126 | port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | |
127 | UPF_FIXED_TYPE; | |
8d38a5b2 | 128 | spin_lock_init(&port->lock); |
b912b5e2 | 129 | |
aa959474 JG |
130 | if (resource_type(&resource) == IORESOURCE_IO) { |
131 | port->iotype = UPIO_PORT; | |
132 | port->iobase = resource.start; | |
133 | } else { | |
134 | port->mapbase = resource.start; | |
135 | port->mapsize = resource_size(&resource); | |
136 | ||
137 | /* Check for shifted address mapping */ | |
138 | if (of_property_read_u32(np, "reg-offset", &prop) == 0) | |
139 | port->mapbase += prop; | |
140 | ||
141 | port->iotype = UPIO_MEM; | |
142 | if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { | |
143 | switch (prop) { | |
144 | case 1: | |
145 | port->iotype = UPIO_MEM; | |
146 | break; | |
147 | case 2: | |
148 | port->iotype = UPIO_MEM16; | |
149 | break; | |
150 | case 4: | |
151 | port->iotype = of_device_is_big_endian(np) ? | |
152 | UPIO_MEM32BE : UPIO_MEM32; | |
153 | break; | |
154 | default: | |
155 | dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n", | |
156 | prop); | |
157 | ret = -EINVAL; | |
b29330d8 | 158 | goto err_unprepare; |
aa959474 JG |
159 | } |
160 | } | |
161 | port->flags |= UPF_IOREMAP; | |
162 | } | |
b912b5e2 | 163 | |
f4817843 LR |
164 | /* Compatibility with the deprecated pxa driver and 8250_pxa drivers. */ |
165 | if (of_device_is_compatible(np, "mrvl,mmp-uart")) | |
166 | port->regshift = 2; | |
167 | ||
b912b5e2 | 168 | /* Check for registers offset within the devices address range */ |
b84e7731 GL |
169 | if (of_property_read_u32(np, "reg-shift", &prop) == 0) |
170 | port->regshift = prop; | |
b912b5e2 | 171 | |
9f1ca068 HK |
172 | /* Check for fifo size */ |
173 | if (of_property_read_u32(np, "fifo-size", &prop) == 0) | |
174 | port->fifosize = prop; | |
175 | ||
3239fd31 LS |
176 | /* Check for a fixed line number */ |
177 | ret = of_alias_get_id(np, "serial"); | |
178 | if (ret >= 0) | |
179 | port->line = ret; | |
180 | ||
a27d9382 JG |
181 | irq = of_irq_get(np, 0); |
182 | if (irq < 0) { | |
183 | if (irq == -EPROBE_DEFER) { | |
184 | ret = -EPROBE_DEFER; | |
185 | goto err_unprepare; | |
186 | } | |
187 | /* IRQ support not mandatory */ | |
188 | irq = 0; | |
c58caaab | 189 | } |
7423734e | 190 | |
a27d9382 JG |
191 | port->irq = irq; |
192 | ||
e2860e1f | 193 | info->rst = devm_reset_control_get_optional_shared(&ofdev->dev, NULL); |
b9820a31 MY |
194 | if (IS_ERR(info->rst)) { |
195 | ret = PTR_ERR(info->rst); | |
a27d9382 | 196 | goto err_unprepare; |
b9820a31 MY |
197 | } |
198 | ||
e2860e1f JS |
199 | ret = reset_control_deassert(info->rst); |
200 | if (ret) | |
a27d9382 | 201 | goto err_unprepare; |
e2860e1f | 202 | |
8d38a5b2 | 203 | port->type = type; |
b84e7731 | 204 | port->uartclk = clk; |
fde8be29 | 205 | |
5c98e9cd | 206 | if (of_property_read_bool(np, "no-loopback-test")) |
fde8be29 GJ |
207 | port->flags |= UPF_SKIP_TEST; |
208 | ||
8d38a5b2 | 209 | port->dev = &ofdev->dev; |
0c11b888 | 210 | port->rs485_config = of_8250_rs485_config; |
8d38a5b2 | 211 | |
9b8777e3 JC |
212 | switch (type) { |
213 | case PORT_TEGRA: | |
bf03f65b | 214 | port->handle_break = tegra_serial_handle_break; |
9b8777e3 JC |
215 | break; |
216 | ||
217 | case PORT_RT2880: | |
218 | port->iotype = UPIO_AU; | |
219 | break; | |
220 | } | |
bf03f65b | 221 | |
d43b54d2 SW |
222 | if (IS_ENABLED(CONFIG_SERIAL_8250_FSL) && |
223 | (of_device_is_compatible(np, "fsl,ns16550") || | |
d68fefdd | 224 | of_device_is_compatible(np, "fsl,16550-FIFO64"))) { |
d43b54d2 | 225 | port->handle_irq = fsl8250_handle_irq; |
d68fefdd DS |
226 | port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE); |
227 | } | |
d43b54d2 | 228 | |
8d38a5b2 | 229 | return 0; |
fa9ba3ac | 230 | err_unprepare: |
a2d23eda FCJ |
231 | clk_disable_unprepare(info->clk); |
232 | err_pmruntime: | |
233 | pm_runtime_put_sync(&ofdev->dev); | |
234 | pm_runtime_disable(&ofdev->dev); | |
0bbeb3c3 | 235 | return ret; |
8d38a5b2 AB |
236 | } |
237 | ||
238 | /* | |
239 | * Try to register a serial port | |
240 | */ | |
9671f099 | 241 | static int of_platform_serial_probe(struct platform_device *ofdev) |
8d38a5b2 | 242 | { |
e34b9c94 | 243 | struct of_serial_info *info; |
aa42db44 | 244 | struct uart_8250_port port8250; |
525667c0 | 245 | unsigned int port_type; |
aa42db44 | 246 | u32 tx_threshold; |
8d38a5b2 AB |
247 | int ret; |
248 | ||
525667c0 AS |
249 | port_type = (unsigned long)of_device_get_match_data(&ofdev->dev); |
250 | if (port_type == PORT_UNKNOWN) | |
793218df GL |
251 | return -EINVAL; |
252 | ||
5c98e9cd | 253 | if (of_property_read_bool(ofdev->dev.of_node, "used-by-rtas")) |
8d38a5b2 AB |
254 | return -EBUSY; |
255 | ||
7e12e675 | 256 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
e34b9c94 IK |
257 | if (info == NULL) |
258 | return -ENOMEM; | |
259 | ||
aa42db44 AB |
260 | memset(&port8250, 0, sizeof(port8250)); |
261 | ret = of_platform_serial_setup(ofdev, port_type, &port8250.port, info); | |
8d38a5b2 | 262 | if (ret) |
fa9ba3ac | 263 | goto err_free; |
8d38a5b2 | 264 | |
aa42db44 AB |
265 | if (port8250.port.fifosize) |
266 | port8250.capabilities = UART_CAP_FIFO; | |
b0b8c84c | 267 | |
aa42db44 AB |
268 | /* Check for TX FIFO threshold & set tx_loadsz */ |
269 | if ((of_property_read_u32(ofdev->dev.of_node, "tx-threshold", | |
270 | &tx_threshold) == 0) && | |
271 | (tx_threshold < port8250.port.fifosize)) | |
272 | port8250.tx_loadsz = port8250.port.fifosize - tx_threshold; | |
b0b8c84c | 273 | |
aa42db44 AB |
274 | if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control")) |
275 | port8250.capabilities |= UART_CAP_AFE; | |
ffea0439 | 276 | |
6d7f677a DD |
277 | if (of_property_read_u32(ofdev->dev.of_node, |
278 | "overrun-throttle-ms", | |
279 | &port8250.overrun_backoff_time_ms) != 0) | |
280 | port8250.overrun_backoff_time_ms = 0; | |
281 | ||
aa42db44 | 282 | ret = serial8250_register_8250_port(&port8250); |
8d38a5b2 | 283 | if (ret < 0) |
fa9ba3ac | 284 | goto err_dispose; |
8d38a5b2 | 285 | |
e34b9c94 IK |
286 | info->type = port_type; |
287 | info->line = ret; | |
696faedd | 288 | platform_set_drvdata(ofdev, info); |
8d38a5b2 | 289 | return 0; |
fa9ba3ac | 290 | err_dispose: |
aa42db44 | 291 | irq_dispose_mapping(port8250.port.irq); |
a2d23eda FCJ |
292 | pm_runtime_put_sync(&ofdev->dev); |
293 | pm_runtime_disable(&ofdev->dev); | |
294 | clk_disable_unprepare(info->clk); | |
fa9ba3ac AK |
295 | err_free: |
296 | kfree(info); | |
8d38a5b2 AB |
297 | return ret; |
298 | } | |
299 | ||
300 | /* | |
301 | * Release a line | |
302 | */ | |
2dc11581 | 303 | static int of_platform_serial_remove(struct platform_device *ofdev) |
8d38a5b2 | 304 | { |
696faedd | 305 | struct of_serial_info *info = platform_get_drvdata(ofdev); |
aa42db44 AB |
306 | |
307 | serial8250_unregister_port(info->line); | |
0bbeb3c3 | 308 | |
e2860e1f | 309 | reset_control_assert(info->rst); |
a2d23eda FCJ |
310 | pm_runtime_put_sync(&ofdev->dev); |
311 | pm_runtime_disable(&ofdev->dev); | |
312 | clk_disable_unprepare(info->clk); | |
e34b9c94 | 313 | kfree(info); |
8d38a5b2 AB |
314 | return 0; |
315 | } | |
316 | ||
8ad3b135 | 317 | #ifdef CONFIG_PM_SLEEP |
aa42db44 | 318 | static int of_serial_suspend(struct device *dev) |
8ad3b135 | 319 | { |
aa42db44 | 320 | struct of_serial_info *info = dev_get_drvdata(dev); |
8ad3b135 JL |
321 | struct uart_8250_port *port8250 = serial8250_get_port(info->line); |
322 | struct uart_port *port = &port8250->port; | |
323 | ||
324 | serial8250_suspend_port(info->line); | |
aa42db44 | 325 | |
a2d23eda FCJ |
326 | if (!uart_console(port) || console_suspend_enabled) { |
327 | pm_runtime_put_sync(dev); | |
8ad3b135 | 328 | clk_disable_unprepare(info->clk); |
a2d23eda | 329 | } |
aa42db44 | 330 | return 0; |
8ad3b135 JL |
331 | } |
332 | ||
aa42db44 | 333 | static int of_serial_resume(struct device *dev) |
8ad3b135 | 334 | { |
aa42db44 | 335 | struct of_serial_info *info = dev_get_drvdata(dev); |
8ad3b135 JL |
336 | struct uart_8250_port *port8250 = serial8250_get_port(info->line); |
337 | struct uart_port *port = &port8250->port; | |
338 | ||
a2d23eda FCJ |
339 | if (!uart_console(port) || console_suspend_enabled) { |
340 | pm_runtime_get_sync(dev); | |
8ad3b135 | 341 | clk_prepare_enable(info->clk); |
a2d23eda | 342 | } |
8ad3b135 JL |
343 | |
344 | serial8250_resume_port(info->line); | |
8ad3b135 JL |
345 | |
346 | return 0; | |
347 | } | |
348 | #endif | |
349 | static SIMPLE_DEV_PM_OPS(of_serial_pm_ops, of_serial_suspend, of_serial_resume); | |
350 | ||
8d38a5b2 AB |
351 | /* |
352 | * A few common types, add more as needed. | |
353 | */ | |
ed0bb232 | 354 | static const struct of_device_id of_platform_serial_table[] = { |
8c6e9112 GL |
355 | { .compatible = "ns8250", .data = (void *)PORT_8250, }, |
356 | { .compatible = "ns16450", .data = (void *)PORT_16450, }, | |
357 | { .compatible = "ns16550a", .data = (void *)PORT_16550A, }, | |
358 | { .compatible = "ns16550", .data = (void *)PORT_16550, }, | |
359 | { .compatible = "ns16750", .data = (void *)PORT_16750, }, | |
360 | { .compatible = "ns16850", .data = (void *)PORT_16850, }, | |
2e39e5be | 361 | { .compatible = "nvidia,tegra20-uart", .data = (void *)PORT_TEGRA, }, |
e4305f0c | 362 | { .compatible = "nxp,lpc3220-uart", .data = (void *)PORT_LPC3220, }, |
9b8777e3 | 363 | { .compatible = "ralink,rt2880-uart", .data = (void *)PORT_RT2880, }, |
3a50365d | 364 | { .compatible = "intel,xscale-uart", .data = (void *)PORT_XSCALE, }, |
e06c93ca LFT |
365 | { .compatible = "altr,16550-FIFO32", |
366 | .data = (void *)PORT_ALTR_16550_F32, }, | |
367 | { .compatible = "altr,16550-FIFO64", | |
368 | .data = (void *)PORT_ALTR_16550_F64, }, | |
369 | { .compatible = "altr,16550-FIFO128", | |
370 | .data = (void *)PORT_ALTR_16550_F128, }, | |
1c16ae65 SW |
371 | { .compatible = "mediatek,mtk-btif", |
372 | .data = (void *)PORT_MTK_BTIF, }, | |
6ad991b6 RH |
373 | { .compatible = "mrvl,mmp-uart", |
374 | .data = (void *)PORT_XSCALE, }, | |
a2d6a987 | 375 | { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, }, |
f597fbce | 376 | { .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, }, |
8d38a5b2 AB |
377 | { /* end of list */ }, |
378 | }; | |
8d58db1e | 379 | MODULE_DEVICE_TABLE(of, of_platform_serial_table); |
8d38a5b2 | 380 | |
793218df | 381 | static struct platform_driver of_platform_serial_driver = { |
4018294b GL |
382 | .driver = { |
383 | .name = "of_serial", | |
4018294b | 384 | .of_match_table = of_platform_serial_table, |
434ba16e | 385 | .pm = &of_serial_pm_ops, |
4018294b | 386 | }, |
8d38a5b2 AB |
387 | .probe = of_platform_serial_probe, |
388 | .remove = of_platform_serial_remove, | |
8d38a5b2 AB |
389 | }; |
390 | ||
940ab889 | 391 | module_platform_driver(of_platform_serial_driver); |
8d38a5b2 AB |
392 | |
393 | MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>"); | |
394 | MODULE_LICENSE("GPL"); | |
395 | MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices"); |