diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-22 19:12:24 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-22 19:12:24 -0400 |
commit | 94b5aff4c6f72fee6b0f49d49e4fa8b204e8ded9 (patch) | |
tree | 39197121b6ef8cddaa0f4057fe24b4ced58e8982 /drivers/tty/serial/amba-pl011.c | |
parent | 5d4e2d08e7fdf7339f84a1c670d296a77e02f881 (diff) | |
parent | 59bd234b72fc29887839d792b7d6c7e8d2a577a6 (diff) |
Merge tag 'tty-3.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull TTY updates from Greg Kroah-Hartman:
"Here's the big TTY/serial driver pull request for the 3.5-rc1 merge
window.
Nothing major in here, just lots of incremental changes from Alan and
Jiri reworking some tty core things to behave better and to get a more
solid grasp on some of the nasty tty locking issues.
There are a few tty and serial driver updates in here as well.
All of this has been in the linux-next releases for a while with no
problems.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>"
* tag 'tty-3.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (115 commits)
serial: bfin_uart: Make MMR access compatible with 32 bits bf609 style controller.
serial: bfin_uart: RTS and CTS MMRs can be either 16-bit width or 32-bit width.
serial: bfin_uart: narrow the reboot condition in DMA tx interrupt
serial: bfin_uart: Adapt bf5xx serial driver to bf60x serial4 controller.
Revert "serial_core: Update buffer overrun statistics."
tty: hvc_xen: NULL dereference on allocation failure
tty: Fix LED error return
tty: Allow uart_register/unregister/register
tty: move global ldisc idle waitqueue to the individual ldisc
serial8250-em: Add DT support
serial8250-em: clk_get() IS_ERR() error handling fix
serial_core: Update buffer overrun statistics.
tty: drop the pty lock during hangup
cris: fix missing tty arg in wait_event_interruptible_tty call
tty/amiserial: Add missing argument for tty_unlock()
tty_lock: Localise the lock
pty: Lock the devpts bits privately
tty_lock: undo the old tty_lock use on the ctty
serial8250-em: Emma Mobile UART driver V2
Add missing call to uart_update_timeout()
...
Diffstat (limited to 'drivers/tty/serial/amba-pl011.c')
-rw-r--r-- | drivers/tty/serial/amba-pl011.c | 109 |
1 files changed, 18 insertions, 91 deletions
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 062ef8c2b3cb..4ad721fb8405 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c | |||
@@ -68,30 +68,6 @@ | |||
68 | #define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) | 68 | #define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) |
69 | #define UART_DUMMY_DR_RX (1 << 16) | 69 | #define UART_DUMMY_DR_RX (1 << 16) |
70 | 70 | ||
71 | |||
72 | #define UART_WA_SAVE_NR 14 | ||
73 | |||
74 | static void pl011_lockup_wa(unsigned long data); | ||
75 | static const u32 uart_wa_reg[UART_WA_SAVE_NR] = { | ||
76 | ST_UART011_DMAWM, | ||
77 | ST_UART011_TIMEOUT, | ||
78 | ST_UART011_LCRH_RX, | ||
79 | UART011_IBRD, | ||
80 | UART011_FBRD, | ||
81 | ST_UART011_LCRH_TX, | ||
82 | UART011_IFLS, | ||
83 | ST_UART011_XFCR, | ||
84 | ST_UART011_XON1, | ||
85 | ST_UART011_XON2, | ||
86 | ST_UART011_XOFF1, | ||
87 | ST_UART011_XOFF2, | ||
88 | UART011_CR, | ||
89 | UART011_IMSC | ||
90 | }; | ||
91 | |||
92 | static u32 uart_wa_regdata[UART_WA_SAVE_NR]; | ||
93 | static DECLARE_TASKLET(pl011_lockup_tlet, pl011_lockup_wa, 0); | ||
94 | |||
95 | /* There is by now at least one vendor with differing details, so handle it */ | 71 | /* There is by now at least one vendor with differing details, so handle it */ |
96 | struct vendor_data { | 72 | struct vendor_data { |
97 | unsigned int ifls; | 73 | unsigned int ifls; |
@@ -101,6 +77,7 @@ struct vendor_data { | |||
101 | bool oversampling; | 77 | bool oversampling; |
102 | bool interrupt_may_hang; /* vendor-specific */ | 78 | bool interrupt_may_hang; /* vendor-specific */ |
103 | bool dma_threshold; | 79 | bool dma_threshold; |
80 | bool cts_event_workaround; | ||
104 | }; | 81 | }; |
105 | 82 | ||
106 | static struct vendor_data vendor_arm = { | 83 | static struct vendor_data vendor_arm = { |
@@ -110,6 +87,7 @@ static struct vendor_data vendor_arm = { | |||
110 | .lcrh_rx = UART011_LCRH, | 87 | .lcrh_rx = UART011_LCRH, |
111 | .oversampling = false, | 88 | .oversampling = false, |
112 | .dma_threshold = false, | 89 | .dma_threshold = false, |
90 | .cts_event_workaround = false, | ||
113 | }; | 91 | }; |
114 | 92 | ||
115 | static struct vendor_data vendor_st = { | 93 | static struct vendor_data vendor_st = { |
@@ -120,6 +98,7 @@ static struct vendor_data vendor_st = { | |||
120 | .oversampling = true, | 98 | .oversampling = true, |
121 | .interrupt_may_hang = true, | 99 | .interrupt_may_hang = true, |
122 | .dma_threshold = true, | 100 | .dma_threshold = true, |
101 | .cts_event_workaround = true, | ||
123 | }; | 102 | }; |
124 | 103 | ||
125 | static struct uart_amba_port *amba_ports[UART_NR]; | 104 | static struct uart_amba_port *amba_ports[UART_NR]; |
@@ -1055,69 +1034,6 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) | |||
1055 | #define pl011_dma_flush_buffer NULL | 1034 | #define pl011_dma_flush_buffer NULL |
1056 | #endif | 1035 | #endif |
1057 | 1036 | ||
1058 | |||
1059 | /* | ||
1060 | * pl011_lockup_wa | ||
1061 | * This workaround aims to break the deadlock situation | ||
1062 | * when after long transfer over uart in hardware flow | ||
1063 | * control, uart interrupt registers cannot be cleared. | ||
1064 | * Hence uart transfer gets blocked. | ||
1065 | * | ||
1066 | * It is seen that during such deadlock condition ICR | ||
1067 | * don't get cleared even on multiple write. This leads | ||
1068 | * pass_counter to decrease and finally reach zero. This | ||
1069 | * can be taken as trigger point to run this UART_BT_WA. | ||
1070 | * | ||
1071 | */ | ||
1072 | static void pl011_lockup_wa(unsigned long data) | ||
1073 | { | ||
1074 | struct uart_amba_port *uap = amba_ports[0]; | ||
1075 | void __iomem *base = uap->port.membase; | ||
1076 | struct circ_buf *xmit = &uap->port.state->xmit; | ||
1077 | struct tty_struct *tty = uap->port.state->port.tty; | ||
1078 | int buf_empty_retries = 200; | ||
1079 | int loop; | ||
1080 | |||
1081 | /* Stop HCI layer from submitting data for tx */ | ||
1082 | tty->hw_stopped = 1; | ||
1083 | while (!uart_circ_empty(xmit)) { | ||
1084 | if (buf_empty_retries-- == 0) | ||
1085 | break; | ||
1086 | udelay(100); | ||
1087 | } | ||
1088 | |||
1089 | /* Backup registers */ | ||
1090 | for (loop = 0; loop < UART_WA_SAVE_NR; loop++) | ||
1091 | uart_wa_regdata[loop] = readl(base + uart_wa_reg[loop]); | ||
1092 | |||
1093 | /* Disable UART so that FIFO data is flushed out */ | ||
1094 | writew(0x00, uap->port.membase + UART011_CR); | ||
1095 | |||
1096 | /* Soft reset UART module */ | ||
1097 | if (uap->port.dev->platform_data) { | ||
1098 | struct amba_pl011_data *plat; | ||
1099 | |||
1100 | plat = uap->port.dev->platform_data; | ||
1101 | if (plat->reset) | ||
1102 | plat->reset(); | ||
1103 | } | ||
1104 | |||
1105 | /* Restore registers */ | ||
1106 | for (loop = 0; loop < UART_WA_SAVE_NR; loop++) | ||
1107 | writew(uart_wa_regdata[loop] , | ||
1108 | uap->port.membase + uart_wa_reg[loop]); | ||
1109 | |||
1110 | /* Initialise the old status of the modem signals */ | ||
1111 | uap->old_status = readw(uap->port.membase + UART01x_FR) & | ||
1112 | UART01x_FR_MODEM_ANY; | ||
1113 | |||
1114 | if (readl(base + UART011_MIS) & 0x2) | ||
1115 | printk(KERN_EMERG "UART_BT_WA: ***FAILED***\n"); | ||
1116 | |||
1117 | /* Start Tx/Rx */ | ||
1118 | tty->hw_stopped = 0; | ||
1119 | } | ||
1120 | |||
1121 | static void pl011_stop_tx(struct uart_port *port) | 1037 | static void pl011_stop_tx(struct uart_port *port) |
1122 | { | 1038 | { |
1123 | struct uart_amba_port *uap = (struct uart_amba_port *)port; | 1039 | struct uart_amba_port *uap = (struct uart_amba_port *)port; |
@@ -1246,12 +1162,26 @@ static irqreturn_t pl011_int(int irq, void *dev_id) | |||
1246 | unsigned long flags; | 1162 | unsigned long flags; |
1247 | unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; | 1163 | unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; |
1248 | int handled = 0; | 1164 | int handled = 0; |
1165 | unsigned int dummy_read; | ||
1249 | 1166 | ||
1250 | spin_lock_irqsave(&uap->port.lock, flags); | 1167 | spin_lock_irqsave(&uap->port.lock, flags); |
1251 | 1168 | ||
1252 | status = readw(uap->port.membase + UART011_MIS); | 1169 | status = readw(uap->port.membase + UART011_MIS); |
1253 | if (status) { | 1170 | if (status) { |
1254 | do { | 1171 | do { |
1172 | if (uap->vendor->cts_event_workaround) { | ||
1173 | /* workaround to make sure that all bits are unlocked.. */ | ||
1174 | writew(0x00, uap->port.membase + UART011_ICR); | ||
1175 | |||
1176 | /* | ||
1177 | * WA: introduce 26ns(1 uart clk) delay before W1C; | ||
1178 | * single apb access will incur 2 pclk(133.12Mhz) delay, | ||
1179 | * so add 2 dummy reads | ||
1180 | */ | ||
1181 | dummy_read = readw(uap->port.membase + UART011_ICR); | ||
1182 | dummy_read = readw(uap->port.membase + UART011_ICR); | ||
1183 | } | ||
1184 | |||
1255 | writew(status & ~(UART011_TXIS|UART011_RTIS| | 1185 | writew(status & ~(UART011_TXIS|UART011_RTIS| |
1256 | UART011_RXIS), | 1186 | UART011_RXIS), |
1257 | uap->port.membase + UART011_ICR); | 1187 | uap->port.membase + UART011_ICR); |
@@ -1268,11 +1198,8 @@ static irqreturn_t pl011_int(int irq, void *dev_id) | |||
1268 | if (status & UART011_TXIS) | 1198 | if (status & UART011_TXIS) |
1269 | pl011_tx_chars(uap); | 1199 | pl011_tx_chars(uap); |
1270 | 1200 | ||
1271 | if (pass_counter-- == 0) { | 1201 | if (pass_counter-- == 0) |
1272 | if (uap->interrupt_may_hang) | ||
1273 | tasklet_schedule(&pl011_lockup_tlet); | ||
1274 | break; | 1202 | break; |
1275 | } | ||
1276 | 1203 | ||
1277 | status = readw(uap->port.membase + UART011_MIS); | 1204 | status = readw(uap->port.membase + UART011_MIS); |
1278 | } while (status != 0); | 1205 | } while (status != 0); |