diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-21 16:41:04 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-21 16:41:04 -0500 |
commit | 21eaab6d19ed43e82ed39c8deb7f192134fb4a0e (patch) | |
tree | d995205afdcb7f47462bcd28067dc0c4ab0b7b02 /drivers/tty/serial/sccnxp.c | |
parent | 74e1a2a39355b2d3ae8c60c78d8add162c6d7183 (diff) | |
parent | 9e17df37d710f8998e9cb10a548304fe33d4a5c2 (diff) |
Merge tag 'tty-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial patches from Greg Kroah-Hartman:
"Here's the big tty/serial driver patches for 3.9-rc1.
More tty port rework and fixes from Jiri here, as well as lots of
individual serial driver updates and fixes.
All of these have been in the linux-next tree for a while."
* tag 'tty-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (140 commits)
tty: mxser: improve error handling in mxser_probe() and mxser_module_init()
serial: imx: fix uninitialized variable warning
serial: tegra: assume CONFIG_OF
TTY: do not update atime/mtime on read/write
lguest: select CONFIG_TTY to build properly.
ARM defconfigs: add missing inclusions of linux/platform_device.h
fb/exynos: include platform_device.h
ARM: sa1100/assabet: include platform_device.h directly
serial: imx: Fix recursive locking bug
pps: Fix build breakage from decoupling pps from tty
tty: Remove ancient hardpps()
pps: Additional cleanups in uart_handle_dcd_change
pps: Move timestamp read into PPS code proper
pps: Don't crash the machine when exiting will do
pps: Fix a use-after free bug when unregistering a source.
pps: Use pps_lookup_dev to reduce ldisc coupling
pps: Add pps_lookup_dev() function
tty: serial: uartlite: Support uartlite on big and little endian systems
tty: serial: uartlite: Fix sparse and checkpatch warnings
serial/arc-uart: Miscll DT related updates (Grant's review comments)
...
Fix up trivial conflicts, mostly just due to the TTY config option
clashing with the EXPERIMENTAL removal.
Diffstat (limited to 'drivers/tty/serial/sccnxp.c')
-rw-r--r-- | drivers/tty/serial/sccnxp.c | 171 |
1 files changed, 113 insertions, 58 deletions
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index e869eab180be..08dbfb88d42c 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c | |||
@@ -24,8 +24,9 @@ | |||
24 | #include <linux/io.h> | 24 | #include <linux/io.h> |
25 | #include <linux/tty.h> | 25 | #include <linux/tty.h> |
26 | #include <linux/tty_flip.h> | 26 | #include <linux/tty_flip.h> |
27 | #include <linux/spinlock.h> | ||
27 | #include <linux/platform_device.h> | 28 | #include <linux/platform_device.h> |
28 | #include <linux/platform_data/sccnxp.h> | 29 | #include <linux/platform_data/serial-sccnxp.h> |
29 | 30 | ||
30 | #define SCCNXP_NAME "uart-sccnxp" | 31 | #define SCCNXP_NAME "uart-sccnxp" |
31 | #define SCCNXP_MAJOR 204 | 32 | #define SCCNXP_MAJOR 204 |
@@ -107,6 +108,7 @@ enum { | |||
107 | struct sccnxp_port { | 108 | struct sccnxp_port { |
108 | struct uart_driver uart; | 109 | struct uart_driver uart; |
109 | struct uart_port port[SCCNXP_MAX_UARTS]; | 110 | struct uart_port port[SCCNXP_MAX_UARTS]; |
111 | bool opened[SCCNXP_MAX_UARTS]; | ||
110 | 112 | ||
111 | const char *name; | 113 | const char *name; |
112 | int irq; | 114 | int irq; |
@@ -123,7 +125,10 @@ struct sccnxp_port { | |||
123 | struct console console; | 125 | struct console console; |
124 | #endif | 126 | #endif |
125 | 127 | ||
126 | struct mutex sccnxp_mutex; | 128 | spinlock_t lock; |
129 | |||
130 | bool poll; | ||
131 | struct timer_list timer; | ||
127 | 132 | ||
128 | struct sccnxp_pdata pdata; | 133 | struct sccnxp_pdata pdata; |
129 | }; | 134 | }; |
@@ -175,14 +180,12 @@ static int sccnxp_update_best_err(int a, int b, int *besterr) | |||
175 | return 1; | 180 | return 1; |
176 | } | 181 | } |
177 | 182 | ||
178 | struct baud_table { | 183 | static const struct { |
179 | u8 csr; | 184 | u8 csr; |
180 | u8 acr; | 185 | u8 acr; |
181 | u8 mr0; | 186 | u8 mr0; |
182 | int baud; | 187 | int baud; |
183 | }; | 188 | } baud_std[] = { |
184 | |||
185 | const struct baud_table baud_std[] = { | ||
186 | { 0, ACR_BAUD0, MR0_BAUD_NORMAL, 50, }, | 189 | { 0, ACR_BAUD0, MR0_BAUD_NORMAL, 50, }, |
187 | { 0, ACR_BAUD1, MR0_BAUD_NORMAL, 75, }, | 190 | { 0, ACR_BAUD1, MR0_BAUD_NORMAL, 75, }, |
188 | { 1, ACR_BAUD0, MR0_BAUD_NORMAL, 110, }, | 191 | { 1, ACR_BAUD0, MR0_BAUD_NORMAL, 110, }, |
@@ -286,10 +289,6 @@ static void sccnxp_handle_rx(struct uart_port *port) | |||
286 | { | 289 | { |
287 | u8 sr; | 290 | u8 sr; |
288 | unsigned int ch, flag; | 291 | unsigned int ch, flag; |
289 | struct tty_struct *tty = tty_port_tty_get(&port->state->port); | ||
290 | |||
291 | if (!tty) | ||
292 | return; | ||
293 | 292 | ||
294 | for (;;) { | 293 | for (;;) { |
295 | sr = sccnxp_port_read(port, SCCNXP_SR_REG); | 294 | sr = sccnxp_port_read(port, SCCNXP_SR_REG); |
@@ -305,14 +304,19 @@ static void sccnxp_handle_rx(struct uart_port *port) | |||
305 | if (unlikely(sr)) { | 304 | if (unlikely(sr)) { |
306 | if (sr & SR_BRK) { | 305 | if (sr & SR_BRK) { |
307 | port->icount.brk++; | 306 | port->icount.brk++; |
307 | sccnxp_port_write(port, SCCNXP_CR_REG, | ||
308 | CR_CMD_BREAK_RESET); | ||
308 | if (uart_handle_break(port)) | 309 | if (uart_handle_break(port)) |
309 | continue; | 310 | continue; |
310 | } else if (sr & SR_PE) | 311 | } else if (sr & SR_PE) |
311 | port->icount.parity++; | 312 | port->icount.parity++; |
312 | else if (sr & SR_FE) | 313 | else if (sr & SR_FE) |
313 | port->icount.frame++; | 314 | port->icount.frame++; |
314 | else if (sr & SR_OVR) | 315 | else if (sr & SR_OVR) { |
315 | port->icount.overrun++; | 316 | port->icount.overrun++; |
317 | sccnxp_port_write(port, SCCNXP_CR_REG, | ||
318 | CR_CMD_STATUS_RESET); | ||
319 | } | ||
316 | 320 | ||
317 | sr &= port->read_status_mask; | 321 | sr &= port->read_status_mask; |
318 | if (sr & SR_BRK) | 322 | if (sr & SR_BRK) |
@@ -334,9 +338,7 @@ static void sccnxp_handle_rx(struct uart_port *port) | |||
334 | uart_insert_char(port, sr, SR_OVR, ch, flag); | 338 | uart_insert_char(port, sr, SR_OVR, ch, flag); |
335 | } | 339 | } |
336 | 340 | ||
337 | tty_flip_buffer_push(tty); | 341 | tty_flip_buffer_push(&port->state->port); |
338 | |||
339 | tty_kref_put(tty); | ||
340 | } | 342 | } |
341 | 343 | ||
342 | static void sccnxp_handle_tx(struct uart_port *port) | 344 | static void sccnxp_handle_tx(struct uart_port *port) |
@@ -378,31 +380,48 @@ static void sccnxp_handle_tx(struct uart_port *port) | |||
378 | uart_write_wakeup(port); | 380 | uart_write_wakeup(port); |
379 | } | 381 | } |
380 | 382 | ||
381 | static irqreturn_t sccnxp_ist(int irq, void *dev_id) | 383 | static void sccnxp_handle_events(struct sccnxp_port *s) |
382 | { | 384 | { |
383 | int i; | 385 | int i; |
384 | u8 isr; | 386 | u8 isr; |
385 | struct sccnxp_port *s = (struct sccnxp_port *)dev_id; | ||
386 | |||
387 | mutex_lock(&s->sccnxp_mutex); | ||
388 | 387 | ||
389 | for (;;) { | 388 | do { |
390 | isr = sccnxp_read(&s->port[0], SCCNXP_ISR_REG); | 389 | isr = sccnxp_read(&s->port[0], SCCNXP_ISR_REG); |
391 | isr &= s->imr; | 390 | isr &= s->imr; |
392 | if (!isr) | 391 | if (!isr) |
393 | break; | 392 | break; |
394 | 393 | ||
395 | dev_dbg(s->port[0].dev, "IRQ status: 0x%02x\n", isr); | ||
396 | |||
397 | for (i = 0; i < s->uart.nr; i++) { | 394 | for (i = 0; i < s->uart.nr; i++) { |
398 | if (isr & ISR_RXRDY(i)) | 395 | if (s->opened[i] && (isr & ISR_RXRDY(i))) |
399 | sccnxp_handle_rx(&s->port[i]); | 396 | sccnxp_handle_rx(&s->port[i]); |
400 | if (isr & ISR_TXRDY(i)) | 397 | if (s->opened[i] && (isr & ISR_TXRDY(i))) |
401 | sccnxp_handle_tx(&s->port[i]); | 398 | sccnxp_handle_tx(&s->port[i]); |
402 | } | 399 | } |
403 | } | 400 | } while (1); |
401 | } | ||
402 | |||
403 | static void sccnxp_timer(unsigned long data) | ||
404 | { | ||
405 | struct sccnxp_port *s = (struct sccnxp_port *)data; | ||
406 | unsigned long flags; | ||
404 | 407 | ||
405 | mutex_unlock(&s->sccnxp_mutex); | 408 | spin_lock_irqsave(&s->lock, flags); |
409 | sccnxp_handle_events(s); | ||
410 | spin_unlock_irqrestore(&s->lock, flags); | ||
411 | |||
412 | if (!timer_pending(&s->timer)) | ||
413 | mod_timer(&s->timer, jiffies + | ||
414 | usecs_to_jiffies(s->pdata.poll_time_us)); | ||
415 | } | ||
416 | |||
417 | static irqreturn_t sccnxp_ist(int irq, void *dev_id) | ||
418 | { | ||
419 | struct sccnxp_port *s = (struct sccnxp_port *)dev_id; | ||
420 | unsigned long flags; | ||
421 | |||
422 | spin_lock_irqsave(&s->lock, flags); | ||
423 | sccnxp_handle_events(s); | ||
424 | spin_unlock_irqrestore(&s->lock, flags); | ||
406 | 425 | ||
407 | return IRQ_HANDLED; | 426 | return IRQ_HANDLED; |
408 | } | 427 | } |
@@ -410,8 +429,9 @@ static irqreturn_t sccnxp_ist(int irq, void *dev_id) | |||
410 | static void sccnxp_start_tx(struct uart_port *port) | 429 | static void sccnxp_start_tx(struct uart_port *port) |
411 | { | 430 | { |
412 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 431 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
432 | unsigned long flags; | ||
413 | 433 | ||
414 | mutex_lock(&s->sccnxp_mutex); | 434 | spin_lock_irqsave(&s->lock, flags); |
415 | 435 | ||
416 | /* Set direction to output */ | 436 | /* Set direction to output */ |
417 | if (s->flags & SCCNXP_HAVE_IO) | 437 | if (s->flags & SCCNXP_HAVE_IO) |
@@ -419,7 +439,7 @@ static void sccnxp_start_tx(struct uart_port *port) | |||
419 | 439 | ||
420 | sccnxp_enable_irq(port, IMR_TXRDY); | 440 | sccnxp_enable_irq(port, IMR_TXRDY); |
421 | 441 | ||
422 | mutex_unlock(&s->sccnxp_mutex); | 442 | spin_unlock_irqrestore(&s->lock, flags); |
423 | } | 443 | } |
424 | 444 | ||
425 | static void sccnxp_stop_tx(struct uart_port *port) | 445 | static void sccnxp_stop_tx(struct uart_port *port) |
@@ -430,20 +450,22 @@ static void sccnxp_stop_tx(struct uart_port *port) | |||
430 | static void sccnxp_stop_rx(struct uart_port *port) | 450 | static void sccnxp_stop_rx(struct uart_port *port) |
431 | { | 451 | { |
432 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 452 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
453 | unsigned long flags; | ||
433 | 454 | ||
434 | mutex_lock(&s->sccnxp_mutex); | 455 | spin_lock_irqsave(&s->lock, flags); |
435 | sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE); | 456 | sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE); |
436 | mutex_unlock(&s->sccnxp_mutex); | 457 | spin_unlock_irqrestore(&s->lock, flags); |
437 | } | 458 | } |
438 | 459 | ||
439 | static unsigned int sccnxp_tx_empty(struct uart_port *port) | 460 | static unsigned int sccnxp_tx_empty(struct uart_port *port) |
440 | { | 461 | { |
441 | u8 val; | 462 | u8 val; |
463 | unsigned long flags; | ||
442 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 464 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
443 | 465 | ||
444 | mutex_lock(&s->sccnxp_mutex); | 466 | spin_lock_irqsave(&s->lock, flags); |
445 | val = sccnxp_port_read(port, SCCNXP_SR_REG); | 467 | val = sccnxp_port_read(port, SCCNXP_SR_REG); |
446 | mutex_unlock(&s->sccnxp_mutex); | 468 | spin_unlock_irqrestore(&s->lock, flags); |
447 | 469 | ||
448 | return (val & SR_TXEMT) ? TIOCSER_TEMT : 0; | 470 | return (val & SR_TXEMT) ? TIOCSER_TEMT : 0; |
449 | } | 471 | } |
@@ -456,28 +478,30 @@ static void sccnxp_enable_ms(struct uart_port *port) | |||
456 | static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl) | 478 | static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl) |
457 | { | 479 | { |
458 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 480 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
481 | unsigned long flags; | ||
459 | 482 | ||
460 | if (!(s->flags & SCCNXP_HAVE_IO)) | 483 | if (!(s->flags & SCCNXP_HAVE_IO)) |
461 | return; | 484 | return; |
462 | 485 | ||
463 | mutex_lock(&s->sccnxp_mutex); | 486 | spin_lock_irqsave(&s->lock, flags); |
464 | 487 | ||
465 | sccnxp_set_bit(port, DTR_OP, mctrl & TIOCM_DTR); | 488 | sccnxp_set_bit(port, DTR_OP, mctrl & TIOCM_DTR); |
466 | sccnxp_set_bit(port, RTS_OP, mctrl & TIOCM_RTS); | 489 | sccnxp_set_bit(port, RTS_OP, mctrl & TIOCM_RTS); |
467 | 490 | ||
468 | mutex_unlock(&s->sccnxp_mutex); | 491 | spin_unlock_irqrestore(&s->lock, flags); |
469 | } | 492 | } |
470 | 493 | ||
471 | static unsigned int sccnxp_get_mctrl(struct uart_port *port) | 494 | static unsigned int sccnxp_get_mctrl(struct uart_port *port) |
472 | { | 495 | { |
473 | u8 bitmask, ipr; | 496 | u8 bitmask, ipr; |
497 | unsigned long flags; | ||
474 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 498 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
475 | unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; | 499 | unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; |
476 | 500 | ||
477 | if (!(s->flags & SCCNXP_HAVE_IO)) | 501 | if (!(s->flags & SCCNXP_HAVE_IO)) |
478 | return mctrl; | 502 | return mctrl; |
479 | 503 | ||
480 | mutex_lock(&s->sccnxp_mutex); | 504 | spin_lock_irqsave(&s->lock, flags); |
481 | 505 | ||
482 | ipr = ~sccnxp_read(port, SCCNXP_IPCR_REG); | 506 | ipr = ~sccnxp_read(port, SCCNXP_IPCR_REG); |
483 | 507 | ||
@@ -506,7 +530,7 @@ static unsigned int sccnxp_get_mctrl(struct uart_port *port) | |||
506 | mctrl |= (ipr & bitmask) ? TIOCM_RNG : 0; | 530 | mctrl |= (ipr & bitmask) ? TIOCM_RNG : 0; |
507 | } | 531 | } |
508 | 532 | ||
509 | mutex_unlock(&s->sccnxp_mutex); | 533 | spin_unlock_irqrestore(&s->lock, flags); |
510 | 534 | ||
511 | return mctrl; | 535 | return mctrl; |
512 | } | 536 | } |
@@ -514,21 +538,23 @@ static unsigned int sccnxp_get_mctrl(struct uart_port *port) | |||
514 | static void sccnxp_break_ctl(struct uart_port *port, int break_state) | 538 | static void sccnxp_break_ctl(struct uart_port *port, int break_state) |
515 | { | 539 | { |
516 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 540 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
541 | unsigned long flags; | ||
517 | 542 | ||
518 | mutex_lock(&s->sccnxp_mutex); | 543 | spin_lock_irqsave(&s->lock, flags); |
519 | sccnxp_port_write(port, SCCNXP_CR_REG, break_state ? | 544 | sccnxp_port_write(port, SCCNXP_CR_REG, break_state ? |
520 | CR_CMD_START_BREAK : CR_CMD_STOP_BREAK); | 545 | CR_CMD_START_BREAK : CR_CMD_STOP_BREAK); |
521 | mutex_unlock(&s->sccnxp_mutex); | 546 | spin_unlock_irqrestore(&s->lock, flags); |
522 | } | 547 | } |
523 | 548 | ||
524 | static void sccnxp_set_termios(struct uart_port *port, | 549 | static void sccnxp_set_termios(struct uart_port *port, |
525 | struct ktermios *termios, struct ktermios *old) | 550 | struct ktermios *termios, struct ktermios *old) |
526 | { | 551 | { |
527 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 552 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
553 | unsigned long flags; | ||
528 | u8 mr1, mr2; | 554 | u8 mr1, mr2; |
529 | int baud; | 555 | int baud; |
530 | 556 | ||
531 | mutex_lock(&s->sccnxp_mutex); | 557 | spin_lock_irqsave(&s->lock, flags); |
532 | 558 | ||
533 | /* Mask termios capabilities we don't support */ | 559 | /* Mask termios capabilities we don't support */ |
534 | termios->c_cflag &= ~CMSPAR; | 560 | termios->c_cflag &= ~CMSPAR; |
@@ -595,20 +621,22 @@ static void sccnxp_set_termios(struct uart_port *port, | |||
595 | /* Update timeout according to new baud rate */ | 621 | /* Update timeout according to new baud rate */ |
596 | uart_update_timeout(port, termios->c_cflag, baud); | 622 | uart_update_timeout(port, termios->c_cflag, baud); |
597 | 623 | ||
624 | /* Report actual baudrate back to core */ | ||
598 | if (tty_termios_baud_rate(termios)) | 625 | if (tty_termios_baud_rate(termios)) |
599 | tty_termios_encode_baud_rate(termios, baud, baud); | 626 | tty_termios_encode_baud_rate(termios, baud, baud); |
600 | 627 | ||
601 | /* Enable RX & TX */ | 628 | /* Enable RX & TX */ |
602 | sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE); | 629 | sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE); |
603 | 630 | ||
604 | mutex_unlock(&s->sccnxp_mutex); | 631 | spin_unlock_irqrestore(&s->lock, flags); |
605 | } | 632 | } |
606 | 633 | ||
607 | static int sccnxp_startup(struct uart_port *port) | 634 | static int sccnxp_startup(struct uart_port *port) |
608 | { | 635 | { |
609 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 636 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
637 | unsigned long flags; | ||
610 | 638 | ||
611 | mutex_lock(&s->sccnxp_mutex); | 639 | spin_lock_irqsave(&s->lock, flags); |
612 | 640 | ||
613 | if (s->flags & SCCNXP_HAVE_IO) { | 641 | if (s->flags & SCCNXP_HAVE_IO) { |
614 | /* Outputs are controlled manually */ | 642 | /* Outputs are controlled manually */ |
@@ -627,7 +655,9 @@ static int sccnxp_startup(struct uart_port *port) | |||
627 | /* Enable RX interrupt */ | 655 | /* Enable RX interrupt */ |
628 | sccnxp_enable_irq(port, IMR_RXRDY); | 656 | sccnxp_enable_irq(port, IMR_RXRDY); |
629 | 657 | ||
630 | mutex_unlock(&s->sccnxp_mutex); | 658 | s->opened[port->line] = 1; |
659 | |||
660 | spin_unlock_irqrestore(&s->lock, flags); | ||
631 | 661 | ||
632 | return 0; | 662 | return 0; |
633 | } | 663 | } |
@@ -635,8 +665,11 @@ static int sccnxp_startup(struct uart_port *port) | |||
635 | static void sccnxp_shutdown(struct uart_port *port) | 665 | static void sccnxp_shutdown(struct uart_port *port) |
636 | { | 666 | { |
637 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 667 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
668 | unsigned long flags; | ||
638 | 669 | ||
639 | mutex_lock(&s->sccnxp_mutex); | 670 | spin_lock_irqsave(&s->lock, flags); |
671 | |||
672 | s->opened[port->line] = 0; | ||
640 | 673 | ||
641 | /* Disable interrupts */ | 674 | /* Disable interrupts */ |
642 | sccnxp_disable_irq(port, IMR_TXRDY | IMR_RXRDY); | 675 | sccnxp_disable_irq(port, IMR_TXRDY | IMR_RXRDY); |
@@ -648,7 +681,7 @@ static void sccnxp_shutdown(struct uart_port *port) | |||
648 | if (s->flags & SCCNXP_HAVE_IO) | 681 | if (s->flags & SCCNXP_HAVE_IO) |
649 | sccnxp_set_bit(port, DIR_OP, 0); | 682 | sccnxp_set_bit(port, DIR_OP, 0); |
650 | 683 | ||
651 | mutex_unlock(&s->sccnxp_mutex); | 684 | spin_unlock_irqrestore(&s->lock, flags); |
652 | } | 685 | } |
653 | 686 | ||
654 | static const char *sccnxp_type(struct uart_port *port) | 687 | static const char *sccnxp_type(struct uart_port *port) |
@@ -722,10 +755,11 @@ static void sccnxp_console_write(struct console *co, const char *c, unsigned n) | |||
722 | { | 755 | { |
723 | struct sccnxp_port *s = (struct sccnxp_port *)co->data; | 756 | struct sccnxp_port *s = (struct sccnxp_port *)co->data; |
724 | struct uart_port *port = &s->port[co->index]; | 757 | struct uart_port *port = &s->port[co->index]; |
758 | unsigned long flags; | ||
725 | 759 | ||
726 | mutex_lock(&s->sccnxp_mutex); | 760 | spin_lock_irqsave(&s->lock, flags); |
727 | uart_console_write(port, c, n, sccnxp_console_putchar); | 761 | uart_console_write(port, c, n, sccnxp_console_putchar); |
728 | mutex_unlock(&s->sccnxp_mutex); | 762 | spin_unlock_irqrestore(&s->lock, flags); |
729 | } | 763 | } |
730 | 764 | ||
731 | static int sccnxp_console_setup(struct console *co, char *options) | 765 | static int sccnxp_console_setup(struct console *co, char *options) |
@@ -764,7 +798,7 @@ static int sccnxp_probe(struct platform_device *pdev) | |||
764 | } | 798 | } |
765 | platform_set_drvdata(pdev, s); | 799 | platform_set_drvdata(pdev, s); |
766 | 800 | ||
767 | mutex_init(&s->sccnxp_mutex); | 801 | spin_lock_init(&s->lock); |
768 | 802 | ||
769 | /* Individual chip settings */ | 803 | /* Individual chip settings */ |
770 | switch (chiptype) { | 804 | switch (chiptype) { |
@@ -861,11 +895,19 @@ static int sccnxp_probe(struct platform_device *pdev) | |||
861 | } else | 895 | } else |
862 | memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); | 896 | memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); |
863 | 897 | ||
864 | s->irq = platform_get_irq(pdev, 0); | 898 | if (s->pdata.poll_time_us) { |
865 | if (s->irq <= 0) { | 899 | dev_info(&pdev->dev, "Using poll mode, resolution %u usecs\n", |
866 | dev_err(&pdev->dev, "Missing irq resource data\n"); | 900 | s->pdata.poll_time_us); |
867 | ret = -ENXIO; | 901 | s->poll = 1; |
868 | goto err_out; | 902 | } |
903 | |||
904 | if (!s->poll) { | ||
905 | s->irq = platform_get_irq(pdev, 0); | ||
906 | if (s->irq < 0) { | ||
907 | dev_err(&pdev->dev, "Missing irq resource data\n"); | ||
908 | ret = -ENXIO; | ||
909 | goto err_out; | ||
910 | } | ||
869 | } | 911 | } |
870 | 912 | ||
871 | /* Check input frequency */ | 913 | /* Check input frequency */ |
@@ -929,13 +971,23 @@ static int sccnxp_probe(struct platform_device *pdev) | |||
929 | if (s->pdata.init) | 971 | if (s->pdata.init) |
930 | s->pdata.init(); | 972 | s->pdata.init(); |
931 | 973 | ||
932 | ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, sccnxp_ist, | 974 | if (!s->poll) { |
933 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | 975 | ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, |
934 | dev_name(&pdev->dev), s); | 976 | sccnxp_ist, |
935 | if (!ret) | 977 | IRQF_TRIGGER_FALLING | |
978 | IRQF_ONESHOT, | ||
979 | dev_name(&pdev->dev), s); | ||
980 | if (!ret) | ||
981 | return 0; | ||
982 | |||
983 | dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq); | ||
984 | } else { | ||
985 | init_timer(&s->timer); | ||
986 | setup_timer(&s->timer, sccnxp_timer, (unsigned long)s); | ||
987 | mod_timer(&s->timer, jiffies + | ||
988 | usecs_to_jiffies(s->pdata.poll_time_us)); | ||
936 | return 0; | 989 | return 0; |
937 | 990 | } | |
938 | dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq); | ||
939 | 991 | ||
940 | err_out: | 992 | err_out: |
941 | platform_set_drvdata(pdev, NULL); | 993 | platform_set_drvdata(pdev, NULL); |
@@ -948,7 +1000,10 @@ static int sccnxp_remove(struct platform_device *pdev) | |||
948 | int i; | 1000 | int i; |
949 | struct sccnxp_port *s = platform_get_drvdata(pdev); | 1001 | struct sccnxp_port *s = platform_get_drvdata(pdev); |
950 | 1002 | ||
951 | devm_free_irq(&pdev->dev, s->irq, s); | 1003 | if (!s->poll) |
1004 | devm_free_irq(&pdev->dev, s->irq, s); | ||
1005 | else | ||
1006 | del_timer_sync(&s->timer); | ||
952 | 1007 | ||
953 | for (i = 0; i < s->uart.nr; i++) | 1008 | for (i = 0; i < s->uart.nr; i++) |
954 | uart_remove_one_port(&s->uart, &s->port[i]); | 1009 | uart_remove_one_port(&s->uart, &s->port[i]); |