1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Serial Port driver for Open Firmware platform devices |
4 | * |
5 | * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp. |
6 | */ |
7 | |
8 | #include <linux/bits.h> |
9 | #include <linux/console.h> |
10 | #include <linux/math.h> |
11 | #include <linux/module.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/serial_core.h> |
14 | #include <linux/serial_reg.h> |
15 | #include <linux/of_address.h> |
16 | #include <linux/of_irq.h> |
17 | #include <linux/of_platform.h> |
18 | #include <linux/pm_runtime.h> |
19 | #include <linux/clk.h> |
20 | #include <linux/reset.h> |
21 | |
22 | #include "8250.h" |
23 | |
24 | struct of_serial_info { |
25 | struct clk *clk; |
26 | struct reset_control *rst; |
27 | int type; |
28 | int line; |
29 | }; |
30 | |
31 | /* Nuvoton NPCM timeout register */ |
32 | #define UART_NPCM_TOR 7 |
33 | #define UART_NPCM_TOIE BIT(7) /* Timeout Interrupt Enable */ |
34 | |
35 | static int npcm_startup(struct uart_port *port) |
36 | { |
37 | /* |
38 | * Nuvoton calls the scratch register 'UART_TOR' (timeout |
39 | * register). Enable it, and set TIOC (timeout interrupt |
40 | * comparator) to be 0x20 for correct operation. |
41 | */ |
42 | serial_port_out(up: port, UART_NPCM_TOR, UART_NPCM_TOIE | 0x20); |
43 | |
44 | return serial8250_do_startup(port); |
45 | } |
46 | |
47 | /* Nuvoton NPCM UARTs have a custom divisor calculation */ |
48 | static unsigned int npcm_get_divisor(struct uart_port *port, unsigned int baud, |
49 | unsigned int *frac) |
50 | { |
51 | return DIV_ROUND_CLOSEST(port->uartclk, 16 * baud + 2) - 2; |
52 | } |
53 | |
54 | static int npcm_setup(struct uart_port *port) |
55 | { |
56 | port->get_divisor = npcm_get_divisor; |
57 | port->startup = npcm_startup; |
58 | return 0; |
59 | } |
60 | |
61 | /* |
62 | * Fill a struct uart_port for a given device node |
63 | */ |
64 | static int of_platform_serial_setup(struct platform_device *ofdev, |
65 | int type, struct uart_8250_port *up, |
66 | struct of_serial_info *info) |
67 | { |
68 | struct resource resource; |
69 | struct device *dev = &ofdev->dev; |
70 | struct device_node *np = dev->of_node; |
71 | struct uart_port *port = &up->port; |
72 | u32 spd; |
73 | int ret; |
74 | |
75 | memset(port, 0, sizeof *port); |
76 | |
77 | pm_runtime_enable(dev: &ofdev->dev); |
78 | pm_runtime_get_sync(dev: &ofdev->dev); |
79 | |
80 | ret = of_address_to_resource(dev: np, index: 0, r: &resource); |
81 | if (ret) { |
82 | dev_err_probe(dev, err: ret, fmt: "invalid address\n" ); |
83 | goto err_pmruntime; |
84 | } |
85 | |
86 | port->dev = &ofdev->dev; |
87 | port->flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_FIXED_TYPE; |
88 | spin_lock_init(&port->lock); |
89 | |
90 | if (resource_type(res: &resource) == IORESOURCE_IO) { |
91 | port->iotype = UPIO_PORT; |
92 | port->iobase = resource.start; |
93 | } else { |
94 | port->mapbase = resource.start; |
95 | port->mapsize = resource_size(res: &resource); |
96 | port->flags |= UPF_IOREMAP; |
97 | } |
98 | |
99 | ret = uart_read_and_validate_port_properties(port); |
100 | if (ret) |
101 | goto err_pmruntime; |
102 | |
103 | /* Get clk rate through clk driver if present */ |
104 | if (!port->uartclk) { |
105 | info->clk = devm_clk_get_enabled(dev, NULL); |
106 | if (IS_ERR(ptr: info->clk)) { |
107 | ret = dev_err_probe(dev, err: PTR_ERR(ptr: info->clk), fmt: "failed to get clock\n" ); |
108 | goto err_pmruntime; |
109 | } |
110 | |
111 | port->uartclk = clk_get_rate(clk: info->clk); |
112 | } |
113 | /* If current-speed was set, then try not to change it. */ |
114 | if (of_property_read_u32(np, propname: "current-speed" , out_value: &spd) == 0) |
115 | port->custom_divisor = port->uartclk / (16 * spd); |
116 | |
117 | /* Compatibility with the deprecated pxa driver and 8250_pxa drivers. */ |
118 | if (of_device_is_compatible(device: np, "mrvl,mmp-uart" )) |
119 | port->regshift = 2; |
120 | |
121 | info->rst = devm_reset_control_get_optional_shared(dev: &ofdev->dev, NULL); |
122 | if (IS_ERR(ptr: info->rst)) { |
123 | ret = PTR_ERR(ptr: info->rst); |
124 | goto err_pmruntime; |
125 | } |
126 | |
127 | ret = reset_control_deassert(rstc: info->rst); |
128 | if (ret) |
129 | goto err_pmruntime; |
130 | |
131 | port->type = type; |
132 | port->rs485_config = serial8250_em485_config; |
133 | port->rs485_supported = serial8250_em485_supported; |
134 | up->rs485_start_tx = serial8250_em485_start_tx; |
135 | up->rs485_stop_tx = serial8250_em485_stop_tx; |
136 | |
137 | switch (type) { |
138 | case PORT_RT2880: |
139 | ret = rt288x_setup(p: port); |
140 | break; |
141 | case PORT_NPCM: |
142 | ret = npcm_setup(port); |
143 | break; |
144 | default: |
145 | /* Nothing to do */ |
146 | ret = 0; |
147 | break; |
148 | } |
149 | if (ret) |
150 | goto err_pmruntime; |
151 | |
152 | if (IS_REACHABLE(CONFIG_SERIAL_8250_FSL) && |
153 | (of_device_is_compatible(device: np, "fsl,ns16550" ) || |
154 | of_device_is_compatible(device: np, "fsl,16550-FIFO64" ))) { |
155 | port->handle_irq = fsl8250_handle_irq; |
156 | port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE); |
157 | } |
158 | |
159 | return 0; |
160 | err_pmruntime: |
161 | pm_runtime_put_sync(dev: &ofdev->dev); |
162 | pm_runtime_disable(dev: &ofdev->dev); |
163 | return ret; |
164 | } |
165 | |
166 | /* |
167 | * Try to register a serial port |
168 | */ |
169 | static int of_platform_serial_probe(struct platform_device *ofdev) |
170 | { |
171 | struct of_serial_info *info; |
172 | struct uart_8250_port port8250; |
173 | unsigned int port_type; |
174 | u32 tx_threshold; |
175 | int ret; |
176 | |
177 | if (IS_ENABLED(CONFIG_SERIAL_8250_BCM7271) && |
178 | of_device_is_compatible(device: ofdev->dev.of_node, "brcm,bcm7271-uart" )) |
179 | return -ENODEV; |
180 | |
181 | port_type = (unsigned long)of_device_get_match_data(dev: &ofdev->dev); |
182 | if (port_type == PORT_UNKNOWN) |
183 | return -EINVAL; |
184 | |
185 | if (of_property_read_bool(np: ofdev->dev.of_node, propname: "used-by-rtas" )) |
186 | return -EBUSY; |
187 | |
188 | info = kzalloc(size: sizeof(*info), GFP_KERNEL); |
189 | if (info == NULL) |
190 | return -ENOMEM; |
191 | |
192 | memset(&port8250, 0, sizeof(port8250)); |
193 | ret = of_platform_serial_setup(ofdev, type: port_type, up: &port8250, info); |
194 | if (ret) |
195 | goto err_free; |
196 | |
197 | if (port8250.port.fifosize) |
198 | port8250.capabilities = UART_CAP_FIFO; |
199 | |
200 | /* Check for TX FIFO threshold & set tx_loadsz */ |
201 | if ((of_property_read_u32(np: ofdev->dev.of_node, propname: "tx-threshold" , |
202 | out_value: &tx_threshold) == 0) && |
203 | (tx_threshold < port8250.port.fifosize)) |
204 | port8250.tx_loadsz = port8250.port.fifosize - tx_threshold; |
205 | |
206 | if (of_property_read_bool(np: ofdev->dev.of_node, propname: "auto-flow-control" )) |
207 | port8250.capabilities |= UART_CAP_AFE; |
208 | |
209 | if (of_property_read_u32(np: ofdev->dev.of_node, |
210 | propname: "overrun-throttle-ms" , |
211 | out_value: &port8250.overrun_backoff_time_ms) != 0) |
212 | port8250.overrun_backoff_time_ms = 0; |
213 | |
214 | ret = serial8250_register_8250_port(&port8250); |
215 | if (ret < 0) |
216 | goto err_dispose; |
217 | |
218 | info->type = port_type; |
219 | info->line = ret; |
220 | platform_set_drvdata(pdev: ofdev, data: info); |
221 | return 0; |
222 | err_dispose: |
223 | pm_runtime_put_sync(dev: &ofdev->dev); |
224 | pm_runtime_disable(dev: &ofdev->dev); |
225 | err_free: |
226 | kfree(objp: info); |
227 | return ret; |
228 | } |
229 | |
230 | /* |
231 | * Release a line |
232 | */ |
233 | static void of_platform_serial_remove(struct platform_device *ofdev) |
234 | { |
235 | struct of_serial_info *info = platform_get_drvdata(pdev: ofdev); |
236 | |
237 | serial8250_unregister_port(line: info->line); |
238 | |
239 | reset_control_assert(rstc: info->rst); |
240 | pm_runtime_put_sync(dev: &ofdev->dev); |
241 | pm_runtime_disable(dev: &ofdev->dev); |
242 | kfree(objp: info); |
243 | } |
244 | |
245 | #ifdef CONFIG_PM_SLEEP |
246 | static int of_serial_suspend(struct device *dev) |
247 | { |
248 | struct of_serial_info *info = dev_get_drvdata(dev); |
249 | struct uart_8250_port *port8250 = serial8250_get_port(line: info->line); |
250 | struct uart_port *port = &port8250->port; |
251 | |
252 | serial8250_suspend_port(line: info->line); |
253 | |
254 | if (!uart_console(port) || console_suspend_enabled) { |
255 | pm_runtime_put_sync(dev); |
256 | clk_disable_unprepare(clk: info->clk); |
257 | } |
258 | return 0; |
259 | } |
260 | |
261 | static int of_serial_resume(struct device *dev) |
262 | { |
263 | struct of_serial_info *info = dev_get_drvdata(dev); |
264 | struct uart_8250_port *port8250 = serial8250_get_port(line: info->line); |
265 | struct uart_port *port = &port8250->port; |
266 | |
267 | if (!uart_console(port) || console_suspend_enabled) { |
268 | pm_runtime_get_sync(dev); |
269 | clk_prepare_enable(clk: info->clk); |
270 | } |
271 | |
272 | serial8250_resume_port(line: info->line); |
273 | |
274 | return 0; |
275 | } |
276 | #endif |
277 | static SIMPLE_DEV_PM_OPS(of_serial_pm_ops, of_serial_suspend, of_serial_resume); |
278 | |
279 | /* |
280 | * A few common types, add more as needed. |
281 | */ |
282 | static const struct of_device_id of_platform_serial_table[] = { |
283 | { .compatible = "ns8250" , .data = (void *)PORT_8250, }, |
284 | { .compatible = "ns16450" , .data = (void *)PORT_16450, }, |
285 | { .compatible = "ns16550a" , .data = (void *)PORT_16550A, }, |
286 | { .compatible = "ns16550" , .data = (void *)PORT_16550, }, |
287 | { .compatible = "ns16750" , .data = (void *)PORT_16750, }, |
288 | { .compatible = "ns16850" , .data = (void *)PORT_16850, }, |
289 | { .compatible = "nxp,lpc3220-uart" , .data = (void *)PORT_LPC3220, }, |
290 | { .compatible = "ralink,rt2880-uart" , .data = (void *)PORT_RT2880, }, |
291 | { .compatible = "intel,xscale-uart" , .data = (void *)PORT_XSCALE, }, |
292 | { .compatible = "altr,16550-FIFO32" , |
293 | .data = (void *)PORT_ALTR_16550_F32, }, |
294 | { .compatible = "altr,16550-FIFO64" , |
295 | .data = (void *)PORT_ALTR_16550_F64, }, |
296 | { .compatible = "altr,16550-FIFO128" , |
297 | .data = (void *)PORT_ALTR_16550_F128, }, |
298 | { .compatible = "fsl,16550-FIFO64" , |
299 | .data = (void *)PORT_16550A_FSL64, }, |
300 | { .compatible = "mediatek,mtk-btif" , |
301 | .data = (void *)PORT_MTK_BTIF, }, |
302 | { .compatible = "mrvl,mmp-uart" , |
303 | .data = (void *)PORT_XSCALE, }, |
304 | { .compatible = "ti,da830-uart" , .data = (void *)PORT_DA830, }, |
305 | { .compatible = "nuvoton,wpcm450-uart" , .data = (void *)PORT_NPCM, }, |
306 | { .compatible = "nuvoton,npcm750-uart" , .data = (void *)PORT_NPCM, }, |
307 | { /* end of list */ }, |
308 | }; |
309 | MODULE_DEVICE_TABLE(of, of_platform_serial_table); |
310 | |
311 | static struct platform_driver of_platform_serial_driver = { |
312 | .driver = { |
313 | .name = "of_serial" , |
314 | .of_match_table = of_platform_serial_table, |
315 | .pm = &of_serial_pm_ops, |
316 | }, |
317 | .probe = of_platform_serial_probe, |
318 | .remove_new = of_platform_serial_remove, |
319 | }; |
320 | |
321 | module_platform_driver(of_platform_serial_driver); |
322 | |
323 | MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>" ); |
324 | MODULE_LICENSE("GPL" ); |
325 | MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices" ); |
326 | |