aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/tty/serial/8250_dw.c99
-rw-r--r--drivers/tty/serial/Kconfig7
-rw-r--r--drivers/tty/serial/Makefile1
-rw-r--r--include/linux/serial_8250.h8
4 files changed, 115 insertions, 0 deletions
diff --git a/drivers/tty/serial/8250_dw.c b/drivers/tty/serial/8250_dw.c
new file mode 100644
index 000000000000..e25782aeec7a
--- /dev/null
+++ b/drivers/tty/serial/8250_dw.c
@@ -0,0 +1,99 @@
1/*
2 * Synopsys DesignWare specific 8250 operations.
3 *
4 * Copyright 2011 Picochip, Jamie Iles.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * The Synopsys DesignWare 8250 has an extra feature whereby it detects if the
12 * LCR is written whilst busy. If it is, then a busy detect interrupt is
13 * raised, the LCR needs to be rewritten and the uart status register read.
14 */
15#include <linux/io.h>
16#include <linux/serial_8250.h>
17#include <linux/serial_core.h>
18#include <linux/serial_reg.h>
19#include <linux/slab.h>
20
21struct dw8250_data {
22 int last_lcr;
23};
24
25static void dw8250_serial_out(struct uart_port *p, int offset, int value)
26{
27 struct dw8250_data *d = p->private_data;
28
29 if (offset == UART_LCR)
30 d->last_lcr = value;
31
32 offset <<= p->regshift;
33 writeb(value, p->membase + offset);
34}
35
36static unsigned int dw8250_serial_in(struct uart_port *p, int offset)
37{
38 offset <<= p->regshift;
39
40 return readb(p->membase + offset);
41}
42
43static void dw8250_serial_out32(struct uart_port *p, int offset,
44 int value)
45{
46 struct dw8250_data *d = p->private_data;
47
48 if (offset == UART_LCR)
49 d->last_lcr = value;
50
51 offset <<= p->regshift;
52 writel(value, p->membase + offset);
53}
54
55static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
56{
57 offset <<= p->regshift;
58
59 return readl(p->membase + offset);
60}
61
62/* Offset for the DesignWare's UART Status Register. */
63#define UART_USR 0x1f
64
65static int dw8250_handle_irq(struct uart_port *p)
66{
67 struct dw8250_data *d = p->private_data;
68 unsigned int iir = p->serial_in(p, UART_IIR);
69
70 if (serial8250_handle_irq(p, iir)) {
71 return 1;
72 } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
73 /* Clear the USR and write the LCR again. */
74 (void)p->serial_in(p, UART_USR);
75 p->serial_out(p, d->last_lcr, UART_LCR);
76
77 return 1;
78 }
79
80 return 0;
81}
82
83int serial8250_use_designware_io(struct uart_port *up)
84{
85 up->private_data = kzalloc(sizeof(struct dw8250_data), GFP_KERNEL);
86 if (!up->private_data)
87 return -ENOMEM;
88
89 if (up->iotype == UPIO_MEM32) {
90 up->serial_out = dw8250_serial_out32;
91 up->serial_in = dw8250_serial_in32;
92 } else {
93 up->serial_out = dw8250_serial_out;
94 up->serial_in = dw8250_serial_in;
95 }
96 up->handle_irq = dw8250_handle_irq;
97
98 return 0;
99}
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index a9b307f582e1..d2d1cc2329b7 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -267,6 +267,13 @@ config SERIAL_8250_RM9K
267 port hardware found on MIPS RM9122 and similar processors. 267 port hardware found on MIPS RM9122 and similar processors.
268 If unsure, say N. 268 If unsure, say N.
269 269
270config SERIAL_8250_DW
271 bool "Support for Synopsys DesignWare 8250 quirks"
272 depends on SERIAL_8250
273 help
274 Selecting this option will enable handling of the extra features
275 present in the Synopsys DesignWare APB UART.
276
270comment "Non-8250 serial port support" 277comment "Non-8250 serial port support"
271 278
272config SERIAL_AMBA_PL010 279config SERIAL_AMBA_PL010
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 78748136ccc8..7b59958f50ec 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o
28obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o 28obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o
29obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o 29obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
30obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o 30obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o
31obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
31obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o 32obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
32obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o 33obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
33obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o 34obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index 1f05bbeac01e..09e2dbcd7ca3 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -86,5 +86,13 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir);
86extern void serial8250_set_isa_configurator(void (*v) 86extern void serial8250_set_isa_configurator(void (*v)
87 (int port, struct uart_port *up, 87 (int port, struct uart_port *up,
88 unsigned short *capabilities)); 88 unsigned short *capabilities));
89#ifndef SERIAL_8250_DW
90extern int serial8250_use_designware_io(struct uart_port *up);
91#else
92static inline int serial8250_use_designware_io(struct uart_port *up)
93{
94 return -EIO;
95}
96#endif
89 97
90#endif 98#endif