Commit | Line | Data |
---|---|---|
7d4008eb JI |
1 | /* |
2 | * Synopsys DesignWare 8250 driver. | |
3 | * | |
4 | * Copyright 2011 Picochip, Jamie Iles. | |
6a7320c4 | 5 | * Copyright 2013 Intel Corporation |
7d4008eb JI |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * The Synopsys DesignWare 8250 has an extra feature whereby it detects if the | |
13 | * LCR is written whilst busy. If it is, then a busy detect interrupt is | |
14 | * raised, the LCR needs to be rewritten and the uart status register read. | |
15 | */ | |
16 | #include <linux/device.h> | |
17 | #include <linux/init.h> | |
18 | #include <linux/io.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/serial_8250.h> | |
21 | #include <linux/serial_core.h> | |
22 | #include <linux/serial_reg.h> | |
23 | #include <linux/of.h> | |
24 | #include <linux/of_irq.h> | |
25 | #include <linux/of_platform.h> | |
26 | #include <linux/platform_device.h> | |
27 | #include <linux/slab.h> | |
6a7320c4 | 28 | #include <linux/acpi.h> |
e302cd93 | 29 | #include <linux/clk.h> |
ffc3ae6d | 30 | #include <linux/pm_runtime.h> |
7d4008eb | 31 | |
7277b2a1 HK |
32 | #include "8250.h" |
33 | ||
30046df2 HK |
34 | /* Offsets for the DesignWare specific registers */ |
35 | #define DW_UART_USR 0x1f /* UART Status Register */ | |
36 | #define DW_UART_CPR 0xf4 /* Component Parameter Register */ | |
37 | #define DW_UART_UCV 0xf8 /* UART Component Version */ | |
38 | ||
39 | /* Component Parameter Register bits */ | |
40 | #define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0) | |
41 | #define DW_UART_CPR_AFCE_MODE (1 << 4) | |
42 | #define DW_UART_CPR_THRE_MODE (1 << 5) | |
43 | #define DW_UART_CPR_SIR_MODE (1 << 6) | |
44 | #define DW_UART_CPR_SIR_LP_MODE (1 << 7) | |
45 | #define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8) | |
46 | #define DW_UART_CPR_FIFO_ACCESS (1 << 9) | |
47 | #define DW_UART_CPR_FIFO_STAT (1 << 10) | |
48 | #define DW_UART_CPR_SHADOW (1 << 11) | |
49 | #define DW_UART_CPR_ENCODED_PARMS (1 << 12) | |
50 | #define DW_UART_CPR_DMA_EXTRA (1 << 13) | |
51 | #define DW_UART_CPR_FIFO_MODE (0xff << 16) | |
52 | /* Helper for fifo size calculation */ | |
53 | #define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16) | |
54 | ||
55 | ||
7d4008eb | 56 | struct dw8250_data { |
e302cd93 EL |
57 | int last_lcr; |
58 | int line; | |
59 | struct clk *clk; | |
7d4008eb JI |
60 | }; |
61 | ||
62 | static void dw8250_serial_out(struct uart_port *p, int offset, int value) | |
63 | { | |
64 | struct dw8250_data *d = p->private_data; | |
65 | ||
66 | if (offset == UART_LCR) | |
67 | d->last_lcr = value; | |
68 | ||
69 | offset <<= p->regshift; | |
70 | writeb(value, p->membase + offset); | |
71 | } | |
72 | ||
73 | static unsigned int dw8250_serial_in(struct uart_port *p, int offset) | |
74 | { | |
75 | offset <<= p->regshift; | |
76 | ||
77 | return readb(p->membase + offset); | |
78 | } | |
79 | ||
80 | static void dw8250_serial_out32(struct uart_port *p, int offset, int value) | |
81 | { | |
82 | struct dw8250_data *d = p->private_data; | |
83 | ||
84 | if (offset == UART_LCR) | |
85 | d->last_lcr = value; | |
86 | ||
87 | offset <<= p->regshift; | |
88 | writel(value, p->membase + offset); | |
89 | } | |
90 | ||
91 | static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) | |
92 | { | |
93 | offset <<= p->regshift; | |
94 | ||
95 | return readl(p->membase + offset); | |
96 | } | |
97 | ||
7d4008eb JI |
98 | static int dw8250_handle_irq(struct uart_port *p) |
99 | { | |
100 | struct dw8250_data *d = p->private_data; | |
101 | unsigned int iir = p->serial_in(p, UART_IIR); | |
102 | ||
103 | if (serial8250_handle_irq(p, iir)) { | |
104 | return 1; | |
105 | } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { | |
106 | /* Clear the USR and write the LCR again. */ | |
30046df2 | 107 | (void)p->serial_in(p, DW_UART_USR); |
68e56cb3 | 108 | p->serial_out(p, UART_LCR, d->last_lcr); |
7d4008eb JI |
109 | |
110 | return 1; | |
111 | } | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
ffc3ae6d HK |
116 | static void |
117 | dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) | |
118 | { | |
119 | if (!state) | |
120 | pm_runtime_get_sync(port->dev); | |
121 | ||
122 | serial8250_do_pm(port, state, old); | |
123 | ||
124 | if (state) | |
125 | pm_runtime_put_sync_suspend(port->dev); | |
126 | } | |
127 | ||
a7260c8c HK |
128 | static int dw8250_probe_of(struct uart_port *p) |
129 | { | |
130 | struct device_node *np = p->dev->of_node; | |
131 | u32 val; | |
132 | ||
133 | if (!of_property_read_u32(np, "reg-io-width", &val)) { | |
134 | switch (val) { | |
135 | case 1: | |
136 | break; | |
137 | case 4: | |
138 | p->iotype = UPIO_MEM32; | |
139 | p->serial_in = dw8250_serial_in32; | |
140 | p->serial_out = dw8250_serial_out32; | |
141 | break; | |
142 | default: | |
143 | dev_err(p->dev, "unsupported reg-io-width (%u)\n", val); | |
144 | return -EINVAL; | |
145 | } | |
146 | } | |
147 | ||
148 | if (!of_property_read_u32(np, "reg-shift", &val)) | |
149 | p->regshift = val; | |
150 | ||
e302cd93 EL |
151 | /* clock got configured through clk api, all done */ |
152 | if (p->uartclk) | |
153 | return 0; | |
154 | ||
155 | /* try to find out clock frequency from DT as fallback */ | |
a7260c8c | 156 | if (of_property_read_u32(np, "clock-frequency", &val)) { |
e302cd93 | 157 | dev_err(p->dev, "clk or clock-frequency not defined\n"); |
a7260c8c HK |
158 | return -EINVAL; |
159 | } | |
160 | p->uartclk = val; | |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
053fac36 | 165 | #ifdef CONFIG_ACPI |
94b2b47c | 166 | static int dw8250_probe_acpi(struct uart_8250_port *up) |
6a7320c4 HK |
167 | { |
168 | const struct acpi_device_id *id; | |
94b2b47c | 169 | struct uart_port *p = &up->port; |
6a7320c4 HK |
170 | |
171 | id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev); | |
172 | if (!id) | |
173 | return -ENODEV; | |
174 | ||
175 | p->iotype = UPIO_MEM32; | |
176 | p->serial_in = dw8250_serial_in32; | |
177 | p->serial_out = dw8250_serial_out32; | |
178 | p->regshift = 2; | |
aea02e87 HK |
179 | |
180 | if (!p->uartclk) | |
181 | p->uartclk = (unsigned int)id->driver_data; | |
6a7320c4 | 182 | |
94b2b47c HK |
183 | up->dma = devm_kzalloc(p->dev, sizeof(*up->dma), GFP_KERNEL); |
184 | if (!up->dma) | |
185 | return -ENOMEM; | |
186 | ||
187 | up->dma->rxconf.src_maxburst = p->fifosize / 4; | |
188 | up->dma->txconf.dst_maxburst = p->fifosize / 4; | |
7277b2a1 | 189 | |
6a7320c4 HK |
190 | return 0; |
191 | } | |
053fac36 HK |
192 | #else |
193 | static inline int dw8250_probe_acpi(struct uart_port *p) | |
194 | { | |
195 | return -ENODEV; | |
196 | } | |
197 | #endif /* CONFIG_ACPI */ | |
6a7320c4 | 198 | |
30046df2 HK |
199 | static void dw8250_setup_port(struct uart_8250_port *up) |
200 | { | |
201 | struct uart_port *p = &up->port; | |
202 | u32 reg = readl(p->membase + DW_UART_UCV); | |
203 | ||
204 | /* | |
205 | * If the Component Version Register returns zero, we know that | |
206 | * ADDITIONAL_FEATURES are not enabled. No need to go any further. | |
207 | */ | |
208 | if (!reg) | |
209 | return; | |
210 | ||
211 | dev_dbg_ratelimited(p->dev, "Designware UART version %c.%c%c\n", | |
212 | (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); | |
213 | ||
214 | reg = readl(p->membase + DW_UART_CPR); | |
215 | if (!reg) | |
216 | return; | |
217 | ||
218 | /* Select the type based on fifo */ | |
219 | if (reg & DW_UART_CPR_FIFO_MODE) { | |
220 | p->type = PORT_16550A; | |
221 | p->flags |= UPF_FIXED_TYPE; | |
222 | p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); | |
223 | up->tx_loadsz = p->fifosize; | |
2920adb6 | 224 | up->capabilities = UART_CAP_FIFO; |
30046df2 | 225 | } |
2920adb6 HK |
226 | |
227 | if (reg & DW_UART_CPR_AFCE_MODE) | |
228 | up->capabilities |= UART_CAP_AFE; | |
30046df2 HK |
229 | } |
230 | ||
9671f099 | 231 | static int dw8250_probe(struct platform_device *pdev) |
7d4008eb | 232 | { |
2655a2c7 | 233 | struct uart_8250_port uart = {}; |
7d4008eb JI |
234 | struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
235 | struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | |
7d4008eb | 236 | struct dw8250_data *data; |
a7260c8c | 237 | int err; |
7d4008eb JI |
238 | |
239 | if (!regs || !irq) { | |
240 | dev_err(&pdev->dev, "no registers/irq defined\n"); | |
241 | return -EINVAL; | |
242 | } | |
243 | ||
2655a2c7 AC |
244 | spin_lock_init(&uart.port.lock); |
245 | uart.port.mapbase = regs->start; | |
246 | uart.port.irq = irq->start; | |
247 | uart.port.handle_irq = dw8250_handle_irq; | |
ffc3ae6d | 248 | uart.port.pm = dw8250_do_pm; |
2655a2c7 | 249 | uart.port.type = PORT_8250; |
f93366ff | 250 | uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; |
2655a2c7 | 251 | uart.port.dev = &pdev->dev; |
7d4008eb | 252 | |
b88d0826 HK |
253 | uart.port.membase = devm_ioremap(&pdev->dev, regs->start, |
254 | resource_size(regs)); | |
f93366ff HK |
255 | if (!uart.port.membase) |
256 | return -ENOMEM; | |
257 | ||
e302cd93 EL |
258 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); |
259 | if (!data) | |
260 | return -ENOMEM; | |
261 | ||
262 | data->clk = devm_clk_get(&pdev->dev, NULL); | |
263 | if (!IS_ERR(data->clk)) { | |
264 | clk_prepare_enable(data->clk); | |
265 | uart.port.uartclk = clk_get_rate(data->clk); | |
266 | } | |
267 | ||
2655a2c7 AC |
268 | uart.port.iotype = UPIO_MEM; |
269 | uart.port.serial_in = dw8250_serial_in; | |
270 | uart.port.serial_out = dw8250_serial_out; | |
e302cd93 | 271 | uart.port.private_data = data; |
a7260c8c | 272 | |
f5836a55 HK |
273 | dw8250_setup_port(&uart); |
274 | ||
a7260c8c HK |
275 | if (pdev->dev.of_node) { |
276 | err = dw8250_probe_of(&uart.port); | |
277 | if (err) | |
278 | return err; | |
6a7320c4 | 279 | } else if (ACPI_HANDLE(&pdev->dev)) { |
94b2b47c | 280 | err = dw8250_probe_acpi(&uart); |
6a7320c4 HK |
281 | if (err) |
282 | return err; | |
a7260c8c HK |
283 | } else { |
284 | return -ENODEV; | |
7d4008eb JI |
285 | } |
286 | ||
2655a2c7 | 287 | data->line = serial8250_register_8250_port(&uart); |
7d4008eb JI |
288 | if (data->line < 0) |
289 | return data->line; | |
290 | ||
291 | platform_set_drvdata(pdev, data); | |
292 | ||
ffc3ae6d HK |
293 | pm_runtime_set_active(&pdev->dev); |
294 | pm_runtime_enable(&pdev->dev); | |
295 | ||
7d4008eb JI |
296 | return 0; |
297 | } | |
298 | ||
ae8d8a14 | 299 | static int dw8250_remove(struct platform_device *pdev) |
7d4008eb JI |
300 | { |
301 | struct dw8250_data *data = platform_get_drvdata(pdev); | |
302 | ||
ffc3ae6d HK |
303 | pm_runtime_get_sync(&pdev->dev); |
304 | ||
7d4008eb JI |
305 | serial8250_unregister_port(data->line); |
306 | ||
e302cd93 EL |
307 | if (!IS_ERR(data->clk)) |
308 | clk_disable_unprepare(data->clk); | |
309 | ||
ffc3ae6d HK |
310 | pm_runtime_disable(&pdev->dev); |
311 | pm_runtime_put_noidle(&pdev->dev); | |
312 | ||
7d4008eb JI |
313 | return 0; |
314 | } | |
315 | ||
b61c5ed5 | 316 | #ifdef CONFIG_PM |
ffc3ae6d | 317 | static int dw8250_suspend(struct device *dev) |
b61c5ed5 | 318 | { |
ffc3ae6d | 319 | struct dw8250_data *data = dev_get_drvdata(dev); |
b61c5ed5 JH |
320 | |
321 | serial8250_suspend_port(data->line); | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
ffc3ae6d | 326 | static int dw8250_resume(struct device *dev) |
b61c5ed5 | 327 | { |
ffc3ae6d | 328 | struct dw8250_data *data = dev_get_drvdata(dev); |
b61c5ed5 JH |
329 | |
330 | serial8250_resume_port(data->line); | |
331 | ||
332 | return 0; | |
333 | } | |
b61c5ed5 JH |
334 | #endif /* CONFIG_PM */ |
335 | ||
ffc3ae6d HK |
336 | #ifdef CONFIG_PM_RUNTIME |
337 | static int dw8250_runtime_suspend(struct device *dev) | |
338 | { | |
339 | struct dw8250_data *data = dev_get_drvdata(dev); | |
340 | ||
341 | clk_disable_unprepare(data->clk); | |
342 | ||
343 | return 0; | |
344 | } | |
345 | ||
346 | static int dw8250_runtime_resume(struct device *dev) | |
347 | { | |
348 | struct dw8250_data *data = dev_get_drvdata(dev); | |
349 | ||
350 | clk_prepare_enable(data->clk); | |
351 | ||
352 | return 0; | |
353 | } | |
354 | #endif | |
355 | ||
356 | static const struct dev_pm_ops dw8250_pm_ops = { | |
357 | SET_SYSTEM_SLEEP_PM_OPS(dw8250_suspend, dw8250_resume) | |
358 | SET_RUNTIME_PM_OPS(dw8250_runtime_suspend, dw8250_runtime_resume, NULL) | |
359 | }; | |
360 | ||
a7260c8c | 361 | static const struct of_device_id dw8250_of_match[] = { |
7d4008eb JI |
362 | { .compatible = "snps,dw-apb-uart" }, |
363 | { /* Sentinel */ } | |
364 | }; | |
a7260c8c | 365 | MODULE_DEVICE_TABLE(of, dw8250_of_match); |
7d4008eb | 366 | |
6a7320c4 | 367 | static const struct acpi_device_id dw8250_acpi_match[] = { |
aea02e87 HK |
368 | { "INT33C4", 0 }, |
369 | { "INT33C5", 0 }, | |
6a7320c4 HK |
370 | { }, |
371 | }; | |
372 | MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match); | |
373 | ||
7d4008eb JI |
374 | static struct platform_driver dw8250_platform_driver = { |
375 | .driver = { | |
376 | .name = "dw-apb-uart", | |
377 | .owner = THIS_MODULE, | |
ffc3ae6d | 378 | .pm = &dw8250_pm_ops, |
a7260c8c | 379 | .of_match_table = dw8250_of_match, |
6a7320c4 | 380 | .acpi_match_table = ACPI_PTR(dw8250_acpi_match), |
7d4008eb JI |
381 | }, |
382 | .probe = dw8250_probe, | |
2d47b716 | 383 | .remove = dw8250_remove, |
7d4008eb JI |
384 | }; |
385 | ||
c8381c15 | 386 | module_platform_driver(dw8250_platform_driver); |
7d4008eb JI |
387 | |
388 | MODULE_AUTHOR("Jamie Iles"); | |
389 | MODULE_LICENSE("GPL"); | |
390 | MODULE_DESCRIPTION("Synopsys DesignWare 8250 serial port driver"); |