From 2e124b4a390ca85325fae75764bef92f0547fa25 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 3 Jan 2013 15:53:06 +0100 Subject: TTY: switch tty_flip_buffer_push Now, we start converting tty buffer functions to actually use tty_port. This will allow us to get rid of the need of tty in many call sites. Only tty_port will needed and hence no more tty_port_tty_get in those paths. Now, the one where most of tty_port_tty_get gets removed: tty_flip_buffer_push. IOW we also closed all the races in drivers not using tty_port_tty_get at all yet. Also we move tty_flip_buffer_push declaration from include/linux/tty.h to include/linux/tty_flip.h to all others while we are changing it anyway. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sccnxp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/tty/serial/sccnxp.c') diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 418b495e3233..2ced871becff 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -285,10 +285,6 @@ static void sccnxp_handle_rx(struct uart_port *port) { u8 sr; unsigned int ch, flag; - struct tty_struct *tty = tty_port_tty_get(&port->state->port); - - if (!tty) - return; for (;;) { sr = sccnxp_port_read(port, SCCNXP_SR_REG); @@ -333,9 +329,7 @@ static void sccnxp_handle_rx(struct uart_port *port) uart_insert_char(port, sr, SR_OVR, ch, flag); } - tty_flip_buffer_push(tty); - - tty_kref_put(tty); + tty_flip_buffer_push(&port->state->port); } static void sccnxp_handle_tx(struct uart_port *port) -- cgit From ec063899b7b308019afa9f5eb32f0a58a6c6ee53 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 3 Dec 2012 22:23:31 +0400 Subject: serial: sccnxp: Implement polling mode This patch adds support for polling work mode, i.e. system is perform periodical check chip status when IRQ-line is not connected to CPU. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sccnxp.c | 148 ++++++++++++++++++++++++++++++-------------- 1 file changed, 103 insertions(+), 45 deletions(-) (limited to 'drivers/tty/serial/sccnxp.c') diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 2ced871becff..3a4c57e6ea1e 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -106,6 +107,7 @@ enum { struct sccnxp_port { struct uart_driver uart; struct uart_port port[SCCNXP_MAX_UARTS]; + bool opened[SCCNXP_MAX_UARTS]; const char *name; int irq; @@ -122,7 +124,10 @@ struct sccnxp_port { struct console console; #endif - struct mutex sccnxp_mutex; + spinlock_t lock; + + bool poll; + struct timer_list timer; struct sccnxp_pdata pdata; }; @@ -371,31 +376,48 @@ static void sccnxp_handle_tx(struct uart_port *port) uart_write_wakeup(port); } -static irqreturn_t sccnxp_ist(int irq, void *dev_id) +static void sccnxp_handle_events(struct sccnxp_port *s) { int i; u8 isr; - struct sccnxp_port *s = (struct sccnxp_port *)dev_id; - - mutex_lock(&s->sccnxp_mutex); - for (;;) { + do { isr = sccnxp_read(&s->port[0], SCCNXP_ISR_REG); isr &= s->imr; if (!isr) break; - dev_dbg(s->port[0].dev, "IRQ status: 0x%02x\n", isr); - for (i = 0; i < s->uart.nr; i++) { - if (isr & ISR_RXRDY(i)) + if (s->opened[i] && (isr & ISR_RXRDY(i))) sccnxp_handle_rx(&s->port[i]); - if (isr & ISR_TXRDY(i)) + if (s->opened[i] && (isr & ISR_TXRDY(i))) sccnxp_handle_tx(&s->port[i]); } - } + } while (1); +} + +static void sccnxp_timer(unsigned long data) +{ + struct sccnxp_port *s = (struct sccnxp_port *)data; + unsigned long flags; - mutex_unlock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); + sccnxp_handle_events(s); + spin_unlock_irqrestore(&s->lock, flags); + + if (!timer_pending(&s->timer)) + mod_timer(&s->timer, jiffies + + usecs_to_jiffies(s->pdata.poll_time_us)); +} + +static irqreturn_t sccnxp_ist(int irq, void *dev_id) +{ + struct sccnxp_port *s = (struct sccnxp_port *)dev_id; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + sccnxp_handle_events(s); + spin_unlock_irqrestore(&s->lock, flags); return IRQ_HANDLED; } @@ -403,8 +425,9 @@ static irqreturn_t sccnxp_ist(int irq, void *dev_id) static void sccnxp_start_tx(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); /* Set direction to output */ if (s->flags & SCCNXP_HAVE_IO) @@ -412,7 +435,7 @@ static void sccnxp_start_tx(struct uart_port *port) sccnxp_enable_irq(port, IMR_TXRDY); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static void sccnxp_stop_tx(struct uart_port *port) @@ -423,20 +446,22 @@ static void sccnxp_stop_tx(struct uart_port *port) static void sccnxp_stop_rx(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static unsigned int sccnxp_tx_empty(struct uart_port *port) { u8 val; + unsigned long flags; struct sccnxp_port *s = dev_get_drvdata(port->dev); - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); val = sccnxp_port_read(port, SCCNXP_SR_REG); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); return (val & SR_TXEMT) ? TIOCSER_TEMT : 0; } @@ -449,28 +474,30 @@ static void sccnxp_enable_ms(struct uart_port *port) static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; if (!(s->flags & SCCNXP_HAVE_IO)) return; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); sccnxp_set_bit(port, DTR_OP, mctrl & TIOCM_DTR); sccnxp_set_bit(port, RTS_OP, mctrl & TIOCM_RTS); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static unsigned int sccnxp_get_mctrl(struct uart_port *port) { u8 bitmask, ipr; + unsigned long flags; struct sccnxp_port *s = dev_get_drvdata(port->dev); unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; if (!(s->flags & SCCNXP_HAVE_IO)) return mctrl; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); ipr = ~sccnxp_read(port, SCCNXP_IPCR_REG); @@ -499,7 +526,7 @@ static unsigned int sccnxp_get_mctrl(struct uart_port *port) mctrl |= (ipr & bitmask) ? TIOCM_RNG : 0; } - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); return mctrl; } @@ -507,21 +534,23 @@ static unsigned int sccnxp_get_mctrl(struct uart_port *port) static void sccnxp_break_ctl(struct uart_port *port, int break_state) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); sccnxp_port_write(port, SCCNXP_CR_REG, break_state ? CR_CMD_START_BREAK : CR_CMD_STOP_BREAK); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static void sccnxp_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; u8 mr1, mr2; int baud; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); /* Mask termios capabilities we don't support */ termios->c_cflag &= ~CMSPAR; @@ -588,20 +617,22 @@ static void sccnxp_set_termios(struct uart_port *port, /* Update timeout according to new baud rate */ uart_update_timeout(port, termios->c_cflag, baud); + /* Report actual baudrate back to core */ if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); /* Enable RX & TX */ sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static int sccnxp_startup(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); if (s->flags & SCCNXP_HAVE_IO) { /* Outputs are controlled manually */ @@ -620,7 +651,9 @@ static int sccnxp_startup(struct uart_port *port) /* Enable RX interrupt */ sccnxp_enable_irq(port, IMR_RXRDY); - mutex_unlock(&s->sccnxp_mutex); + s->opened[port->line] = 1; + + spin_unlock_irqrestore(&s->lock, flags); return 0; } @@ -628,8 +661,11 @@ static int sccnxp_startup(struct uart_port *port) static void sccnxp_shutdown(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); - mutex_lock(&s->sccnxp_mutex); + s->opened[port->line] = 0; /* Disable interrupts */ sccnxp_disable_irq(port, IMR_TXRDY | IMR_RXRDY); @@ -641,7 +677,7 @@ static void sccnxp_shutdown(struct uart_port *port) if (s->flags & SCCNXP_HAVE_IO) sccnxp_set_bit(port, DIR_OP, 0); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static const char *sccnxp_type(struct uart_port *port) @@ -715,10 +751,11 @@ static void sccnxp_console_write(struct console *co, const char *c, unsigned n) { struct sccnxp_port *s = (struct sccnxp_port *)co->data; struct uart_port *port = &s->port[co->index]; + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); uart_console_write(port, c, n, sccnxp_console_putchar); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static int sccnxp_console_setup(struct console *co, char *options) @@ -757,7 +794,7 @@ static int sccnxp_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, s); - mutex_init(&s->sccnxp_mutex); + spin_lock_init(&s->lock); /* Individual chip settings */ switch (chiptype) { @@ -854,11 +891,19 @@ static int sccnxp_probe(struct platform_device *pdev) } else memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); - s->irq = platform_get_irq(pdev, 0); - if (s->irq <= 0) { - dev_err(&pdev->dev, "Missing irq resource data\n"); - ret = -ENXIO; - goto err_out; + if (pdata->poll_time_us) { + dev_info(&pdev->dev, "Using poll mode, resolution %u usecs\n", + pdata->poll_time_us); + s->poll = 1; + } + + if (!s->poll) { + s->irq = platform_get_irq(pdev, 0); + if (s->irq < 0) { + dev_err(&pdev->dev, "Missing irq resource data\n"); + ret = -ENXIO; + goto err_out; + } } /* Check input frequency */ @@ -923,13 +968,23 @@ static int sccnxp_probe(struct platform_device *pdev) if (s->pdata.init) s->pdata.init(); - ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, sccnxp_ist, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(&pdev->dev), s); - if (!ret) + if (!s->poll) { + ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, + sccnxp_ist, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + dev_name(&pdev->dev), s); + if (!ret) + return 0; + + dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq); + } else { + init_timer(&s->timer); + setup_timer(&s->timer, sccnxp_timer, (unsigned long)s); + mod_timer(&s->timer, jiffies + + usecs_to_jiffies(s->pdata.poll_time_us)); return 0; - - dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq); + } err_out: platform_set_drvdata(pdev, NULL); @@ -942,7 +997,10 @@ static int sccnxp_remove(struct platform_device *pdev) int i; struct sccnxp_port *s = platform_get_drvdata(pdev); - devm_free_irq(&pdev->dev, s->irq, s); + if (!s->poll) + devm_free_irq(&pdev->dev, s->irq, s); + else + del_timer_sync(&s->timer); for (i = 0; i < s->uart.nr; i++) uart_remove_one_port(&s->uart, &s->port[i]); -- cgit From 463dcc42e4832150eb3de804682b924f9b73a91a Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 3 Dec 2012 22:23:32 +0400 Subject: serial: sccnxp: Rename header file to match functionality Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sccnxp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty/serial/sccnxp.c') diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 3a4c57e6ea1e..c864353352c5 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #define SCCNXP_NAME "uart-sccnxp" #define SCCNXP_MAJOR 204 -- cgit From b786337d8c2867962348711e8d1211b292b6e3c5 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Thu, 17 Jan 2013 18:34:45 +0400 Subject: serial: sccnxp: Fix possible crash if no platform data supplied This patch fix possible kernel crash if no platform data supplied. We should not use platform data in this case, instead we will use default values from private driver structure. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sccnxp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/tty/serial/sccnxp.c') diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index c864353352c5..c5f0e964ec05 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -891,9 +891,9 @@ static int sccnxp_probe(struct platform_device *pdev) } else memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); - if (pdata->poll_time_us) { + if (s->pdata.poll_time_us) { dev_info(&pdev->dev, "Using poll mode, resolution %u usecs\n", - pdata->poll_time_us); + s->pdata.poll_time_us); s->poll = 1; } -- cgit From f548b96de684c86c72cd7ba019c03a7afe94fd53 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 21 Jan 2013 19:38:56 +0400 Subject: serial: sccnxp: Reset break and overrun bits in RX handler This patch adds a hardware reset the break and overflow bits for these events. Without resetting the bits they will be reported to the core every time, when once occur. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sccnxp.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/tty/serial/sccnxp.c') diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index c5f0e964ec05..c7dec1678f65 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -305,14 +305,19 @@ static void sccnxp_handle_rx(struct uart_port *port) if (unlikely(sr)) { if (sr & SR_BRK) { port->icount.brk++; + sccnxp_port_write(port, SCCNXP_CR_REG, + CR_CMD_BREAK_RESET); if (uart_handle_break(port)) continue; } else if (sr & SR_PE) port->icount.parity++; else if (sr & SR_FE) port->icount.frame++; - else if (sr & SR_OVR) + else if (sr & SR_OVR) { port->icount.overrun++; + sccnxp_port_write(port, SCCNXP_CR_REG, + CR_CMD_STATUS_RESET); + } sr &= port->read_status_mask; if (sr & SR_BRK) -- cgit From 4bbed6bc41aa76183449ade053891e28dec0ae3b Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 21 Jan 2013 19:38:57 +0400 Subject: serial: sccnxp: Make baudrate table struct static This struct is used only for driver, so it should be static. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sccnxp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/tty/serial/sccnxp.c') diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index c7dec1678f65..caccbe8fc1be 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -179,14 +179,12 @@ static int sccnxp_update_best_err(int a, int b, int *besterr) return 1; } -struct baud_table { +static const struct { u8 csr; u8 acr; u8 mr0; int baud; -}; - -const struct baud_table baud_std[] = { +} baud_std[] = { { 0, ACR_BAUD0, MR0_BAUD_NORMAL, 50, }, { 0, ACR_BAUD1, MR0_BAUD_NORMAL, 75, }, { 1, ACR_BAUD0, MR0_BAUD_NORMAL, 110, }, -- cgit