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> | |
7d4008eb JI |
17 | #include <linux/io.h> |
18 | #include <linux/module.h> | |
19 | #include <linux/serial_8250.h> | |
7d4008eb JI |
20 | #include <linux/serial_reg.h> |
21 | #include <linux/of.h> | |
22 | #include <linux/of_irq.h> | |
23 | #include <linux/of_platform.h> | |
24 | #include <linux/platform_device.h> | |
25 | #include <linux/slab.h> | |
6a7320c4 | 26 | #include <linux/acpi.h> |
e302cd93 | 27 | #include <linux/clk.h> |
7fe090bf | 28 | #include <linux/reset.h> |
ffc3ae6d | 29 | #include <linux/pm_runtime.h> |
7d4008eb | 30 | |
d5f1af7e DD |
31 | #include <asm/byteorder.h> |
32 | ||
7277b2a1 HK |
33 | #include "8250.h" |
34 | ||
30046df2 HK |
35 | /* Offsets for the DesignWare specific registers */ |
36 | #define DW_UART_USR 0x1f /* UART Status Register */ | |
37 | #define DW_UART_CPR 0xf4 /* Component Parameter Register */ | |
38 | #define DW_UART_UCV 0xf8 /* UART Component Version */ | |
39 | ||
40 | /* Component Parameter Register bits */ | |
41 | #define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0) | |
42 | #define DW_UART_CPR_AFCE_MODE (1 << 4) | |
43 | #define DW_UART_CPR_THRE_MODE (1 << 5) | |
44 | #define DW_UART_CPR_SIR_MODE (1 << 6) | |
45 | #define DW_UART_CPR_SIR_LP_MODE (1 << 7) | |
46 | #define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8) | |
47 | #define DW_UART_CPR_FIFO_ACCESS (1 << 9) | |
48 | #define DW_UART_CPR_FIFO_STAT (1 << 10) | |
49 | #define DW_UART_CPR_SHADOW (1 << 11) | |
50 | #define DW_UART_CPR_ENCODED_PARMS (1 << 12) | |
51 | #define DW_UART_CPR_DMA_EXTRA (1 << 13) | |
52 | #define DW_UART_CPR_FIFO_MODE (0xff << 16) | |
53 | /* Helper for fifo size calculation */ | |
54 | #define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16) | |
55 | ||
56 | ||
7d4008eb | 57 | struct dw8250_data { |
fe958555 | 58 | u8 usr_reg; |
fe958555 | 59 | int line; |
dfd37668 DL |
60 | int msr_mask_on; |
61 | int msr_mask_off; | |
fe958555 | 62 | struct clk *clk; |
7d78cbef | 63 | struct clk *pclk; |
7fe090bf | 64 | struct reset_control *rst; |
fe958555 | 65 | struct uart_8250_dma dma; |
4f042054 HK |
66 | |
67 | unsigned int skip_autocfg:1; | |
7d4008eb JI |
68 | }; |
69 | ||
c439c33d LP |
70 | #define BYT_PRV_CLK 0x800 |
71 | #define BYT_PRV_CLK_EN (1 << 0) | |
72 | #define BYT_PRV_CLK_M_VAL_SHIFT 1 | |
73 | #define BYT_PRV_CLK_N_VAL_SHIFT 16 | |
74 | #define BYT_PRV_CLK_UPDATE (1 << 31) | |
75 | ||
33acbb82 TK |
76 | static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value) |
77 | { | |
78 | struct dw8250_data *d = p->private_data; | |
79 | ||
dfd37668 DL |
80 | /* Override any modem control signals if needed */ |
81 | if (offset == UART_MSR) { | |
82 | value |= d->msr_mask_on; | |
83 | value &= ~d->msr_mask_off; | |
84 | } | |
85 | ||
33acbb82 TK |
86 | return value; |
87 | } | |
88 | ||
c49436b6 TK |
89 | static void dw8250_force_idle(struct uart_port *p) |
90 | { | |
b1261c86 AS |
91 | struct uart_8250_port *up = up_to_u8250p(p); |
92 | ||
93 | serial8250_clear_and_reinit_fifos(up); | |
c49436b6 TK |
94 | (void)p->serial_in(p, UART_RX); |
95 | } | |
96 | ||
7d4008eb JI |
97 | static void dw8250_serial_out(struct uart_port *p, int offset, int value) |
98 | { | |
33acbb82 | 99 | writeb(value, p->membase + (offset << p->regshift)); |
c49436b6 TK |
100 | |
101 | /* Make sure LCR write wasn't ignored */ | |
102 | if (offset == UART_LCR) { | |
103 | int tries = 1000; | |
104 | while (tries--) { | |
6979f8d2 JH |
105 | unsigned int lcr = p->serial_in(p, UART_LCR); |
106 | if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) | |
c49436b6 TK |
107 | return; |
108 | dw8250_force_idle(p); | |
109 | writeb(value, p->membase + (UART_LCR << p->regshift)); | |
110 | } | |
7fd6f640 PH |
111 | /* |
112 | * FIXME: this deadlocks if port->lock is already held | |
113 | * dev_err(p->dev, "Couldn't set LCR to %d\n", value); | |
114 | */ | |
c49436b6 | 115 | } |
7d4008eb JI |
116 | } |
117 | ||
118 | static unsigned int dw8250_serial_in(struct uart_port *p, int offset) | |
119 | { | |
33acbb82 | 120 | unsigned int value = readb(p->membase + (offset << p->regshift)); |
7d4008eb | 121 | |
33acbb82 | 122 | return dw8250_modify_msr(p, offset, value); |
7d4008eb JI |
123 | } |
124 | ||
bca2092d DD |
125 | #ifdef CONFIG_64BIT |
126 | static unsigned int dw8250_serial_inq(struct uart_port *p, int offset) | |
d5f1af7e | 127 | { |
bca2092d DD |
128 | unsigned int value; |
129 | ||
130 | value = (u8)__raw_readq(p->membase + (offset << p->regshift)); | |
131 | ||
132 | return dw8250_modify_msr(p, offset, value); | |
d5f1af7e DD |
133 | } |
134 | ||
bca2092d DD |
135 | static void dw8250_serial_outq(struct uart_port *p, int offset, int value) |
136 | { | |
bca2092d DD |
137 | value &= 0xff; |
138 | __raw_writeq(value, p->membase + (offset << p->regshift)); | |
139 | /* Read back to ensure register write ordering. */ | |
140 | __raw_readq(p->membase + (UART_LCR << p->regshift)); | |
141 | ||
142 | /* Make sure LCR write wasn't ignored */ | |
143 | if (offset == UART_LCR) { | |
144 | int tries = 1000; | |
145 | while (tries--) { | |
146 | unsigned int lcr = p->serial_in(p, UART_LCR); | |
147 | if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) | |
148 | return; | |
149 | dw8250_force_idle(p); | |
150 | __raw_writeq(value & 0xff, | |
151 | p->membase + (UART_LCR << p->regshift)); | |
152 | } | |
7fd6f640 PH |
153 | /* |
154 | * FIXME: this deadlocks if port->lock is already held | |
155 | * dev_err(p->dev, "Couldn't set LCR to %d\n", value); | |
156 | */ | |
bca2092d DD |
157 | } |
158 | } | |
159 | #endif /* CONFIG_64BIT */ | |
160 | ||
7d4008eb JI |
161 | static void dw8250_serial_out32(struct uart_port *p, int offset, int value) |
162 | { | |
33acbb82 | 163 | writel(value, p->membase + (offset << p->regshift)); |
c49436b6 TK |
164 | |
165 | /* Make sure LCR write wasn't ignored */ | |
166 | if (offset == UART_LCR) { | |
167 | int tries = 1000; | |
168 | while (tries--) { | |
6979f8d2 JH |
169 | unsigned int lcr = p->serial_in(p, UART_LCR); |
170 | if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) | |
c49436b6 TK |
171 | return; |
172 | dw8250_force_idle(p); | |
173 | writel(value, p->membase + (UART_LCR << p->regshift)); | |
174 | } | |
7fd6f640 PH |
175 | /* |
176 | * FIXME: this deadlocks if port->lock is already held | |
177 | * dev_err(p->dev, "Couldn't set LCR to %d\n", value); | |
178 | */ | |
c49436b6 | 179 | } |
7d4008eb JI |
180 | } |
181 | ||
182 | static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) | |
183 | { | |
33acbb82 | 184 | unsigned int value = readl(p->membase + (offset << p->regshift)); |
7d4008eb | 185 | |
33acbb82 | 186 | return dw8250_modify_msr(p, offset, value); |
7d4008eb JI |
187 | } |
188 | ||
7d4008eb JI |
189 | static int dw8250_handle_irq(struct uart_port *p) |
190 | { | |
191 | struct dw8250_data *d = p->private_data; | |
192 | unsigned int iir = p->serial_in(p, UART_IIR); | |
193 | ||
194 | if (serial8250_handle_irq(p, iir)) { | |
195 | return 1; | |
196 | } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { | |
c49436b6 | 197 | /* Clear the USR */ |
d5f1af7e | 198 | (void)p->serial_in(p, d->usr_reg); |
7d4008eb JI |
199 | |
200 | return 1; | |
201 | } | |
202 | ||
203 | return 0; | |
204 | } | |
205 | ||
ffc3ae6d HK |
206 | static void |
207 | dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) | |
208 | { | |
209 | if (!state) | |
210 | pm_runtime_get_sync(port->dev); | |
211 | ||
212 | serial8250_do_pm(port, state, old); | |
213 | ||
214 | if (state) | |
215 | pm_runtime_put_sync_suspend(port->dev); | |
216 | } | |
217 | ||
4e26b134 HK |
218 | static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, |
219 | struct ktermios *old) | |
220 | { | |
221 | unsigned int baud = tty_termios_baud_rate(termios); | |
222 | struct dw8250_data *d = p->private_data; | |
223 | unsigned int rate; | |
224 | int ret; | |
225 | ||
226 | if (IS_ERR(d->clk) || !old) | |
227 | goto out; | |
228 | ||
229 | /* Not requesting clock rates below 1.8432Mhz */ | |
230 | if (baud < 115200) | |
231 | baud = 115200; | |
232 | ||
233 | clk_disable_unprepare(d->clk); | |
234 | rate = clk_round_rate(d->clk, baud * 16); | |
235 | ret = clk_set_rate(d->clk, rate); | |
236 | clk_prepare_enable(d->clk); | |
237 | ||
238 | if (!ret) | |
239 | p->uartclk = rate; | |
0a6c301a QZ |
240 | |
241 | p->status &= ~UPSTAT_AUTOCTS; | |
242 | if (termios->c_cflag & CRTSCTS) | |
243 | p->status |= UPSTAT_AUTOCTS; | |
244 | ||
4e26b134 HK |
245 | out: |
246 | serial8250_do_set_termios(p, termios, old); | |
247 | } | |
248 | ||
7fb8c56c HK |
249 | static bool dw8250_dma_filter(struct dma_chan *chan, void *param) |
250 | { | |
9a1870ce | 251 | return false; |
7fb8c56c HK |
252 | } |
253 | ||
d5f1af7e DD |
254 | static void dw8250_setup_port(struct uart_8250_port *up) |
255 | { | |
256 | struct uart_port *p = &up->port; | |
257 | u32 reg = readl(p->membase + DW_UART_UCV); | |
258 | ||
259 | /* | |
260 | * If the Component Version Register returns zero, we know that | |
261 | * ADDITIONAL_FEATURES are not enabled. No need to go any further. | |
262 | */ | |
263 | if (!reg) | |
264 | return; | |
265 | ||
266 | dev_dbg_ratelimited(p->dev, "Designware UART version %c.%c%c\n", | |
267 | (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); | |
268 | ||
269 | reg = readl(p->membase + DW_UART_CPR); | |
270 | if (!reg) | |
271 | return; | |
272 | ||
273 | /* Select the type based on fifo */ | |
274 | if (reg & DW_UART_CPR_FIFO_MODE) { | |
275 | p->type = PORT_16550A; | |
276 | p->flags |= UPF_FIXED_TYPE; | |
277 | p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); | |
278 | up->tx_loadsz = p->fifosize; | |
279 | up->capabilities = UART_CAP_FIFO; | |
280 | } | |
281 | ||
282 | if (reg & DW_UART_CPR_AFCE_MODE) | |
283 | up->capabilities |= UART_CAP_AFE; | |
284 | } | |
285 | ||
0788c39b HK |
286 | static bool dw8250_idma_filter(struct dma_chan *chan, void *param) |
287 | { | |
288 | struct device *dev = param; | |
289 | ||
290 | if (dev != chan->device->dev->parent) | |
291 | return false; | |
292 | ||
293 | return true; | |
294 | } | |
295 | ||
9e08fa50 | 296 | static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) |
6a7320c4 | 297 | { |
9e08fa50 HK |
298 | if (p->dev->of_node) { |
299 | struct device_node *np = p->dev->of_node; | |
300 | int id; | |
301 | ||
302 | /* get index of serial line, if found in DT aliases */ | |
303 | id = of_alias_get_id(np, "serial"); | |
304 | if (id >= 0) | |
305 | p->line = id; | |
306 | #ifdef CONFIG_64BIT | |
307 | if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) { | |
308 | p->serial_in = dw8250_serial_inq; | |
309 | p->serial_out = dw8250_serial_outq; | |
310 | p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; | |
311 | p->type = PORT_OCTEON; | |
312 | data->usr_reg = 0x27; | |
313 | data->skip_autocfg = true; | |
314 | } | |
315 | #endif | |
316 | } else if (has_acpi_companion(p->dev)) { | |
317 | p->iotype = UPIO_MEM32; | |
318 | p->regshift = 2; | |
319 | p->serial_in = dw8250_serial_in32; | |
320 | p->serial_out = dw8250_serial_out32; | |
321 | p->set_termios = dw8250_set_termios; | |
322 | } | |
aea02e87 | 323 | |
0788c39b | 324 | /* Platforms with iDMA */ |
9e08fa50 | 325 | if (platform_get_resource_byname(to_platform_device(p->dev), |
0788c39b | 326 | IORESOURCE_MEM, "lpss_priv")) { |
9e08fa50 HK |
327 | p->set_termios = dw8250_set_termios; |
328 | data->dma.rx_param = p->dev->parent; | |
329 | data->dma.tx_param = p->dev->parent; | |
0788c39b HK |
330 | data->dma.fn = dw8250_idma_filter; |
331 | } | |
6a7320c4 HK |
332 | } |
333 | ||
9671f099 | 334 | static int dw8250_probe(struct platform_device *pdev) |
7d4008eb | 335 | { |
2655a2c7 | 336 | struct uart_8250_port uart = {}; |
7d4008eb | 337 | struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
833b1f7b | 338 | int irq = platform_get_irq(pdev, 0); |
78d3da75 | 339 | struct uart_port *p = &uart.port; |
7d4008eb | 340 | struct dw8250_data *data; |
a7260c8c | 341 | int err; |
1bd8edba | 342 | u32 val; |
7d4008eb | 343 | |
833b1f7b AB |
344 | if (!regs) { |
345 | dev_err(&pdev->dev, "no registers defined\n"); | |
7d4008eb JI |
346 | return -EINVAL; |
347 | } | |
348 | ||
833b1f7b AB |
349 | if (irq < 0) { |
350 | if (irq != -EPROBE_DEFER) | |
351 | dev_err(&pdev->dev, "cannot get irq\n"); | |
352 | return irq; | |
353 | } | |
354 | ||
78d3da75 HK |
355 | spin_lock_init(&p->lock); |
356 | p->mapbase = regs->start; | |
357 | p->irq = irq; | |
358 | p->handle_irq = dw8250_handle_irq; | |
359 | p->pm = dw8250_do_pm; | |
360 | p->type = PORT_8250; | |
361 | p->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; | |
362 | p->dev = &pdev->dev; | |
363 | p->iotype = UPIO_MEM; | |
364 | p->serial_in = dw8250_serial_in; | |
365 | p->serial_out = dw8250_serial_out; | |
366 | ||
367 | p->membase = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); | |
368 | if (!p->membase) | |
f93366ff HK |
369 | return -ENOMEM; |
370 | ||
e302cd93 EL |
371 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); |
372 | if (!data) | |
373 | return -ENOMEM; | |
374 | ||
d5f1af7e | 375 | data->usr_reg = DW_UART_USR; |
78d3da75 | 376 | p->private_data = data; |
23f5b3fd | 377 | |
1bd8edba HK |
378 | err = device_property_read_u32(p->dev, "reg-shift", &val); |
379 | if (!err) | |
380 | p->regshift = val; | |
381 | ||
382 | err = device_property_read_u32(p->dev, "reg-io-width", &val); | |
383 | if (!err && val == 4) { | |
384 | p->iotype = UPIO_MEM32; | |
385 | p->serial_in = dw8250_serial_in32; | |
386 | p->serial_out = dw8250_serial_out32; | |
387 | } | |
388 | ||
389 | if (device_property_read_bool(p->dev, "dcd-override")) { | |
390 | /* Always report DCD as active */ | |
391 | data->msr_mask_on |= UART_MSR_DCD; | |
392 | data->msr_mask_off |= UART_MSR_DDCD; | |
393 | } | |
394 | ||
395 | if (device_property_read_bool(p->dev, "dsr-override")) { | |
396 | /* Always report DSR as active */ | |
397 | data->msr_mask_on |= UART_MSR_DSR; | |
398 | data->msr_mask_off |= UART_MSR_DDSR; | |
399 | } | |
400 | ||
401 | if (device_property_read_bool(p->dev, "cts-override")) { | |
402 | /* Always report CTS as active */ | |
403 | data->msr_mask_on |= UART_MSR_CTS; | |
404 | data->msr_mask_off |= UART_MSR_DCTS; | |
405 | } | |
406 | ||
407 | if (device_property_read_bool(p->dev, "ri-override")) { | |
408 | /* Always report Ring indicator as inactive */ | |
409 | data->msr_mask_off |= UART_MSR_RI; | |
410 | data->msr_mask_off |= UART_MSR_TERI; | |
411 | } | |
412 | ||
23f5b3fd | 413 | /* Always ask for fixed clock rate from a property. */ |
78d3da75 | 414 | device_property_read_u32(p->dev, "clock-frequency", &p->uartclk); |
23f5b3fd HK |
415 | |
416 | /* If there is separate baudclk, get the rate from it. */ | |
7d78cbef | 417 | data->clk = devm_clk_get(&pdev->dev, "baudclk"); |
c8ed99d4 | 418 | if (IS_ERR(data->clk) && PTR_ERR(data->clk) != -EPROBE_DEFER) |
7d78cbef | 419 | data->clk = devm_clk_get(&pdev->dev, NULL); |
c8ed99d4 CYT |
420 | if (IS_ERR(data->clk) && PTR_ERR(data->clk) == -EPROBE_DEFER) |
421 | return -EPROBE_DEFER; | |
23f5b3fd | 422 | if (!IS_ERR_OR_NULL(data->clk)) { |
7d78cbef HS |
423 | err = clk_prepare_enable(data->clk); |
424 | if (err) | |
425 | dev_warn(&pdev->dev, "could not enable optional baudclk: %d\n", | |
426 | err); | |
427 | else | |
78d3da75 | 428 | p->uartclk = clk_get_rate(data->clk); |
7d78cbef HS |
429 | } |
430 | ||
23f5b3fd | 431 | /* If no clock rate is defined, fail. */ |
78d3da75 | 432 | if (!p->uartclk) { |
23f5b3fd HK |
433 | dev_err(&pdev->dev, "clock rate not defined\n"); |
434 | return -EINVAL; | |
435 | } | |
436 | ||
7d78cbef | 437 | data->pclk = devm_clk_get(&pdev->dev, "apb_pclk"); |
c8ed99d4 CYT |
438 | if (IS_ERR(data->clk) && PTR_ERR(data->clk) == -EPROBE_DEFER) { |
439 | err = -EPROBE_DEFER; | |
440 | goto err_clk; | |
441 | } | |
7d78cbef HS |
442 | if (!IS_ERR(data->pclk)) { |
443 | err = clk_prepare_enable(data->pclk); | |
444 | if (err) { | |
445 | dev_err(&pdev->dev, "could not enable apb_pclk\n"); | |
c8ed99d4 | 446 | goto err_clk; |
7d78cbef | 447 | } |
e302cd93 EL |
448 | } |
449 | ||
7fe090bf | 450 | data->rst = devm_reset_control_get_optional(&pdev->dev, NULL); |
c8ed99d4 CYT |
451 | if (IS_ERR(data->rst) && PTR_ERR(data->rst) == -EPROBE_DEFER) { |
452 | err = -EPROBE_DEFER; | |
453 | goto err_pclk; | |
454 | } | |
7fe090bf CYT |
455 | if (!IS_ERR(data->rst)) |
456 | reset_control_deassert(data->rst); | |
457 | ||
7fb8c56c HK |
458 | data->dma.rx_param = data; |
459 | data->dma.tx_param = data; | |
460 | data->dma.fn = dw8250_dma_filter; | |
461 | ||
9e08fa50 | 462 | dw8250_quirks(p, data); |
7d4008eb | 463 | |
4f042054 HK |
464 | if (!data->skip_autocfg) |
465 | dw8250_setup_port(&uart); | |
466 | ||
2559318c HK |
467 | /* If we have a valid fifosize, try hooking up DMA */ |
468 | if (p->fifosize) { | |
469 | data->dma.rxconf.src_maxburst = p->fifosize / 4; | |
470 | data->dma.txconf.dst_maxburst = p->fifosize / 4; | |
471 | uart.dma = &data->dma; | |
472 | } | |
473 | ||
2655a2c7 | 474 | data->line = serial8250_register_8250_port(&uart); |
c8ed99d4 CYT |
475 | if (data->line < 0) { |
476 | err = data->line; | |
477 | goto err_reset; | |
478 | } | |
7d4008eb JI |
479 | |
480 | platform_set_drvdata(pdev, data); | |
481 | ||
ffc3ae6d HK |
482 | pm_runtime_set_active(&pdev->dev); |
483 | pm_runtime_enable(&pdev->dev); | |
484 | ||
7d4008eb | 485 | return 0; |
c8ed99d4 CYT |
486 | |
487 | err_reset: | |
488 | if (!IS_ERR(data->rst)) | |
489 | reset_control_assert(data->rst); | |
490 | ||
491 | err_pclk: | |
492 | if (!IS_ERR(data->pclk)) | |
493 | clk_disable_unprepare(data->pclk); | |
494 | ||
495 | err_clk: | |
496 | if (!IS_ERR(data->clk)) | |
497 | clk_disable_unprepare(data->clk); | |
498 | ||
499 | return err; | |
7d4008eb JI |
500 | } |
501 | ||
ae8d8a14 | 502 | static int dw8250_remove(struct platform_device *pdev) |
7d4008eb JI |
503 | { |
504 | struct dw8250_data *data = platform_get_drvdata(pdev); | |
505 | ||
ffc3ae6d HK |
506 | pm_runtime_get_sync(&pdev->dev); |
507 | ||
7d4008eb JI |
508 | serial8250_unregister_port(data->line); |
509 | ||
7fe090bf CYT |
510 | if (!IS_ERR(data->rst)) |
511 | reset_control_assert(data->rst); | |
512 | ||
7d78cbef HS |
513 | if (!IS_ERR(data->pclk)) |
514 | clk_disable_unprepare(data->pclk); | |
515 | ||
e302cd93 EL |
516 | if (!IS_ERR(data->clk)) |
517 | clk_disable_unprepare(data->clk); | |
518 | ||
ffc3ae6d HK |
519 | pm_runtime_disable(&pdev->dev); |
520 | pm_runtime_put_noidle(&pdev->dev); | |
521 | ||
7d4008eb JI |
522 | return 0; |
523 | } | |
524 | ||
13b949f0 | 525 | #ifdef CONFIG_PM_SLEEP |
ffc3ae6d | 526 | static int dw8250_suspend(struct device *dev) |
b61c5ed5 | 527 | { |
ffc3ae6d | 528 | struct dw8250_data *data = dev_get_drvdata(dev); |
b61c5ed5 JH |
529 | |
530 | serial8250_suspend_port(data->line); | |
531 | ||
532 | return 0; | |
533 | } | |
534 | ||
ffc3ae6d | 535 | static int dw8250_resume(struct device *dev) |
b61c5ed5 | 536 | { |
ffc3ae6d | 537 | struct dw8250_data *data = dev_get_drvdata(dev); |
b61c5ed5 JH |
538 | |
539 | serial8250_resume_port(data->line); | |
540 | ||
541 | return 0; | |
542 | } | |
13b949f0 | 543 | #endif /* CONFIG_PM_SLEEP */ |
b61c5ed5 | 544 | |
d39fe4e5 | 545 | #ifdef CONFIG_PM |
ffc3ae6d HK |
546 | static int dw8250_runtime_suspend(struct device *dev) |
547 | { | |
548 | struct dw8250_data *data = dev_get_drvdata(dev); | |
549 | ||
dbd2df85 EG |
550 | if (!IS_ERR(data->clk)) |
551 | clk_disable_unprepare(data->clk); | |
ffc3ae6d | 552 | |
7d78cbef HS |
553 | if (!IS_ERR(data->pclk)) |
554 | clk_disable_unprepare(data->pclk); | |
555 | ||
ffc3ae6d HK |
556 | return 0; |
557 | } | |
558 | ||
559 | static int dw8250_runtime_resume(struct device *dev) | |
560 | { | |
561 | struct dw8250_data *data = dev_get_drvdata(dev); | |
562 | ||
7d78cbef HS |
563 | if (!IS_ERR(data->pclk)) |
564 | clk_prepare_enable(data->pclk); | |
565 | ||
dbd2df85 EG |
566 | if (!IS_ERR(data->clk)) |
567 | clk_prepare_enable(data->clk); | |
ffc3ae6d HK |
568 | |
569 | return 0; | |
570 | } | |
571 | #endif | |
572 | ||
573 | static const struct dev_pm_ops dw8250_pm_ops = { | |
574 | SET_SYSTEM_SLEEP_PM_OPS(dw8250_suspend, dw8250_resume) | |
575 | SET_RUNTIME_PM_OPS(dw8250_runtime_suspend, dw8250_runtime_resume, NULL) | |
576 | }; | |
577 | ||
a7260c8c | 578 | static const struct of_device_id dw8250_of_match[] = { |
7d4008eb | 579 | { .compatible = "snps,dw-apb-uart" }, |
d5f1af7e | 580 | { .compatible = "cavium,octeon-3860-uart" }, |
7d4008eb JI |
581 | { /* Sentinel */ } |
582 | }; | |
a7260c8c | 583 | MODULE_DEVICE_TABLE(of, dw8250_of_match); |
7d4008eb | 584 | |
6a7320c4 | 585 | static const struct acpi_device_id dw8250_acpi_match[] = { |
aea02e87 HK |
586 | { "INT33C4", 0 }, |
587 | { "INT33C5", 0 }, | |
d24c195f MW |
588 | { "INT3434", 0 }, |
589 | { "INT3435", 0 }, | |
4e26b134 | 590 | { "80860F0A", 0 }, |
f174442e | 591 | { "8086228A", 0 }, |
5e1aeea5 | 592 | { "APMC0D08", 0}, |
5ef86b74 | 593 | { "AMD0020", 0 }, |
6a7320c4 HK |
594 | { }, |
595 | }; | |
596 | MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match); | |
597 | ||
7d4008eb JI |
598 | static struct platform_driver dw8250_platform_driver = { |
599 | .driver = { | |
600 | .name = "dw-apb-uart", | |
ffc3ae6d | 601 | .pm = &dw8250_pm_ops, |
a7260c8c | 602 | .of_match_table = dw8250_of_match, |
6a7320c4 | 603 | .acpi_match_table = ACPI_PTR(dw8250_acpi_match), |
7d4008eb JI |
604 | }, |
605 | .probe = dw8250_probe, | |
2d47b716 | 606 | .remove = dw8250_remove, |
7d4008eb JI |
607 | }; |
608 | ||
c8381c15 | 609 | module_platform_driver(dw8250_platform_driver); |
7d4008eb JI |
610 | |
611 | MODULE_AUTHOR("Jamie Iles"); | |
612 | MODULE_LICENSE("GPL"); | |
613 | MODULE_DESCRIPTION("Synopsys DesignWare 8250 serial port driver"); | |
f3ac3fc2 | 614 | MODULE_ALIAS("platform:dw-apb-uart"); |