diff options
author | Atsushi Nemoto <anemo@mba.ocn.ne.jp> | 2006-03-22 03:07:45 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-22 10:53:55 -0500 |
commit | 83485f826bea154a0ab41e9b8689105531dd7cb2 (patch) | |
tree | 0413f90dda66cabbbcbe076cea5a03cb1982c5bf /drivers/serial/serial_txx9.c | |
parent | 4024ce5e0f396447cc1e07fd65c2a1d056b066bb (diff) |
[PATCH] serial: serial_txx9 driver update
Update the serial_txx9 driver.
* More strict check in verify_port. Cleanup.
* Do not insert a char caused previous overrun.
* Fix some spin_locks.
* Do not call uart_add_one_port for absent ports.
Also, this patch removes a BROKEN tag from Kconfig. This driver has been
marked as BROKEN by removal of uart_register_port, but it has been solved
already on Sep 2005.
Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Cc: Russell King <rmk@arm.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/serial/serial_txx9.c')
-rw-r--r-- | drivers/serial/serial_txx9.c | 77 |
1 files changed, 41 insertions, 36 deletions
diff --git a/drivers/serial/serial_txx9.c b/drivers/serial/serial_txx9.c index ee98a867bc6d..141173efd463 100644 --- a/drivers/serial/serial_txx9.c +++ b/drivers/serial/serial_txx9.c | |||
@@ -33,6 +33,10 @@ | |||
33 | * 1.02 Cleanup. (import 8250.c changes) | 33 | * 1.02 Cleanup. (import 8250.c changes) |
34 | * 1.03 Fix low-latency mode. (import 8250.c changes) | 34 | * 1.03 Fix low-latency mode. (import 8250.c changes) |
35 | * 1.04 Remove usage of deprecated functions, cleanup. | 35 | * 1.04 Remove usage of deprecated functions, cleanup. |
36 | * 1.05 More strict check in verify_port. Cleanup. | ||
37 | * 1.06 Do not insert a char caused previous overrun. | ||
38 | * Fix some spin_locks. | ||
39 | * Do not call uart_add_one_port for absent ports. | ||
36 | */ | 40 | */ |
37 | #include <linux/config.h> | 41 | #include <linux/config.h> |
38 | 42 | ||
@@ -57,7 +61,7 @@ | |||
57 | #include <asm/io.h> | 61 | #include <asm/io.h> |
58 | #include <asm/irq.h> | 62 | #include <asm/irq.h> |
59 | 63 | ||
60 | static char *serial_version = "1.04"; | 64 | static char *serial_version = "1.06"; |
61 | static char *serial_name = "TX39/49 Serial driver"; | 65 | static char *serial_name = "TX39/49 Serial driver"; |
62 | 66 | ||
63 | #define PASS_LIMIT 256 | 67 | #define PASS_LIMIT 256 |
@@ -94,6 +98,8 @@ static char *serial_name = "TX39/49 Serial driver"; | |||
94 | #define UART_NR 4 | 98 | #define UART_NR 4 |
95 | #endif | 99 | #endif |
96 | 100 | ||
101 | #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) | ||
102 | |||
97 | struct uart_txx9_port { | 103 | struct uart_txx9_port { |
98 | struct uart_port port; | 104 | struct uart_port port; |
99 | 105 | ||
@@ -210,7 +216,7 @@ static inline unsigned int sio_in(struct uart_txx9_port *up, int offset) | |||
210 | { | 216 | { |
211 | switch (up->port.iotype) { | 217 | switch (up->port.iotype) { |
212 | default: | 218 | default: |
213 | return *(volatile u32 *)(up->port.membase + offset); | 219 | return __raw_readl(up->port.membase + offset); |
214 | case UPIO_PORT: | 220 | case UPIO_PORT: |
215 | return inl(up->port.iobase + offset); | 221 | return inl(up->port.iobase + offset); |
216 | } | 222 | } |
@@ -221,7 +227,7 @@ sio_out(struct uart_txx9_port *up, int offset, int value) | |||
221 | { | 227 | { |
222 | switch (up->port.iotype) { | 228 | switch (up->port.iotype) { |
223 | default: | 229 | default: |
224 | *(volatile u32 *)(up->port.membase + offset) = value; | 230 | __raw_writel(value, up->port.membase + offset); |
225 | break; | 231 | break; |
226 | case UPIO_PORT: | 232 | case UPIO_PORT: |
227 | outl(value, up->port.iobase + offset); | 233 | outl(value, up->port.iobase + offset); |
@@ -259,34 +265,19 @@ sio_quot_set(struct uart_txx9_port *up, int quot) | |||
259 | static void serial_txx9_stop_tx(struct uart_port *port) | 265 | static void serial_txx9_stop_tx(struct uart_port *port) |
260 | { | 266 | { |
261 | struct uart_txx9_port *up = (struct uart_txx9_port *)port; | 267 | struct uart_txx9_port *up = (struct uart_txx9_port *)port; |
262 | unsigned long flags; | ||
263 | |||
264 | spin_lock_irqsave(&up->port.lock, flags); | ||
265 | sio_mask(up, TXX9_SIDICR, TXX9_SIDICR_TIE); | 268 | sio_mask(up, TXX9_SIDICR, TXX9_SIDICR_TIE); |
266 | spin_unlock_irqrestore(&up->port.lock, flags); | ||
267 | } | 269 | } |
268 | 270 | ||
269 | static void serial_txx9_start_tx(struct uart_port *port) | 271 | static void serial_txx9_start_tx(struct uart_port *port) |
270 | { | 272 | { |
271 | struct uart_txx9_port *up = (struct uart_txx9_port *)port; | 273 | struct uart_txx9_port *up = (struct uart_txx9_port *)port; |
272 | unsigned long flags; | ||
273 | |||
274 | spin_lock_irqsave(&up->port.lock, flags); | ||
275 | sio_set(up, TXX9_SIDICR, TXX9_SIDICR_TIE); | 274 | sio_set(up, TXX9_SIDICR, TXX9_SIDICR_TIE); |
276 | spin_unlock_irqrestore(&up->port.lock, flags); | ||
277 | } | 275 | } |
278 | 276 | ||
279 | static void serial_txx9_stop_rx(struct uart_port *port) | 277 | static void serial_txx9_stop_rx(struct uart_port *port) |
280 | { | 278 | { |
281 | struct uart_txx9_port *up = (struct uart_txx9_port *)port; | 279 | struct uart_txx9_port *up = (struct uart_txx9_port *)port; |
282 | unsigned long flags; | ||
283 | |||
284 | spin_lock_irqsave(&up->port.lock, flags); | ||
285 | up->port.read_status_mask &= ~TXX9_SIDISR_RDIS; | 280 | up->port.read_status_mask &= ~TXX9_SIDISR_RDIS; |
286 | #if 0 | ||
287 | sio_mask(up, TXX9_SIDICR, TXX9_SIDICR_RIE); | ||
288 | #endif | ||
289 | spin_unlock_irqrestore(&up->port.lock, flags); | ||
290 | } | 281 | } |
291 | 282 | ||
292 | static void serial_txx9_enable_ms(struct uart_port *port) | 283 | static void serial_txx9_enable_ms(struct uart_port *port) |
@@ -302,12 +293,16 @@ receive_chars(struct uart_txx9_port *up, unsigned int *status, struct pt_regs *r | |||
302 | unsigned int disr = *status; | 293 | unsigned int disr = *status; |
303 | int max_count = 256; | 294 | int max_count = 256; |
304 | char flag; | 295 | char flag; |
296 | unsigned int next_ignore_status_mask; | ||
305 | 297 | ||
306 | do { | 298 | do { |
307 | ch = sio_in(up, TXX9_SIRFIFO); | 299 | ch = sio_in(up, TXX9_SIRFIFO); |
308 | flag = TTY_NORMAL; | 300 | flag = TTY_NORMAL; |
309 | up->port.icount.rx++; | 301 | up->port.icount.rx++; |
310 | 302 | ||
303 | /* mask out RFDN_MASK bit added by previous overrun */ | ||
304 | next_ignore_status_mask = | ||
305 | up->port.ignore_status_mask & ~TXX9_SIDISR_RFDN_MASK; | ||
311 | if (unlikely(disr & (TXX9_SIDISR_UBRK | TXX9_SIDISR_UPER | | 306 | if (unlikely(disr & (TXX9_SIDISR_UBRK | TXX9_SIDISR_UPER | |
312 | TXX9_SIDISR_UFER | TXX9_SIDISR_UOER))) { | 307 | TXX9_SIDISR_UFER | TXX9_SIDISR_UOER))) { |
313 | /* | 308 | /* |
@@ -328,8 +323,17 @@ receive_chars(struct uart_txx9_port *up, unsigned int *status, struct pt_regs *r | |||
328 | up->port.icount.parity++; | 323 | up->port.icount.parity++; |
329 | else if (disr & TXX9_SIDISR_UFER) | 324 | else if (disr & TXX9_SIDISR_UFER) |
330 | up->port.icount.frame++; | 325 | up->port.icount.frame++; |
331 | if (disr & TXX9_SIDISR_UOER) | 326 | if (disr & TXX9_SIDISR_UOER) { |
332 | up->port.icount.overrun++; | 327 | up->port.icount.overrun++; |
328 | /* | ||
329 | * The receiver read buffer still hold | ||
330 | * a char which caused overrun. | ||
331 | * Ignore next char by adding RFDN_MASK | ||
332 | * to ignore_status_mask temporarily. | ||
333 | */ | ||
334 | next_ignore_status_mask |= | ||
335 | TXX9_SIDISR_RFDN_MASK; | ||
336 | } | ||
333 | 337 | ||
334 | /* | 338 | /* |
335 | * Mask off conditions which should be ingored. | 339 | * Mask off conditions which should be ingored. |
@@ -349,6 +353,7 @@ receive_chars(struct uart_txx9_port *up, unsigned int *status, struct pt_regs *r | |||
349 | uart_insert_char(&up->port, disr, TXX9_SIDISR_UOER, ch, flag); | 353 | uart_insert_char(&up->port, disr, TXX9_SIDISR_UOER, ch, flag); |
350 | 354 | ||
351 | ignore_char: | 355 | ignore_char: |
356 | up->port.ignore_status_mask = next_ignore_status_mask; | ||
352 | disr = sio_in(up, TXX9_SIDISR); | 357 | disr = sio_in(up, TXX9_SIDISR); |
353 | } while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0)); | 358 | } while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0)); |
354 | spin_unlock(&up->port.lock); | 359 | spin_unlock(&up->port.lock); |
@@ -450,14 +455,11 @@ static unsigned int serial_txx9_get_mctrl(struct uart_port *port) | |||
450 | static void serial_txx9_set_mctrl(struct uart_port *port, unsigned int mctrl) | 455 | static void serial_txx9_set_mctrl(struct uart_port *port, unsigned int mctrl) |
451 | { | 456 | { |
452 | struct uart_txx9_port *up = (struct uart_txx9_port *)port; | 457 | struct uart_txx9_port *up = (struct uart_txx9_port *)port; |
453 | unsigned long flags; | ||
454 | 458 | ||
455 | spin_lock_irqsave(&up->port.lock, flags); | ||
456 | if (mctrl & TIOCM_RTS) | 459 | if (mctrl & TIOCM_RTS) |
457 | sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC); | 460 | sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC); |
458 | else | 461 | else |
459 | sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC); | 462 | sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC); |
460 | spin_unlock_irqrestore(&up->port.lock, flags); | ||
461 | } | 463 | } |
462 | 464 | ||
463 | static void serial_txx9_break_ctl(struct uart_port *port, int break_state) | 465 | static void serial_txx9_break_ctl(struct uart_port *port, int break_state) |
@@ -784,8 +786,14 @@ static void serial_txx9_config_port(struct uart_port *port, int uflags) | |||
784 | static int | 786 | static int |
785 | serial_txx9_verify_port(struct uart_port *port, struct serial_struct *ser) | 787 | serial_txx9_verify_port(struct uart_port *port, struct serial_struct *ser) |
786 | { | 788 | { |
787 | if (ser->irq < 0 || | 789 | unsigned long new_port = ser->port; |
788 | ser->baud_base < 9600 || ser->type != PORT_TXX9) | 790 | if (HIGH_BITS_OFFSET) |
791 | new_port += (unsigned long)ser->port_high << HIGH_BITS_OFFSET; | ||
792 | if (ser->type != port->type || | ||
793 | ser->irq != port->irq || | ||
794 | ser->io_type != port->iotype || | ||
795 | new_port != port->iobase || | ||
796 | (unsigned long)ser->iomem_base != port->mapbase) | ||
789 | return -EINVAL; | 797 | return -EINVAL; |
790 | return 0; | 798 | return 0; |
791 | } | 799 | } |
@@ -827,7 +835,8 @@ static void __init serial_txx9_register_ports(struct uart_driver *drv) | |||
827 | 835 | ||
828 | up->port.line = i; | 836 | up->port.line = i; |
829 | up->port.ops = &serial_txx9_pops; | 837 | up->port.ops = &serial_txx9_pops; |
830 | uart_add_one_port(drv, &up->port); | 838 | if (up->port.iobase || up->port.mapbase) |
839 | uart_add_one_port(drv, &up->port); | ||
831 | } | 840 | } |
832 | } | 841 | } |
833 | 842 | ||
@@ -927,11 +936,6 @@ static int serial_txx9_console_setup(struct console *co, char *options) | |||
927 | return -ENODEV; | 936 | return -ENODEV; |
928 | 937 | ||
929 | /* | 938 | /* |
930 | * Temporary fix. | ||
931 | */ | ||
932 | spin_lock_init(&port->lock); | ||
933 | |||
934 | /* | ||
935 | * Disable UART interrupts, set DTR and RTS high | 939 | * Disable UART interrupts, set DTR and RTS high |
936 | * and set speed. | 940 | * and set speed. |
937 | */ | 941 | */ |
@@ -1041,11 +1045,10 @@ static int __devinit serial_txx9_register_port(struct uart_port *port) | |||
1041 | mutex_lock(&serial_txx9_mutex); | 1045 | mutex_lock(&serial_txx9_mutex); |
1042 | for (i = 0; i < UART_NR; i++) { | 1046 | for (i = 0; i < UART_NR; i++) { |
1043 | uart = &serial_txx9_ports[i]; | 1047 | uart = &serial_txx9_ports[i]; |
1044 | if (uart->port.type == PORT_UNKNOWN) | 1048 | if (!(uart->port.iobase || uart->port.mapbase)) |
1045 | break; | 1049 | break; |
1046 | } | 1050 | } |
1047 | if (i < UART_NR) { | 1051 | if (i < UART_NR) { |
1048 | uart_remove_one_port(&serial_txx9_reg, &uart->port); | ||
1049 | uart->port.iobase = port->iobase; | 1052 | uart->port.iobase = port->iobase; |
1050 | uart->port.membase = port->membase; | 1053 | uart->port.membase = port->membase; |
1051 | uart->port.irq = port->irq; | 1054 | uart->port.irq = port->irq; |
@@ -1080,9 +1083,8 @@ static void __devexit serial_txx9_unregister_port(int line) | |||
1080 | uart->port.type = PORT_UNKNOWN; | 1083 | uart->port.type = PORT_UNKNOWN; |
1081 | uart->port.iobase = 0; | 1084 | uart->port.iobase = 0; |
1082 | uart->port.mapbase = 0; | 1085 | uart->port.mapbase = 0; |
1083 | uart->port.membase = 0; | 1086 | uart->port.membase = NULL; |
1084 | uart->port.dev = NULL; | 1087 | uart->port.dev = NULL; |
1085 | uart_add_one_port(&serial_txx9_reg, &uart->port); | ||
1086 | mutex_unlock(&serial_txx9_mutex); | 1088 | mutex_unlock(&serial_txx9_mutex); |
1087 | } | 1089 | } |
1088 | 1090 | ||
@@ -1198,8 +1200,11 @@ static void __exit serial_txx9_exit(void) | |||
1198 | #ifdef ENABLE_SERIAL_TXX9_PCI | 1200 | #ifdef ENABLE_SERIAL_TXX9_PCI |
1199 | pci_unregister_driver(&serial_txx9_pci_driver); | 1201 | pci_unregister_driver(&serial_txx9_pci_driver); |
1200 | #endif | 1202 | #endif |
1201 | for (i = 0; i < UART_NR; i++) | 1203 | for (i = 0; i < UART_NR; i++) { |
1202 | uart_remove_one_port(&serial_txx9_reg, &serial_txx9_ports[i].port); | 1204 | struct uart_txx9_port *up = &serial_txx9_ports[i]; |
1205 | if (up->port.iobase || up->port.mapbase) | ||
1206 | uart_remove_one_port(&serial_txx9_reg, &up->port); | ||
1207 | } | ||
1203 | 1208 | ||
1204 | uart_unregister_driver(&serial_txx9_reg); | 1209 | uart_unregister_driver(&serial_txx9_reg); |
1205 | } | 1210 | } |