From 293b22650f405cb08d72470b42254047138c9a55 Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Sat, 30 Aug 2014 16:35:57 -0400 Subject: jsm: add support for additional Neo cards Add device ids for additional Neo cards. The ids come from the dgnc driver. Signed-off-by: Bill Pemberton Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index 844d5e4..af70134 100644 --- a/drivers/tty/serial/jsm/jsm.h +++ b/drivers/tty/serial/jsm/jsm.h @@ -67,6 +67,16 @@ do { \ #define MAXPORTS 8 #define MAX_STOPS_SENT 5 +/* Board ids */ +#define PCI_DEVICE_ID_NEO_4 0x00B0 +#define PCI_DEVICE_ID_NEO_1_422 0x00CC +#define PCI_DEVICE_ID_NEO_1_422_485 0x00CD +#define PCI_DEVICE_ID_NEO_2_422_485 0x00CE +#define PCIE_DEVICE_ID_NEO_8 0x00F0 +#define PCIE_DEVICE_ID_NEO_4 0x00F1 +#define PCIE_DEVICE_ID_NEO_4RJ45 0x00F2 +#define PCIE_DEVICE_ID_NEO_8RJ45 0x00F3 + /* Board type definitions */ #define T_NEO 0000 diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index a47d882..d2885a7 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -93,12 +93,34 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* store the info for the board we've found */ brd->boardnum = adapter_count++; brd->pci_dev = pdev; - if (pdev->device == PCIE_DEVICE_ID_NEO_4_IBM) + + switch (pdev->device) { + + case PCI_DEVICE_ID_NEO_2DB9: + case PCI_DEVICE_ID_NEO_2DB9PRI: + case PCI_DEVICE_ID_NEO_2RJ45: + case PCI_DEVICE_ID_NEO_2RJ45PRI: + case PCI_DEVICE_ID_NEO_2_422_485: + brd->maxports = 2; + break; + + case PCI_DEVICE_ID_NEO_4: + case PCIE_DEVICE_ID_NEO_4: + case PCIE_DEVICE_ID_NEO_4RJ45: + case PCIE_DEVICE_ID_NEO_4_IBM: brd->maxports = 4; - else if (pdev->device == PCI_DEVICE_ID_DIGI_NEO_8) + break; + + case PCI_DEVICE_ID_DIGI_NEO_8: + case PCIE_DEVICE_ID_NEO_8: + case PCIE_DEVICE_ID_NEO_8RJ45: brd->maxports = 8; - else - brd->maxports = 2; + break; + + default: + brd->maxports = 1; + break; + } spin_lock_init(&brd->bd_intr_lock); @@ -209,6 +231,14 @@ static struct pci_device_id jsm_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI), 0, 0, 3 }, { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4_IBM), 0, 0, 4 }, { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_NEO_8), 0, 0, 5 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_4), 0, 0, 6 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_1_422), 0, 0, 7 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_1_422_485), 0, 0, 8 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2_422_485), 0, 0, 9 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_8), 0, 0, 10 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4), 0, 0, 11 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4RJ45), 0, 0, 12 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_8RJ45), 0, 0, 13 }, { 0, } }; MODULE_DEVICE_TABLE(pci, jsm_pci_tbl); -- cgit v0.10.2 From 27d5775e75ec70c104479bd7fcdceb0d6e0203a8 Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Sat, 30 Aug 2014 16:35:58 -0400 Subject: staging: dgnc: remove Neo card ids from device table The Digi Neo cards are supported by the jsm driver. Remove support for these cards from dgnc. Signed-off-by: Bill Pemberton Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/dgnc/dgnc_driver.c b/drivers/staging/dgnc/dgnc_driver.c index 764613b..ad07cc6 100644 --- a/drivers/staging/dgnc/dgnc_driver.c +++ b/drivers/staging/dgnc/dgnc_driver.c @@ -128,19 +128,6 @@ static struct pci_device_id dgnc_pci_tbl[] = { { DIGI_VID, PCI_DEVICE_CLASSIC_4_422_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, { DIGI_VID, PCI_DEVICE_CLASSIC_8_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, { DIGI_VID, PCI_DEVICE_CLASSIC_8_422_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 }, - { DIGI_VID, PCI_DEVICE_NEO_4_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, - { DIGI_VID, PCI_DEVICE_NEO_8_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }, - { DIGI_VID, PCI_DEVICE_NEO_2DB9_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 }, - { DIGI_VID, PCI_DEVICE_NEO_2DB9PRI_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7 }, - { DIGI_VID, PCI_DEVICE_NEO_2RJ45_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { DIGI_VID, PCI_DEVICE_NEO_2RJ45PRI_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9 }, - { DIGI_VID, PCI_DEVICE_NEO_1_422_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10 }, - { DIGI_VID, PCI_DEVICE_NEO_1_422_485_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11 }, - { DIGI_VID, PCI_DEVICE_NEO_2_422_485_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12 }, - { DIGI_VID, PCI_DEVICE_NEO_EXPRESS_8_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 13 }, - { DIGI_VID, PCI_DEVICE_NEO_EXPRESS_4_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 14 }, - { DIGI_VID, PCI_DEVICE_NEO_EXPRESS_4RJ45_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 15 }, - { DIGI_VID, PCI_DEVICE_NEO_EXPRESS_8RJ45_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 16 }, {0,} /* 0 terminated list. */ }; MODULE_DEVICE_TABLE(pci, dgnc_pci_tbl); -- cgit v0.10.2 From e676253b19b2d269cccf67fdb1592120a0cd0676 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Tue, 5 Aug 2014 11:45:59 +0200 Subject: serial/8250: Add support for RS485 IOCTLs This patch allow the users of the 8250 infrastructure to define a handler for RS485 configration. If no handler is defined the 8250 driver will work as usual. Signed-off-by: Ricardo Ribalda Delgado Acked-by: Alan Cox -- v2:Change suggested by Alan "One Thousand Gnomes": - Move rs485 structure further down on the uart_8250_port structure drivers/tty/serial/8250/8250_core.c | 39 +++++++++++++++++++++++++++++++++++++ include/linux/serial_8250.h | 3 +++ 2 files changed, 42 insertions(+) Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 1d42dba..b28ed1b 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2843,6 +2843,42 @@ serial8250_verify_port(struct uart_port *port, struct serial_struct *ser) return 0; } +static int serial8250_ioctl(struct uart_port *port, unsigned int cmd, + unsigned long arg) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + int ret; + struct serial_rs485 rs485_config; + + if (!up->rs485_config) + return -ENOIOCTLCMD; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485_config, (void __user *)arg, + sizeof(rs485_config))) + return -EFAULT; + + ret = up->rs485_config(up, &rs485_config); + if (ret) + return ret; + + memcpy(&up->rs485, &rs485_config, sizeof(rs485_config)); + + return 0; + case TIOCGRS485: + if (copy_to_user((void __user *)arg, &up->rs485, + sizeof(up->rs485))) + return -EFAULT; + return 0; + default: + break; + } + + return -ENOIOCTLCMD; +} + static const char * serial8250_type(struct uart_port *port) { @@ -2872,6 +2908,7 @@ static struct uart_ops serial8250_pops = { .request_port = serial8250_request_port, .config_port = serial8250_config_port, .verify_port = serial8250_verify_port, + .ioctl = serial8250_ioctl, #ifdef CONFIG_CONSOLE_POLL .poll_get_char = serial8250_get_poll_char, .poll_put_char = serial8250_put_poll_char, @@ -3388,6 +3425,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->port.fifosize = up->port.fifosize; uart->tx_loadsz = up->tx_loadsz; uart->capabilities = up->capabilities; + uart->rs485_config = up->rs485_config; + uart->rs485 = up->rs485; /* Take tx_loadsz from fifosize if it wasn't set separately */ if (uart->port.fifosize && !uart->tx_loadsz) diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index f93649e..6dd6717 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -96,10 +96,13 @@ struct uart_8250_port { unsigned char msr_saved_flags; struct uart_8250_dma *dma; + struct serial_rs485 rs485; /* 8250 specific callbacks */ int (*dl_read)(struct uart_8250_port *); void (*dl_write)(struct uart_8250_port *, int); + int (*rs485_config)(struct uart_8250_port *, + struct serial_rs485 *rs485); }; static inline struct uart_8250_port *up_to_u8250p(struct uart_port *up) -- cgit v0.10.2 From 28e3fb6c4dce76d59a76755c4360d1cd5e0e226c Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Thu, 31 Jul 2014 21:22:26 +0200 Subject: serial: Add support for Fintek F81216A LPC to 4 UART This patch lets you set the RS485 cappabilites of the device through TIOCSRS485 and TIOCGRS485 as defined on Documentation/serial/serial-rs485.txt In order to probe the device, the PNP id and the device id is used. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c new file mode 100644 index 0000000..1bb28cb --- /dev/null +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -0,0 +1,249 @@ +/* + * Probe for F81216A LPC to 4 UART + * + * Based on drivers/tty/serial/8250_pnp.c, by Russell King, et al + * + * Copyright (C) 2014 Ricardo Ribalda, Qtechnology A/S + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +#include +#include +#include +#include +#include +#include "8250.h" + +#define ADDR_PORT 0x4E +#define DATA_PORT 0x4F +#define ENTRY_KEY 0x77 +#define EXIT_KEY 0xAA +#define CHIP_ID1 0x20 +#define CHIP_ID1_VAL 0x02 +#define CHIP_ID2 0x21 +#define CHIP_ID2_VAL 0x16 +#define VENDOR_ID1 0x23 +#define VENDOR_ID1_VAL 0x19 +#define VENDOR_ID2 0x24 +#define VENDOR_ID2_VAL 0x34 +#define LDN 0x7 + +#define RS485 0xF0 +#define RTS_INVERT BIT(5) +#define RS485_URA BIT(4) +#define RXW4C_IRA BIT(3) +#define TXW4C_IRA BIT(2) + +#define DRIVER_NAME "8250_fintek" + +static int fintek_8250_enter_key(void){ + + if (!request_muxed_region(ADDR_PORT, 2, DRIVER_NAME)) + return -EBUSY; + + outb(ENTRY_KEY, ADDR_PORT); + outb(ENTRY_KEY, ADDR_PORT); + return 0; +} + +static void fintek_8250_exit_key(void){ + + outb(EXIT_KEY, ADDR_PORT); + release_region(ADDR_PORT, 2); +} + +static int fintek_8250_get_index(resource_size_t base_addr) +{ + resource_size_t base[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8}; + int i; + + for (i = 0; i < ARRAY_SIZE(base); i++) + if (base_addr == base[i]) + return i; + + return -ENODEV; +} + +static int fintek_8250_check_id(void) +{ + + outb(CHIP_ID1, ADDR_PORT); + if (inb(DATA_PORT) != CHIP_ID1_VAL) + return -ENODEV; + + outb(CHIP_ID2, ADDR_PORT); + if (inb(DATA_PORT) != CHIP_ID2_VAL) + return -ENODEV; + + outb(VENDOR_ID1, ADDR_PORT); + if (inb(DATA_PORT) != VENDOR_ID1_VAL) + return -ENODEV; + + outb(VENDOR_ID2, ADDR_PORT); + if (inb(DATA_PORT) != VENDOR_ID2_VAL) + return -ENODEV; + + return 0; +} + +static int fintek_8250_rs4850_config(struct uart_8250_port *uart, + struct serial_rs485 *rs485) +{ + uint8_t config = 0; + int index = fintek_8250_get_index(uart->port.iobase); + + if (index < 0) + return -EINVAL; + + if (rs485->flags & SER_RS485_ENABLED) + memset(rs485->padding, 0, sizeof(rs485->padding)); + else + memset(rs485, 0, sizeof(*rs485)); + + rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND; + + if (rs485->delay_rts_before_send) { + rs485->delay_rts_before_send = 1; + config |= TXW4C_IRA; + } + + if (rs485->delay_rts_after_send) { + rs485->delay_rts_after_send = 1; + config |= RXW4C_IRA; + } + + if ((!!(rs485->flags & SER_RS485_RTS_ON_SEND)) == + (!!(rs485->flags & SER_RS485_RTS_AFTER_SEND))) + rs485->flags &= SER_RS485_ENABLED; + else + config |= RS485_URA; + + if (rs485->flags & SER_RS485_RTS_ON_SEND) + config |= RTS_INVERT; + + if (fintek_8250_enter_key()) + return -EBUSY; + + outb(LDN, ADDR_PORT); + outb(index, DATA_PORT); + outb(RS485, ADDR_PORT); + outb(config, DATA_PORT); + fintek_8250_exit_key(); + + return 0; +} + +static int +fintek_8250_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) +{ + int line; + struct uart_8250_port uart; + int ret; + + if (!pnp_port_valid(dev, 0)) + return -ENODEV; + + if (fintek_8250_get_index(pnp_port_start(dev, 0)) < 0) + return -ENODEV; + + /* Enable configuration registers*/ + if (fintek_8250_enter_key()) + return -EBUSY; + + /*Check ID*/ + ret = fintek_8250_check_id(); + fintek_8250_exit_key(); + if (ret) + return ret; + + memset(&uart, 0, sizeof(uart)); + if (!pnp_irq_valid(dev, 0)) + return -ENODEV; + uart.port.irq = pnp_irq(dev, 0); + uart.port.iobase = pnp_port_start(dev, 0); + uart.port.iotype = UPIO_PORT; + uart.rs485_config = fintek_8250_rs4850_config; + + uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE) + uart.port.flags |= UPF_SHARE_IRQ; + uart.port.uartclk = 1843200; + uart.port.dev = &dev->dev; + + line = serial8250_register_8250_port(&uart); + if (line < 0) + return -ENODEV; + + pnp_set_drvdata(dev, (void *)((long)line + 1)); + return 0; +} + +static void fintek_8250_remove(struct pnp_dev *dev) +{ + long line = (long)pnp_get_drvdata(dev); + + if (line) + serial8250_unregister_port(line - 1); +} + +#ifdef CONFIG_PM +static int fintek_8250_suspend(struct pnp_dev *dev, pm_message_t state) +{ + long line = (long)pnp_get_drvdata(dev); + + if (!line) + return -ENODEV; + serial8250_suspend_port(line - 1); + return 0; +} + +static int fintek_8250_resume(struct pnp_dev *dev) +{ + long line = (long)pnp_get_drvdata(dev); + + if (!line) + return -ENODEV; + serial8250_resume_port(line - 1); + return 0; +} +#else +#define fintek_8250_suspend NULL +#define fintek_8250_resume NULL +#endif /* CONFIG_PM */ + +static const struct pnp_device_id fintek_dev_table[] = { + /* Qtechnology Panel PC / IO1000 */ + { "PNP0501"}, + {} +}; + +MODULE_DEVICE_TABLE(pnp, fintek_dev_table); + +static struct pnp_driver fintek_8250_driver = { + .name = DRIVER_NAME, + .probe = fintek_8250_probe, + .remove = fintek_8250_remove, + .suspend = fintek_8250_suspend, + .resume = fintek_8250_resume, + .id_table = fintek_dev_table, +}; + +static int fintek_8250_init(void) +{ + return pnp_register_driver(&fintek_8250_driver); +} +module_init(fintek_8250_init); + +static void fintek_8250_exit(void) +{ + pnp_unregister_driver(&fintek_8250_driver); +} +module_exit(fintek_8250_exit); + +MODULE_DESCRIPTION("Fintek F812164 module"); +MODULE_AUTHOR("Ricardo Ribalda "); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 349ee59..8b5c40a 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -298,3 +298,12 @@ config SERIAL_8250_RT288X If you have a Ralink RT288x/RT305x SoC based board and want to use the serial port, say Y to this option. The driver can handle up to 2 serial ports. If unsure, say N. + +config SERIAL_8250_FINTEK + tristate "Support for Fintek F81216A LPC to 4 UART" + depends on SERIAL_8250 && PNP + help + Selecting this option will add support for the Fintek F81216A + LPC to 4 UART. This device has some RS485 functionality not available + through the PNP driver. If unsure, say N. + diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 36d68d0..e08407d 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o +obj-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o -- cgit v0.10.2 From ddea392e614736ae1c38e3a4c3b1dc427412047d Mon Sep 17 00:00:00 2001 From: Kiran Padwal Date: Tue, 5 Aug 2014 13:21:59 +0530 Subject: tty: serial: msm: remove braces {} in msm_serial.c fixed below checkpatch.pl warning: WARNING: braces {} are not necessary for any arm of this statement Signed-off-by: Kiran Padwal Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 0da0b54..6c7300b 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -190,11 +190,10 @@ static void handle_rx(struct uart_port *port) /* Mask conditions we're ignorning. */ sr &= port->read_status_mask; - if (sr & UART_SR_RX_BREAK) { + if (sr & UART_SR_RX_BREAK) flag = TTY_BREAK; - } else if (sr & UART_SR_PAR_FRAME_ERR) { + else if (sr & UART_SR_PAR_FRAME_ERR) flag = TTY_FRAME; - } if (!uart_handle_sysrq_char(port, c)) tty_insert_flip_char(tport, c, flag); -- cgit v0.10.2 From e919cefb965465eb5ae15d05b1df3be6095af100 Mon Sep 17 00:00:00 2001 From: Kiran Padwal Date: Tue, 5 Aug 2014 13:22:00 +0530 Subject: tty: serial: msm: Fix 'Missing a blank line after declarations' warning This patch fixes below checkpatch.pl warning and it remove extra blank lines: WARNING: Missing a blank line after declarations Signed-off-by: Kiran Padwal Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 6c7300b..0295493 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -314,7 +314,6 @@ static unsigned int msm_get_mctrl(struct uart_port *port) return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS; } - static void msm_reset(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); @@ -335,6 +334,7 @@ static void msm_reset(struct uart_port *port) static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl) { unsigned int mr; + mr = msm_read(port, UART_MR1); if (!(mctrl & TIOCM_RTS)) { @@ -430,7 +430,6 @@ static int msm_set_baud_rate(struct uart_port *port, unsigned int baud) return baud; } - static void msm_init_clock(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); @@ -645,6 +644,7 @@ fail_release_port: static void msm_config_port(struct uart_port *port, int flags) { int ret; + if (flags & UART_CONFIG_TYPE) { port->type = PORT_MSM; ret = msm_request_port(port); -- cgit v0.10.2 From 6a7cfe4611c5cab191da5b3934790b763c58906a Mon Sep 17 00:00:00 2001 From: Kiran Padwal Date: Tue, 5 Aug 2014 13:22:01 +0530 Subject: tty: serial: msm: Fix style warnings relating to printk() fixed below checkpatch.pl warning: WARNING: Prefer [subsystem eg: netdev]_err([subsystem]dev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... WARNING: Prefer [subsystem eg: netdev]_info([subsystem]dev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... Signed-off-by: Kiran Padwal Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 0295493..e70d4e8 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -677,7 +677,7 @@ static void msm_power(struct uart_port *port, unsigned int state, clk_disable_unprepare(msm_port->pclk); break; default: - printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state); + pr_err("msm_serial: Unknown PM state %d\n", state); } } @@ -957,7 +957,7 @@ static int __init msm_console_setup(struct console *co, char *options) msm_write(port, UART_CR_TX_ENABLE, UART_CR); } - printk(KERN_INFO "msm_serial: console setup on port #%d\n", port->line); + pr_info("msm_serial: console setup on port #%d\n", port->line); return uart_set_options(port, co, baud, parity, bits, flow); } @@ -1012,7 +1012,7 @@ static int msm_serial_probe(struct platform_device *pdev) if (unlikely(pdev->id < 0 || pdev->id >= UART_NR)) return -ENXIO; - printk(KERN_INFO "msm_serial: detected port #%d\n", pdev->id); + dev_info(&pdev->dev, "msm_serial: detected port #%d\n", pdev->id); port = get_port_from_line(pdev->id); port->dev = &pdev->dev; @@ -1037,8 +1037,7 @@ static int msm_serial_probe(struct platform_device *pdev) } port->uartclk = clk_get_rate(msm_port->clk); - printk(KERN_INFO "uartclk = %d\n", port->uartclk); - + dev_info(&pdev->dev, "uartclk = %d\n", port->uartclk); resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (unlikely(!resource)) @@ -1092,7 +1091,7 @@ static int __init msm_serial_init(void) if (unlikely(ret)) uart_unregister_driver(&msm_uart_driver); - printk(KERN_INFO "msm_serial: driver initialized\n"); + pr_info("msm_serial: driver initialized\n"); return ret; } -- cgit v0.10.2 From 6f47abcb86cd9e061013a12a3aa4748b1949b25e Mon Sep 17 00:00:00 2001 From: Kiran Padwal Date: Tue, 5 Aug 2014 13:22:02 +0530 Subject: tty: serial: msm: Fix 'else is not generally useful after a break or return' warning fixed below checkpatch.pl warning: WARNING: else is not generally useful after a break or return Signed-off-by: Kiran Padwal Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index e70d4e8..dfebbaf 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -700,8 +700,8 @@ static int msm_poll_get_char_single(struct uart_port *port) if (!(msm_read(port, UART_SR) & UART_SR_RX_READY)) return NO_POLL_CHAR; - else - return msm_read(port, rf_reg) & 0xff; + + return msm_read(port, rf_reg) & 0xff; } static int msm_poll_get_char_dm_1p3(struct uart_port *port) -- cgit v0.10.2 From 864119917de4fe041e43787681eb706e6fe86624 Mon Sep 17 00:00:00 2001 From: Kiran Padwal Date: Tue, 5 Aug 2014 13:22:03 +0530 Subject: tty: serial: msm: Fix 'void function return statements are not generally useful' warning fixed below checkpatch.pl warning: WARNING: void function return statements are not generally useful Signed-off-by: Kiran Padwal Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index dfebbaf..26df038 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -787,8 +787,6 @@ static void msm_poll_put_char(struct uart_port *port, unsigned char c) /* Enable interrupts */ msm_write(port, imr, UART_IMR); - - return; } #endif -- cgit v0.10.2 From 4d199a55c41b25f4255eaeea358f944e33e91a4b Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 5 Aug 2014 09:14:35 +0200 Subject: serial: altera: Make of_device_id arrays const Make the of_device_id arrays const, as it is handled as const by all OF functions. Signed-off-by: Tobias Klauser Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index d22e3d9..932e019 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -462,7 +462,7 @@ static int altera_jtaguart_remove(struct platform_device *pdev) } #ifdef CONFIG_OF -static struct of_device_id altera_jtaguart_match[] = { +static const struct of_device_id altera_jtaguart_match[] = { { .compatible = "ALTR,juart-1.0", }, { .compatible = "altr,juart-1.0", }, {}, diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 6a24323..1cb2cdb 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -610,7 +610,7 @@ static int altera_uart_remove(struct platform_device *pdev) } #ifdef CONFIG_OF -static struct of_device_id altera_uart_match[] = { +static const struct of_device_id altera_uart_match[] = { { .compatible = "ALTR,uart-1.0", }, { .compatible = "altr,uart-1.0", }, {}, -- cgit v0.10.2 From a1d51aa2214cea3f91611893610a2f769cada0e7 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Fri, 8 Aug 2014 13:01:21 +0200 Subject: tty: fix typo in comment of tty_termios_encode_baud_rate Signed-off-by: Matthias Brugger Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 6fd60fe..cd1285c 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -402,7 +402,7 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, #ifdef BOTHER /* If the user asked for a precise weird speed give a precise weird - answer. If they asked for a Bfoo speed they many have problems + answer. If they asked for a Bfoo speed they may have problems digesting non-exact replies so fuzz a bit */ if ((termios->c_cflag & CBAUD) == BOTHER) -- cgit v0.10.2 From 2f2dafe77df2c78e189a9fa6b1879dffd06ae5a1 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 1 Sep 2014 20:49:43 +0530 Subject: serial: serial_core.c: printk replacement printk replaced with corresponding dev_* . fixed two broken user-visible strings used by the corresponding printk. the null check for uport->dev and port->dev is removed as dev_* will check for null while printing. printing of dev_name(uport->dev) and dev_name(port->dev) also removed as those are being printed by dev_* . Signed-off-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 29a7be4..079f18d 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -892,10 +892,11 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, */ if (uport->flags & UPF_SPD_MASK) { char buf[64]; - printk(KERN_NOTICE - "%s sets custom speed on %s. This " - "is deprecated.\n", current->comm, - tty_name(port->tty, buf)); + + dev_notice(uport->dev, + "%s sets custom speed on %s. This is deprecated.\n", + current->comm, + tty_name(port->tty, buf)); } uart_change_speed(tty, state, NULL); } @@ -1975,12 +1976,9 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) for (tries = 3; !ops->tx_empty(uport) && tries; tries--) msleep(10); if (!tries) - printk(KERN_ERR "%s%s%s%d: Unable to drain " - "transmitter\n", - uport->dev ? dev_name(uport->dev) : "", - uport->dev ? ": " : "", - drv->dev_name, - drv->tty_driver->name_base + uport->line); + dev_err(uport->dev, "%s%d: Unable to drain transmitter\n", + drv->dev_name, + drv->tty_driver->name_base + uport->line); if (console_suspend_enabled || !uart_console(uport)) ops->shutdown(uport); @@ -2109,9 +2107,7 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) break; } - printk(KERN_INFO "%s%s%s%d at %s (irq = %d, base_baud = %d) is a %s\n", - port->dev ? dev_name(port->dev) : "", - port->dev ? ": " : "", + dev_info(port->dev, "%s%d at %s (irq = %d, base_baud = %d) is a %s\n", drv->dev_name, drv->tty_driver->name_base + port->line, address, port->irq, port->uartclk / 16, uart_type(port)); @@ -2640,7 +2636,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) if (likely(!IS_ERR(tty_dev))) { device_set_wakeup_capable(tty_dev, 1); } else { - printk(KERN_ERR "Cannot register tty device on line %d\n", + dev_err(uport->dev, "Cannot register tty device on line %d\n", uport->line); } @@ -2675,7 +2671,7 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) BUG_ON(in_interrupt()); if (state->uart_port != uport) - printk(KERN_ALERT "Removing wrong port: %p != %p\n", + dev_alert(uport->dev, "Removing wrong port: %p != %p\n", state->uart_port, uport); mutex_lock(&port_mutex); -- cgit v0.10.2 From 62b0a1b3e7593e0647db9ecc5e7809e4410acb81 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 6 Sep 2014 07:20:15 +0400 Subject: serial: clps711x: Use mctrl_gpio helpers for handling modem signals CLPS711X serial driver uses the system wide registers to control the modem signals. Now gpio-syscon driver can be used for this purposes. mctrl_gpio helpers allow us to create GPIO bindings for any of modem/tty control signals that extends the functionality of the driver. This patch makes such change. This change does not break any current DT bindings, since DT support for this platform is not introduced yet. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt b/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt index 12f3cf8..caaeb25 100644 --- a/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt +++ b/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt @@ -8,7 +8,8 @@ Required properties: - syscon: Phandle to SYSCON node, which contain UART control bits. Optional properties: -- uart-use-ms: Indicate the UART has modem signal (DCD, DSR, CTS). +- {rts,cts,dtr,dsr,rng,dcd}-gpios: specify a GPIO for RTS/CTS/DTR/DSR/RI/DCD + line respectively. Note: Each UART port should have an alias correctly numbered in "aliases" node. @@ -24,5 +25,7 @@ Example: interrupts = <12 13>; clocks = <&clks 11>; syscon = <&syscon1>; - uart-use-ms; + cts-gpios = <&sysgpio 0 GPIO_ACTIVE_LOW>; + dsr-gpios = <&sysgpio 1 GPIO_ACTIVE_LOW>; + dcd-gpios = <&sysgpio 2 GPIO_ACTIVE_LOW>; }; diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 26cec64..75cc59f 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -204,6 +204,7 @@ config SERIAL_CLPS711X tristate "CLPS711X serial port support" depends on ARCH_CLPS711X || COMPILE_TEST select SERIAL_CORE + select SERIAL_MCTRL_GPIO help This enables the driver for the on-chip UARTs of the Cirrus Logic EP711x/EP721x/EP731x processors. diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index f5b4c3d..acfe317 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -33,6 +33,8 @@ #include #include +#include "serial_mctrl_gpio.h" + #define UART_CLPS711X_DEVNAME "ttyCL" #define UART_CLPS711X_NR 2 #define UART_CLPS711X_MAJOR 204 @@ -62,7 +64,7 @@ struct clps711x_port { unsigned int tx_enabled; int rx_irq; struct regmap *syscon; - bool use_ms; + struct mctrl_gpios *gpios; }; static struct uart_driver clps711x_uart = { @@ -198,28 +200,17 @@ static unsigned int uart_clps711x_tx_empty(struct uart_port *port) static unsigned int uart_clps711x_get_mctrl(struct uart_port *port) { + unsigned int result = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; struct clps711x_port *s = dev_get_drvdata(port->dev); - unsigned int result = 0; - - if (s->use_ms) { - u32 sysflg = 0; - - regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); - if (sysflg & SYSFLG1_DCD) - result |= TIOCM_CAR; - if (sysflg & SYSFLG1_DSR) - result |= TIOCM_DSR; - if (sysflg & SYSFLG1_CTS) - result |= TIOCM_CTS; - } else - result = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; - return result; + return mctrl_gpio_get(s->gpios, &result); } static void uart_clps711x_set_mctrl(struct uart_port *port, unsigned int mctrl) { - /* Do nothing */ + struct clps711x_port *s = dev_get_drvdata(port->dev); + + mctrl_gpio_set(s->gpios, mctrl); } static void uart_clps711x_break_ctl(struct uart_port *port, int break_state) @@ -490,15 +481,10 @@ static int uart_clps711x_probe(struct platform_device *pdev) s->syscon = syscon_regmap_lookup_by_pdevname(syscon_name); if (IS_ERR(s->syscon)) return PTR_ERR(s->syscon); - - s->use_ms = !index; } else { s->syscon = syscon_regmap_lookup_by_phandle(np, "syscon"); if (IS_ERR(s->syscon)) return PTR_ERR(s->syscon); - - if (!index) - s->use_ms = of_property_read_bool(np, "uart-use-ms"); } s->port.line = index; @@ -513,6 +499,8 @@ static int uart_clps711x_probe(struct platform_device *pdev) platform_set_drvdata(pdev, s); + s->gpios = mctrl_gpio_init(&pdev->dev, 0); + ret = uart_add_one_port(&clps711x_uart, &s->port); if (ret) return ret; -- cgit v0.10.2 From e0525393baf07b1bb6e537ddbe7dfae3621649df Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Tue, 5 Aug 2014 21:43:42 -0700 Subject: TTY: fix decimal printf format specifiers prefixed with 0x The prefix suggests the number should be printed in hex, so use the %x specifier to do that. Found by using regex suggested by Joe Perches. Signed-off-by: Hans Wennborg Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 1deaca4..14c54e0 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -1096,7 +1096,7 @@ static int __init moxa_init(void) continue; } - printk(KERN_INFO "MOXA isa board found at 0x%.8lu and " + printk(KERN_INFO "MOXA isa board found at 0x%.8lx and " "ready (%u ports, firmware loaded)\n", baseaddr[i], brd->numPorts); -- cgit v0.10.2 From 8b374399468da1c25db5b5d436b167aafc10fbdc Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 5 Aug 2014 18:37:24 -0700 Subject: serial: msm_serial: Fix kgdb continue Frank reports that after continuing in kgdb the RX stale event doesn't occur until after the RX fifo is filled up with exactly the amount of characters programmed for the RX watermark (in this case it's 48). To read a single character from the uartdm hardware we force a stale event so that any characters in the RX packing buffer are flushed into the RX fifo immediately instead of waiting for a stale timeout or for the fifo to fill. Forcing that stale event asserts the stale interrupt but we never clear that interrupt via UART_CR_CMD_RESET_STALE_INT in the polling functions. So when kgdb continues the stale interrupt is left pending in the hardware and we don't timeout with a stale event, like we usually would if a user typed one character on the console, until the reset stale interrupt and stale event commands are sent. Frank could get things working again by running handle_rx_dm(). By putting enough characters into the fifo he could trigger a watermark interrupt, and thus cause handle_rx_dm() to run finally resetting the stale interrupt and enabling the stale event so that single characters would cause timeouts again. The fix is to just do what the interrupt routine was doing all along and clear the stale interrupt and enable the event again. Doing this also smooths over any differences in the fifo behavior between v1.3 and v1.4 hardware allowing us to skip forcing the uart into single character mode. Reviewed-by: Frank Rowand Tested-by: Frank Rowand Fixes: f7e54d7ad743 "msm_serial: Add support for poll_{get,put}_char()" Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 26df038..4f9640d 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -682,17 +682,6 @@ static void msm_power(struct uart_port *port, unsigned int state, } #ifdef CONFIG_CONSOLE_POLL -static int msm_poll_init(struct uart_port *port) -{ - struct msm_port *msm_port = UART_TO_MSM(port); - - /* Enable single character mode on RX FIFO */ - if (msm_port->is_uartdm >= UARTDM_1P4) - msm_write(port, UARTDM_DMEN_RX_SC_ENABLE, UARTDM_DMEN); - - return 0; -} - static int msm_poll_get_char_single(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); @@ -704,7 +693,7 @@ static int msm_poll_get_char_single(struct uart_port *port) return msm_read(port, rf_reg) & 0xff; } -static int msm_poll_get_char_dm_1p3(struct uart_port *port) +static int msm_poll_get_char_dm(struct uart_port *port) { int c; static u32 slop; @@ -728,6 +717,10 @@ static int msm_poll_get_char_dm_1p3(struct uart_port *port) slop = msm_read(port, UARTDM_RF); c = sp[0]; count--; + msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR); + msm_write(port, 0xFFFFFF, UARTDM_DMRX); + msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, + UART_CR); } else { c = NO_POLL_CHAR; } @@ -751,8 +744,8 @@ static int msm_poll_get_char(struct uart_port *port) imr = msm_read(port, UART_IMR); msm_write(port, 0, UART_IMR); - if (msm_port->is_uartdm == UARTDM_1P3) - c = msm_poll_get_char_dm_1p3(port); + if (msm_port->is_uartdm) + c = msm_poll_get_char_dm(port); else c = msm_poll_get_char_single(port); @@ -809,7 +802,6 @@ static struct uart_ops msm_uart_pops = { .verify_port = msm_verify_port, .pm = msm_power, #ifdef CONFIG_CONSOLE_POLL - .poll_init = msm_poll_init, .poll_get_char = msm_poll_get_char, .poll_put_char = msm_poll_put_char, #endif -- cgit v0.10.2 From b216df53848129c969a465bb9237fbc9b8fafaad Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 8 Aug 2014 00:26:15 +0400 Subject: tty: Fix potential use after free in release_one_tty In case if we're releasing the last tty reference the following call sequence is possible tty_driver_kref_put destruct_tty_driver kfree(driver); where @driver is used in next module_put call, which leads to | [ 285.964007] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 | [ 285.964007] Workqueue: events release_one_tty | [ 285.964007] task: ffff8800cc7ea5f0 ti: ffff8800cb800000 task.ti: ffff8800cb800000 | [ 285.964007] RIP: 0010:[] [] module_put+0x24/0xf4 | [ 285.964007] RSP: 0018:ffff8800cb801d48 EFLAGS: 00010213 | [ 285.964007] RAX: ffff8800cb801fd8 RBX: ffff8800ca3429d0 RCX: ffff8800cb1db400 | [ 285.964007] RDX: 0000000000000000 RSI: ffffffff817349c1 RDI: 0000000000000001 | [ 285.964007] RBP: ffff8800cb801d60 R08: ffff8800cd632b40 R09: 0000000000000000 | [ 285.964007] R10: 00000000ffffffff R11: ffff88011f40a000 R12: 6b6b6b6b6b6b6b6b | [ 285.964007] R13: ffff8800ca342520 R14: 0000000000000000 R15: ffff88011f5d8200 | [ 285.964007] FS: 0000000000000000(0000) GS:ffff88011f400000(0000) knlGS:0000000000000000 | [ 285.964007] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b | [ 285.964007] CR2: 00007faf5229d090 CR3: 0000000001c0b000 CR4: 00000000000006f0 | [ 285.964007] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 | [ 285.964007] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 | [ 285.964007] Stack: | [ 285.964007] ffff8800ca3429d0 ffff8800ca342a30 ffff8800ca342520 ffff8800cb801d88 | [ 285.964007] ffffffff8146554a ffff8800cc77cc78 ffff8800ca3429d0 ffff88011f5d3800 | [ 285.964007] ffff8800cb801e08 ffffffff810683c1 ffffffff810682ff 0000000000000046 | [ 285.964007] Call Trace: | [ 285.964007] [] release_one_tty+0x54/0xa3 | [ 285.964007] [] process_one_work+0x223/0x404 | [ 285.964007] [] ? process_one_work+0x161/0x404 | [ 285.964007] [] worker_thread+0x136/0x205 | [ 285.964007] [] ? rescuer_thread+0x26a/0x26a | [ 285.964007] [] kthread+0xa2/0xaa | [ 285.964007] [] ? trace_hardirqs_on_caller+0x16/0x1eb | [ 285.964007] [] ? __kthread_parkme+0x65/0x65 | [ 285.964007] [] ret_from_fork+0x7c/0xb0 | [ 285.964007] [] ? __kthread_parkme+0x65/0x65 | [ 285.964007] Code: 09 00 5b 41 5c 5d c3 0f 1f 44 00 00 55 48 85 ff 48 89 e5 41 55 41 54 49 89 fc 53 0f 84 d3 00 | 00 00 bf 01 00 00 00 e8 d0 a1 fc ff <49> 8b 84 24 50 02 00 00 65 48 ff 40 08 4c 8b 6d 08 0f 1f 44 00 so simply keep a local reference to the module owner and use it later. CC: Pavel Emelyanov CC: Jiri Slaby CC: Greg Kroah-Hartman Signed-off-by: Cyrill Gorcunov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 8fbad34..d4eb2a8 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1544,13 +1544,14 @@ static void release_one_tty(struct work_struct *work) struct tty_struct *tty = container_of(work, struct tty_struct, hangup_work); struct tty_driver *driver = tty->driver; + struct module *owner = driver->owner; if (tty->ops->cleanup) tty->ops->cleanup(tty); tty->magic = 0; tty_driver_kref_put(driver); - module_put(driver->owner); + module_put(owner); spin_lock(&tty_files_lock); list_del_init(&tty->tty_files); -- cgit v0.10.2 From 57087d515441cab49ff02480aa40a09abfe02c78 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 7 Aug 2014 07:14:10 -0400 Subject: tty: Fix spurious poll() wakeups When the N_TTY line discipline receives data and wakes readers to process the input, polling writers are also mistakenly woken. This is because, although readers and writers are differentiated by different wait queues (tty->read_wait & tty->write_wait), both wait queues are polled together. Thus, reader wakeups without poll flags still cause poll(POLLOUT) to wakeup. For received data, wakeup readers with POLLIN. Preserve the unspecific wakeup in n_tty_packet_mode_flush(), as this action should flag both POLLIN and POLLOUT. Fixes epoll_wait() for edge-triggered EPOLLOUT. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index f44f1ba..89c4cee 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1166,7 +1166,7 @@ static void n_tty_receive_break(struct tty_struct *tty) } put_tty_queue('\0', ldata); if (waitqueue_active(&tty->read_wait)) - wake_up_interruptible(&tty->read_wait); + wake_up_interruptible_poll(&tty->read_wait, POLLIN); } /** @@ -1226,7 +1226,7 @@ static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c) } else put_tty_queue(c, ldata); if (waitqueue_active(&tty->read_wait)) - wake_up_interruptible(&tty->read_wait); + wake_up_interruptible_poll(&tty->read_wait, POLLIN); } static void @@ -1378,7 +1378,7 @@ handle_newline: ldata->canon_head = ldata->read_head; kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) - wake_up_interruptible(&tty->read_wait); + wake_up_interruptible_poll(&tty->read_wait, POLLIN); return 0; } } @@ -1679,7 +1679,7 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) - wake_up_interruptible(&tty->read_wait); + wake_up_interruptible_poll(&tty->read_wait, POLLIN); } } -- cgit v0.10.2 From c10b73905a0bb4b91e6c153dbbf4c2809084e4c2 Mon Sep 17 00:00:00 2001 From: Daniele Forsi Date: Fri, 8 Aug 2014 17:56:30 +0200 Subject: serial: 8250_hp300: trivial: fix symbol name in #warning message The symbol is defined in drivers/tty/serial/8250/Kconfig as "SERIAL_8250", not just "8250". Signed-off-by: Daniele Forsi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_hp300.c b/drivers/tty/serial/8250/8250_hp300.c index 5bdaf27..afffe4d 100644 --- a/drivers/tty/serial/8250/8250_hp300.c +++ b/drivers/tty/serial/8250/8250_hp300.c @@ -21,7 +21,7 @@ #include "8250.h" #if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI) -#warning CONFIG_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure? +#warning CONFIG_SERIAL_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure? #endif #ifdef CONFIG_HPAPCI -- cgit v0.10.2 From 4ebe78655476e5e94ab12867eb732923cae50ea4 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 13 Aug 2014 13:53:42 +0200 Subject: tty: serial: xuartps: Remove .owner field for driver There is no need to init .owner field. Based on the patch from Peter Griffin "mmc: remove .owner field for drivers using module_platform_driver" This patch removes the superflous .owner field for drivers which use the module_platform_driver API, as this is overriden in platform_driver_register anyway." Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 01951d2..c3c252d 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1428,7 +1428,6 @@ static struct platform_driver cdns_uart_platform_driver = { .probe = cdns_uart_probe, .remove = cdns_uart_remove, .driver = { - .owner = THIS_MODULE, .name = CDNS_UART_NAME, .of_match_table = cdns_uart_of_match, .pm = &cdns_uart_dev_pm_ops, -- cgit v0.10.2 From 4bb2bd0798447c67d563311f337db9c2f390f2c6 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 13 Aug 2014 13:53:43 +0200 Subject: tty: serial: uartlite: Remove .owner field for driver There is no need to init .owner field. Based on the patch from Peter Griffin "mmc: remove .owner field for drivers using module_platform_driver" This patch removes the superflous .owner field for drivers which use the module_platform_driver API, as this is overriden in platform_driver_register anyway." Signed-off-by: Michal Simek Acked-by: Peter Korsgaard Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 9fc22f4..189f52e 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -665,7 +665,6 @@ static struct platform_driver ulite_platform_driver = { .probe = ulite_probe, .remove = ulite_remove, .driver = { - .owner = THIS_MODULE, .name = "uartlite", .of_match_table = of_match_ptr(ulite_of_match), }, -- cgit v0.10.2 From 7acb88af6fedc4424a99425308a2f3e50d5ff1bc Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Fri, 8 Aug 2014 23:56:34 +0800 Subject: drivers/tty/nozomi.c: Use 'nozomi_setup_memory' instead of 'setup_memory' Several architectures (e.g. microblaze, um, and score) have already have extern 'setup_memory', so need use 'nozomi_setup_memory' instead of, or will cause compiling issue. The related error (with allmodconfig for microblaze): CC [M] drivers/tty/nozomi.o drivers/tty/nozomi.c:526:13: error: conflicting types for 'setup_memory' static void setup_memory(struct nozomi *dc) ^ In file included from include/linux/mm.h:51:0, from ./arch/microblaze/include/asm/io.h:17, from include/linux/io.h:22, from include/linux/pci.h:31, from drivers/tty/nozomi.c:46: ./arch/microblaze/include/asm/pgtable.h:569:6: note: previous declaration of 'setup_memory' was here void setup_memory(void); ^ Signed-off-by: Chen Gang Acked-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index cd04293..74885af 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -523,7 +523,7 @@ static u32 write_mem32(void __iomem *mem_addr_start, const u32 *buf, } /* Setup pointers to different channels and also setup buffer sizes. */ -static void setup_memory(struct nozomi *dc) +static void nozomi_setup_memory(struct nozomi *dc) { void __iomem *offset = dc->base_addr + dc->config_table.dl_start; /* The length reported is including the length field of 4 bytes, @@ -671,7 +671,7 @@ static int nozomi_read_config_table(struct nozomi *dc) int i; DBG1("Second phase, configuring card"); - setup_memory(dc); + nozomi_setup_memory(dc); dc->port[PORT_MDM].toggle_ul = dc->config_table.toggle.mdm_ul; dc->port[PORT_MDM].toggle_dl = dc->config_table.toggle.mdm_dl; @@ -705,7 +705,7 @@ static int nozomi_read_config_table(struct nozomi *dc) dc->config_table.version); /* Here we should disable all I/O over F32. */ - setup_memory(dc); + nozomi_setup_memory(dc); /* * We should send ALL channel pair tokens back along -- cgit v0.10.2 From 75e4239b59393487dd79c88ebd419fda11eca465 Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Wed, 13 Aug 2014 15:54:48 +0200 Subject: Documentation: serial: fix header path RS485 related structure will be defined in user space API header. Signed-off-by: Yegor Yefremov Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt index 41c8378..39dac95 100644 --- a/Documentation/serial/serial-rs485.txt +++ b/Documentation/serial/serial-rs485.txt @@ -132,5 +132,5 @@ 5. REFERENCES - [1] include/linux/serial.h + [1] include/uapi/linux/serial.h [2] Documentation/devicetree/bindings/serial/rs485.txt -- cgit v0.10.2 From ff7693d079e58fb62d735b7b8085b53fcfb74528 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Sun, 17 Aug 2014 12:49:49 +0200 Subject: ARM: meson: serial: add MesonX SoC on-chip uart driver The SoC has four fully functional UARTs which use the same programming model. They are named UART_A, UART_B, UART_C and UART_AO (Always-On) which cannot be powered off. Signed-off-by: Carlo Caione Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 75cc59f..636949d 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -200,6 +200,24 @@ config SERIAL_KS8695_CONSOLE receives all kernel messages and warnings and which allows logins in single user mode). +config SERIAL_MESON + tristate "Meson serial port support" + depends on ARCH_MESON + select SERIAL_CORE + help + This enables the driver for the on-chip UARTs of the Amlogic + MesonX processors. + +config SERIAL_MESON_CONSOLE + bool "Support for console on meson" + depends on SERIAL_MESON=y + select SERIAL_CORE_CONSOLE + help + Say Y here if you wish to use a Amlogic MesonX UART as the + system console (the system console is the device which + receives all kernel messages and warnings and which allows + logins in single user mode) as /dev/ttyAMLx. + config SERIAL_CLPS711X tristate "CLPS711X serial port support" depends on ARCH_CLPS711X || COMPILE_TEST diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 0080cc3..9a548ac 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o obj-$(CONFIG_SERIAL_ICOM) += icom.o obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o obj-$(CONFIG_SERIAL_MPSC) += mpsc.o +obj-$(CONFIG_SERIAL_MESON) += meson_uart.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c new file mode 100644 index 0000000..15c74975 --- /dev/null +++ b/drivers/tty/serial/meson_uart.c @@ -0,0 +1,634 @@ +/* + * Based on meson_uart.c, by AMLOGIC, INC. + * + * Copyright (C) 2014 Carlo Caione + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register offsets */ +#define AML_UART_WFIFO 0x00 +#define AML_UART_RFIFO 0x04 +#define AML_UART_CONTROL 0x08 +#define AML_UART_STATUS 0x0c +#define AML_UART_MISC 0x10 +#define AML_UART_REG5 0x14 + +/* AML_UART_CONTROL bits */ +#define AML_UART_TX_EN BIT(12) +#define AML_UART_RX_EN BIT(13) +#define AML_UART_TX_RST BIT(22) +#define AML_UART_RX_RST BIT(23) +#define AML_UART_CLR_ERR BIT(24) +#define AML_UART_RX_INT_EN BIT(27) +#define AML_UART_TX_INT_EN BIT(28) +#define AML_UART_DATA_LEN_MASK (0x03 << 20) +#define AML_UART_DATA_LEN_8BIT (0x00 << 20) +#define AML_UART_DATA_LEN_7BIT (0x01 << 20) +#define AML_UART_DATA_LEN_6BIT (0x02 << 20) +#define AML_UART_DATA_LEN_5BIT (0x03 << 20) + +/* AML_UART_STATUS bits */ +#define AML_UART_PARITY_ERR BIT(16) +#define AML_UART_FRAME_ERR BIT(17) +#define AML_UART_TX_FIFO_WERR BIT(18) +#define AML_UART_RX_EMPTY BIT(20) +#define AML_UART_TX_FULL BIT(21) +#define AML_UART_TX_EMPTY BIT(22) +#define AML_UART_ERR (AML_UART_PARITY_ERR | \ + AML_UART_FRAME_ERR | \ + AML_UART_TX_FIFO_WERR) + +/* AML_UART_CONTROL bits */ +#define AML_UART_TWO_WIRE_EN BIT(15) +#define AML_UART_PARITY_TYPE BIT(18) +#define AML_UART_PARITY_EN BIT(19) +#define AML_UART_CLEAR_ERR BIT(24) +#define AML_UART_STOP_BIN_LEN_MASK (0x03 << 16) +#define AML_UART_STOP_BIN_1SB (0x00 << 16) +#define AML_UART_STOP_BIN_2SB (0x01 << 16) + +/* AML_UART_MISC bits */ +#define AML_UART_XMIT_IRQ(c) (((c) & 0xff) << 8) +#define AML_UART_RECV_IRQ(c) ((c) & 0xff) + +/* AML_UART_REG5 bits */ +#define AML_UART_BAUD_MASK 0x7fffff +#define AML_UART_BAUD_USE BIT(23) + +#define AML_UART_PORT_NUM 6 +#define AML_UART_DEV_NAME "ttyAML" + + +static struct uart_driver meson_uart_driver; + +static struct uart_port *meson_ports[AML_UART_PORT_NUM]; + +static void meson_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static unsigned int meson_uart_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS; +} + +static unsigned int meson_uart_tx_empty(struct uart_port *port) +{ + u32 val; + + val = readl(port->membase + AML_UART_STATUS); + return (val & AML_UART_TX_EMPTY) ? TIOCSER_TEMT : 0; +} + +static void meson_uart_stop_tx(struct uart_port *port) +{ + u32 val; + + val = readl(port->membase + AML_UART_CONTROL); + val &= ~AML_UART_TX_EN; + writel(val, port->membase + AML_UART_CONTROL); +} + +static void meson_uart_stop_rx(struct uart_port *port) +{ + u32 val; + + val = readl(port->membase + AML_UART_CONTROL); + val &= ~AML_UART_RX_EN; + writel(val, port->membase + AML_UART_CONTROL); +} + +static void meson_uart_shutdown(struct uart_port *port) +{ + unsigned long flags; + u32 val; + + free_irq(port->irq, port); + + spin_lock_irqsave(&port->lock, flags); + + val = readl(port->membase + AML_UART_CONTROL); + val &= ~(AML_UART_RX_EN | AML_UART_TX_EN); + val &= ~(AML_UART_RX_INT_EN | AML_UART_TX_INT_EN); + writel(val, port->membase + AML_UART_CONTROL); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void meson_uart_start_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + unsigned int ch; + + if (uart_tx_stopped(port)) { + meson_uart_stop_tx(port); + return; + } + + while (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_FULL)) { + if (port->x_char) { + writel(port->x_char, port->membase + AML_UART_WFIFO); + port->icount.tx++; + port->x_char = 0; + continue; + } + + if (uart_circ_empty(xmit)) + break; + + ch = xmit->buf[xmit->tail]; + writel(ch, port->membase + AML_UART_WFIFO); + xmit->tail = (xmit->tail+1) & (SERIAL_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static void meson_receive_chars(struct uart_port *port) +{ + struct tty_port *tport = &port->state->port; + char flag; + u32 status, ch, mode; + + do { + flag = TTY_NORMAL; + port->icount.rx++; + status = readl(port->membase + AML_UART_STATUS); + + if (status & AML_UART_ERR) { + if (status & AML_UART_TX_FIFO_WERR) + port->icount.overrun++; + else if (status & AML_UART_FRAME_ERR) + port->icount.frame++; + else if (status & AML_UART_PARITY_ERR) + port->icount.frame++; + + mode = readl(port->membase + AML_UART_CONTROL); + mode |= AML_UART_CLEAR_ERR; + writel(mode, port->membase + AML_UART_CONTROL); + + /* It doesn't clear to 0 automatically */ + mode &= ~AML_UART_CLEAR_ERR; + writel(mode, port->membase + AML_UART_CONTROL); + + status &= port->read_status_mask; + if (status & AML_UART_FRAME_ERR) + flag = TTY_FRAME; + else if (status & AML_UART_PARITY_ERR) + flag = TTY_PARITY; + } + + ch = readl(port->membase + AML_UART_RFIFO); + ch &= 0xff; + + if ((status & port->ignore_status_mask) == 0) + tty_insert_flip_char(tport, ch, flag); + + if (status & AML_UART_TX_FIFO_WERR) + tty_insert_flip_char(tport, 0, TTY_OVERRUN); + + } while (!(readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY)); + + spin_unlock(&port->lock); + tty_flip_buffer_push(tport); + spin_lock(&port->lock); +} + +static irqreturn_t meson_uart_interrupt(int irq, void *dev_id) +{ + struct uart_port *port = (struct uart_port *)dev_id; + + spin_lock(&port->lock); + + if (!(readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY)) + meson_receive_chars(port); + + if (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_FULL)) + meson_uart_start_tx(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static const char *meson_uart_type(struct uart_port *port) +{ + return (port->type == PORT_MESON) ? "meson_uart" : NULL; +} + +static int meson_uart_startup(struct uart_port *port) +{ + u32 val; + int ret = 0; + + val = readl(port->membase + AML_UART_CONTROL); + val |= (AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR); + writel(val, port->membase + AML_UART_CONTROL); + + val &= ~(AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR); + writel(val, port->membase + AML_UART_CONTROL); + + val |= (AML_UART_RX_EN | AML_UART_TX_EN); + writel(val, port->membase + AML_UART_CONTROL); + + val |= (AML_UART_RX_INT_EN | AML_UART_TX_INT_EN); + writel(val, port->membase + AML_UART_CONTROL); + + val = (AML_UART_RECV_IRQ(1) | AML_UART_XMIT_IRQ(port->fifosize / 2)); + writel(val, port->membase + AML_UART_MISC); + + ret = request_irq(port->irq, meson_uart_interrupt, 0, + meson_uart_type(port), port); + + return ret; +} + +static void meson_uart_change_speed(struct uart_port *port, unsigned long baud) +{ + u32 val; + + while (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_EMPTY)) + cpu_relax(); + + val = readl(port->membase + AML_UART_REG5); + val &= ~AML_UART_BAUD_MASK; + val = ((port->uartclk * 10 / (baud * 4) + 5) / 10) - 1; + val |= AML_UART_BAUD_USE; + writel(val, port->membase + AML_UART_REG5); +} + +static void meson_uart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned int cflags, iflags, baud; + unsigned long flags; + u32 val; + + spin_lock_irqsave(&port->lock, flags); + + cflags = termios->c_cflag; + iflags = termios->c_iflag; + + val = readl(port->membase + AML_UART_CONTROL); + + val &= ~AML_UART_DATA_LEN_MASK; + switch (cflags & CSIZE) { + case CS8: + val |= AML_UART_DATA_LEN_8BIT; + break; + case CS7: + val |= AML_UART_DATA_LEN_7BIT; + break; + case CS6: + val |= AML_UART_DATA_LEN_6BIT; + break; + case CS5: + val |= AML_UART_DATA_LEN_5BIT; + break; + } + + if (cflags & PARENB) + val |= AML_UART_PARITY_EN; + else + val &= ~AML_UART_PARITY_EN; + + if (cflags & PARODD) + val |= AML_UART_PARITY_TYPE; + else + val &= ~AML_UART_PARITY_TYPE; + + val &= ~AML_UART_STOP_BIN_LEN_MASK; + if (cflags & CSTOPB) + val |= AML_UART_STOP_BIN_2SB; + else + val &= ~AML_UART_STOP_BIN_1SB; + + if (cflags & CRTSCTS) + val &= ~AML_UART_TWO_WIRE_EN; + else + val |= AML_UART_TWO_WIRE_EN; + + writel(val, port->membase + AML_UART_CONTROL); + + baud = uart_get_baud_rate(port, termios, old, 9600, 115200); + meson_uart_change_speed(port, baud); + + port->read_status_mask = AML_UART_TX_FIFO_WERR; + if (iflags & INPCK) + port->read_status_mask |= AML_UART_PARITY_ERR | + AML_UART_FRAME_ERR; + + port->ignore_status_mask = 0; + if (iflags & IGNPAR) + port->ignore_status_mask |= AML_UART_PARITY_ERR | + AML_UART_FRAME_ERR; + + uart_update_timeout(port, termios->c_cflag, baud); + spin_unlock_irqrestore(&port->lock, flags); +} + +static int meson_uart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + int ret = 0; + + if (port->type != PORT_MESON) + ret = -EINVAL; + if (port->irq != ser->irq) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static void meson_uart_release_port(struct uart_port *port) +{ + if (port->flags & UPF_IOREMAP) { + iounmap(port->membase); + port->membase = NULL; + } +} + +static int meson_uart_request_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *res; + int size; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "cannot obtain I/O memory region"); + return -ENODEV; + } + size = resource_size(res); + + if (!devm_request_mem_region(port->dev, port->mapbase, size, + dev_name(port->dev))) { + dev_err(port->dev, "Memory region busy\n"); + return -EBUSY; + } + + if (port->flags & UPF_IOREMAP) { + port->membase = devm_ioremap_nocache(port->dev, + port->mapbase, + size); + if (port->membase == NULL) + return -ENOMEM; + } + + return 0; +} + +static void meson_uart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_MESON; + meson_uart_request_port(port); + } +} + +static struct uart_ops meson_uart_ops = { + .set_mctrl = meson_uart_set_mctrl, + .get_mctrl = meson_uart_get_mctrl, + .tx_empty = meson_uart_tx_empty, + .start_tx = meson_uart_start_tx, + .stop_tx = meson_uart_stop_tx, + .stop_rx = meson_uart_stop_rx, + .startup = meson_uart_startup, + .shutdown = meson_uart_shutdown, + .set_termios = meson_uart_set_termios, + .type = meson_uart_type, + .config_port = meson_uart_config_port, + .request_port = meson_uart_request_port, + .release_port = meson_uart_release_port, + .verify_port = meson_uart_verify_port, +}; + +#ifdef CONFIG_SERIAL_MESON_CONSOLE + +static void meson_console_putchar(struct uart_port *port, int ch) +{ + if (!port->membase) + return; + + while (readl(port->membase + AML_UART_STATUS) & AML_UART_TX_FULL) + cpu_relax(); + writel(ch, port->membase + AML_UART_WFIFO); +} + +static void meson_serial_console_write(struct console *co, const char *s, + u_int count) +{ + struct uart_port *port; + unsigned long flags; + int locked; + + port = meson_ports[co->index]; + if (!port) + return; + + local_irq_save(flags); + if (port->sysrq) { + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&port->lock); + } else { + spin_lock(&port->lock); + locked = 1; + } + + uart_console_write(port, s, count, meson_console_putchar); + + if (locked) + spin_unlock(&port->lock); + local_irq_restore(flags); +} + +static int meson_serial_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= AML_UART_PORT_NUM) + return -EINVAL; + + port = meson_ports[co->index]; + if (!port || !port->membase) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console meson_serial_console = { + .name = AML_UART_DEV_NAME, + .write = meson_serial_console_write, + .device = uart_console_device, + .setup = meson_serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &meson_uart_driver, +}; + +static int __init meson_serial_console_init(void) +{ + register_console(&meson_serial_console); + return 0; +} +console_initcall(meson_serial_console_init); + +#define MESON_SERIAL_CONSOLE (&meson_serial_console) +#else +#define MESON_SERIAL_CONSOLE NULL +#endif + +static struct uart_driver meson_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "meson_uart", + .dev_name = AML_UART_DEV_NAME, + .nr = AML_UART_PORT_NUM, + .cons = MESON_SERIAL_CONSOLE, +}; + +static int meson_uart_probe(struct platform_device *pdev) +{ + struct resource *res_mem, *res_irq; + struct uart_port *port; + struct clk *clk; + int ret = 0; + + if (pdev->dev.of_node) + pdev->id = of_alias_get_id(pdev->dev.of_node, "serial"); + + if (pdev->id < 0 || pdev->id >= AML_UART_PORT_NUM) + return -EINVAL; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) + return -ENODEV; + + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res_irq) + return -ENODEV; + + if (meson_ports[pdev->id]) { + dev_err(&pdev->dev, "port %d already allocated\n", pdev->id); + return -EBUSY; + } + + port = devm_kzalloc(&pdev->dev, sizeof(struct uart_port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + port->uartclk = clk_get_rate(clk); + port->iotype = UPIO_MEM; + port->mapbase = res_mem->start; + port->irq = res_irq->start; + port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_LOW_LATENCY; + port->dev = &pdev->dev; + port->line = pdev->id; + port->type = PORT_MESON; + port->x_char = 0; + port->ops = &meson_uart_ops; + port->fifosize = 64; + + meson_ports[pdev->id] = port; + platform_set_drvdata(pdev, port); + + ret = uart_add_one_port(&meson_uart_driver, port); + if (ret) + meson_ports[pdev->id] = NULL; + + return ret; +} + +static int meson_uart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + + port = platform_get_drvdata(pdev); + uart_remove_one_port(&meson_uart_driver, port); + meson_ports[pdev->id] = NULL; + + return 0; +} + + +static const struct of_device_id meson_uart_dt_match[] = { + { .compatible = "amlogic,meson-uart" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, meson_uart_dt_match); + +static struct platform_driver meson_uart_platform_driver = { + .probe = meson_uart_probe, + .remove = meson_uart_remove, + .driver = { + .owner = THIS_MODULE, + .name = "meson_uart", + .of_match_table = meson_uart_dt_match, + }, +}; + +static int __init meson_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&meson_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&meson_uart_platform_driver); + if (ret) + uart_unregister_driver(&meson_uart_driver); + + return ret; +} + +static void __exit meson_uart_exit(void) +{ + platform_driver_unregister(&meson_uart_platform_driver); + uart_unregister_driver(&meson_uart_driver); +} + +module_init(meson_uart_init); +module_exit(meson_uart_exit); + +MODULE_AUTHOR("Carlo Caione "); +MODULE_DESCRIPTION("Amlogic Meson serial port driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 5820269..16ad852 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -244,4 +244,7 @@ /* SC16IS74xx */ #define PORT_SC16IS7XX 108 +/* MESON */ +#define PORT_MESON 109 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- cgit v0.10.2 From 920314dfd05f40cbe8ad677ba733ff1f8814d93a Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Wed, 20 Aug 2014 12:55:45 -0400 Subject: serial: Fix build failure caused by missing header file Fix build failure caused by missing header file: drivers/tty/serial/nwpserial.c: In function 'wait_for_bits': drivers/tty/serial/nwpserial.c:53:3: error: implicit declaration of function 'udelay' [-Werror=implicit-function-declaration] Signed-off-by: Pranith Kumar Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/nwpserial.c b/drivers/tty/serial/nwpserial.c index c06366b..5da7622 100644 --- a/drivers/tty/serial/nwpserial.c +++ b/drivers/tty/serial/nwpserial.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include -- cgit v0.10.2 From 6004bb11cf0d35574fdedb236d98672ccf86d9a9 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 23 Aug 2014 20:33:24 +0200 Subject: serial: vr41xx_siu: delete double assignment Delete successive assignments to the same location. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression i; @@ *i = ...; i = ...; // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c index db0c8a4..d7f9d62 100644 --- a/drivers/tty/serial/vr41xx_siu.c +++ b/drivers/tty/serial/vr41xx_siu.c @@ -847,7 +847,6 @@ void __init vr41xx_siu_early_setup(struct uart_port *port) siu_uart_ports[port->line].type = port->type; siu_uart_ports[port->line].uartclk = SIU_BAUD_BASE * 16; siu_uart_ports[port->line].mapbase = port->mapbase; - siu_uart_ports[port->line].mapbase = port->mapbase; siu_uart_ports[port->line].ops = &siu_uart_ops; } -- cgit v0.10.2 From 95562e7fdfe3fae2a10bb7ab16fc1260fe8dd810 Mon Sep 17 00:00:00 2001 From: Naveen Krishna Chatradhi Date: Tue, 2 Sep 2014 21:05:42 +0530 Subject: tty/serial: samsung: enable usage for 64-bit Exynos platforms Allow Samsung serial driver to be usable on Exynos 64-bit SoC based platforms. Signed-off-by: Pankaj Dubey Signed-off-by: Naveen Krishna Chatradhi Cc: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 636949d..8079f52 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -239,7 +239,7 @@ config SERIAL_CLPS711X_CONSOLE config SERIAL_SAMSUNG tristate "Samsung SoC serial support" - depends on PLAT_SAMSUNG + depends on PLAT_SAMSUNG || ARCH_EXYNOS select SERIAL_CORE help Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, -- cgit v0.10.2 From a6eec92ec0815ce8ce57e4746e9f717d4146d869 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 2 Sep 2014 17:39:10 -0400 Subject: Revert "serial: uart: add hw flow control support configuration" This reverts commit 06aa82e498c144c7784a6f3d3b55458b272d6146. This commit purports to enable auto CTS flow control for the 8250 UART driver. However, the 8250 UART driver already supports auto CTS flow control via UART_CAP_AFE and UART_CAP_EFR. Indeed, this patch introduces another DT attribute for which an existing firmware flag already exists ("auto-flow-control"). Furthermore, the use of UPF_HARD_FLOW requires the UART driver to define .throttle and .unthrottle methods, neither of which are defined for the 8250 UART driver (which will result in a NULL ptr dereference). Finally, this patch supposes to fix existing bugs in the serial core for auto CTS-enabled hardware, but does not include the class of hardware for which these bugs exist. CC: Murali Karicheri CC: Rob Herring Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/of-serial.txt b/Documentation/devicetree/bindings/serial/of-serial.txt index 7705477..1928a3e 100644 --- a/Documentation/devicetree/bindings/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/serial/of-serial.txt @@ -37,7 +37,6 @@ Optional properties: - auto-flow-control: one way to enable automatic flow control support. The driver is allowed to detect support for the capability even without this property. -- has-hw-flow-control: the hardware has flow control capability. Example: diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index b28ed1b..46c1f5f 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2319,11 +2319,9 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, * the trigger, or the MCR RTS bit is cleared. In the case where * the remote UART is not using CTS auto flow control, we must * have sufficient FIFO entries for the latency of the remote - * UART to respond. IOW, at least 32 bytes of FIFO. Also enable - * AFE if hw flow control is supported + * UART to respond. IOW, at least 32 bytes of FIFO. */ - if ((up->capabilities & UART_CAP_AFE && (port->fifosize >= 32)) || - (port->flags & UPF_HARD_FLOW)) { + if (up->capabilities & UART_CAP_AFE && port->fifosize >= 32) { up->mcr &= ~UART_MCR_AFE; if (termios->c_cflag & CRTSCTS) up->mcr |= UART_MCR_AFE; diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 68d4455..27981e2 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -183,10 +183,6 @@ static int of_platform_serial_probe(struct platform_device *ofdev) "auto-flow-control")) port8250.capabilities |= UART_CAP_AFE; - if (of_property_read_bool(ofdev->dev.of_node, - "has-hw-flow-control")) - port8250.port.flags |= UPF_HARD_FLOW; - ret = serial8250_register_8250_port(&port8250); break; } diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 079f18d..2e57d5b 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -175,12 +175,8 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, if (tty->termios.c_cflag & CBAUD) uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); } - /* - * if hw support flow control without software intervention, - * then skip the below check - */ - if (tty_port_cts_enabled(port) && - !(uport->flags & UPF_HARD_FLOW)) { + + if (tty_port_cts_enabled(port)) { spin_lock_irq(&uport->lock); if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) tty->hw_stopped = 1; @@ -2789,9 +2785,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) uport->icount.cts++; - /* skip below code if the hw flow control is supported */ - if (tty_port_cts_enabled(port) && - !(uport->flags & UPF_HARD_FLOW)) { + if (tty_port_cts_enabled(port)) { if (tty->hw_stopped) { if (status) { tty->hw_stopped = 0; -- cgit v0.10.2 From 99abf3b9242fbc5fec586223a5068544e0e9d90f Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 2 Sep 2014 17:39:11 -0400 Subject: serial: Style fix Unwrap if() conditional; no functional change. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 2e57d5b..0742f77 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1288,8 +1288,7 @@ static void uart_set_termios(struct tty_struct *tty, /* Handle transition away from B0 status */ else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { unsigned int mask = TIOCM_DTR; - if (!(cflag & CRTSCTS) || - !test_bit(TTY_THROTTLED, &tty->flags)) + if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) mask |= TIOCM_RTS; uart_set_mctrl(uport, mask); } -- cgit v0.10.2 From 5e42e9a30cdaae51411a9fd4d7de1dc6a7507038 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 2 Sep 2014 17:39:12 -0400 Subject: serial: imx: Fix x_char handling and tx flow control The serial core expects the UART driver to transmit x_char (START/STOP chars) even if tx is stopped and before data already in the tx ring buffer if possible. Also, sending x_char must not cause additional data in the tx ring buffer to transmit if tx is stopped. Cause x_char to be transmitted before any other data is sent. Auto-stop tx if the tx ring buffer is empty or tx should be stopped. Only perform one write wakeup if tx ring buffer space is below threshold. x_char handling in DMA mode is still broken; add FIXME. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 044e86d..c86f153 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -464,9 +464,19 @@ static inline void imx_transmit_buffer(struct imx_port *sport) { struct circ_buf *xmit = &sport->port.state->xmit; + if (sport->port.x_char) { + /* Send next char */ + writel(sport->port.x_char, sport->port.membase + URTX0); + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + imx_stop_tx(&sport->port); + return; + } + while (!uart_circ_empty(xmit) && - !(readl(sport->port.membase + uts_reg(sport)) - & UTS_TXFULL)) { + !(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) { /* send xmit->buf[xmit->tail] * out the port here */ writel(xmit->buf[xmit->tail], sport->port.membase + URTX0); @@ -567,9 +577,6 @@ static void imx_start_tx(struct uart_port *port) struct imx_port *sport = (struct imx_port *)port; unsigned long temp; - if (uart_circ_empty(&port->state->xmit)) - return; - if (USE_IRDA(sport)) { /* half duplex in IrDA mode; have to disable receive mode */ temp = readl(sport->port.membase + UCR4); @@ -604,7 +611,10 @@ static void imx_start_tx(struct uart_port *port) } if (sport->dma_is_enabled) { - imx_dma_tx(sport); + /* FIXME: port->x_char must be transmitted if != 0 */ + if (!uart_circ_empty(&port->state->xmit) && + !uart_tx_stopped(port)) + imx_dma_tx(sport); return; } @@ -632,27 +642,10 @@ static irqreturn_t imx_rtsint(int irq, void *dev_id) static irqreturn_t imx_txint(int irq, void *dev_id) { struct imx_port *sport = dev_id; - struct circ_buf *xmit = &sport->port.state->xmit; unsigned long flags; spin_lock_irqsave(&sport->port.lock, flags); - if (sport->port.x_char) { - /* Send next char */ - writel(sport->port.x_char, sport->port.membase + URTX0); - goto out; - } - - if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { - imx_stop_tx(&sport->port); - goto out; - } - imx_transmit_buffer(sport); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&sport->port); - -out: spin_unlock_irqrestore(&sport->port.lock, flags); return IRQ_HANDLED; } -- cgit v0.10.2 From c235ccc1c4d6fd8b7d48b976b87416230ffd5149 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 2 Sep 2014 17:39:13 -0400 Subject: serial: core: Fix x_char race The UART driver is expected to clear port->x_char after transmission while holding the port->lock. However, the serial core fails to take the port->lock before assigning port->xchar. This allows for the following race CPU 0 | CPU 1 | | serial8250_handle_irq | ... | serial8250_tx_chars | if (port->x_char) | serial_out(up, UART_TX, port->x_char) uart_send_xchar | port->x_char = ch | | port->x_char = 0 port->ops->start_tx() | | The x_char on CPU 0 will never be sent. Take the port->lock in uart_send_xchar() before assigning port->x_char. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0742f77..bd20cf5 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -600,12 +600,11 @@ static void uart_send_xchar(struct tty_struct *tty, char ch) if (port->ops->send_xchar) port->ops->send_xchar(port, ch); else { + spin_lock_irqsave(&port->lock, flags); port->x_char = ch; - if (ch) { - spin_lock_irqsave(&port->lock, flags); + if (ch) port->ops->start_tx(port); - spin_unlock_irqrestore(&port->lock, flags); - } + spin_unlock_irqrestore(&port->lock, flags); } } -- cgit v0.10.2 From fba594a848f6f35417a358565086d22c533c19c8 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 2 Sep 2014 17:39:14 -0400 Subject: serial: core: Remove unsafe x_char optimization uart_unthrottle() attempts to avoid sending START and the previous x_char if the previous x_char has not yet been sent. However, this optimization could leave the sender in a throttled state; for example, if the sender is throttled and this unthrottle coincides with a manual tcflow(TCION) from user-space, then neither START would be sent. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index bd20cf5..bdc543c 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -647,12 +647,8 @@ static void uart_unthrottle(struct tty_struct *tty) mask &= ~port->flags; } - if (mask & UPF_SOFT_FLOW) { - if (port->x_char) - port->x_char = 0; - else - uart_send_xchar(tty, START_CHAR(tty)); - } + if (mask & UPF_SOFT_FLOW) + uart_send_xchar(tty, START_CHAR(tty)); if (mask & UPF_HARD_FLOW) uart_set_mctrl(port, TIOCM_RTS); -- cgit v0.10.2 From db106df32d679db151b48a09543b03ee41cb2580 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 2 Sep 2014 17:39:15 -0400 Subject: serial: Fix send_xchar() handlers START_CHAR() & STOP_CHAR() can be disabled if set to '\0' (__DISABLED_CHAR). UART drivers which define a send_xchar() handler must not transmit __DISABLED_CHAR. Document requirement. Affected drivers: sunsab sunhv cc: David S. Miller cc: Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/serial/driver b/Documentation/serial/driver index 3bba1ae..ba64e4b 100644 --- a/Documentation/serial/driver +++ b/Documentation/serial/driver @@ -140,6 +140,8 @@ hardware. will append the character to the circular buffer and then call start_tx() / stop_tx() to flush the data out. + Do not transmit if ch == '\0' (__DISABLED_CHAR). + Locking: none. Interrupts: caller dependent. diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 20521db..25d43ce 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -268,6 +268,9 @@ static void sunhv_send_xchar(struct uart_port *port, char ch) unsigned long flags; int limit = 10000; + if (ch == __DISABLED_CHAR) + return; + spin_lock_irqsave(&port->lock, flags); while (limit-- > 0) { diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index b9598b2..0af75f8 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -468,6 +468,9 @@ static void sunsab_send_xchar(struct uart_port *port, char ch) struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; unsigned long flags; + if (ch == __DISABLED_CHAR) + return; + spin_lock_irqsave(&up->port.lock, flags); sunsab_tec_wait(up); -- cgit v0.10.2 From 16f404e3666718917b2541503240cf8430aa49ff Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 2 Sep 2014 17:39:16 -0400 Subject: serial: mpc52xx: Use default serial core x_char handler mpc52xx_uart_send_xchar() is _identical_ to the default serial core x_char handling behavior in uart_send_xchar(). Remove mpc52xx_uart_send_xchar(). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 97888f4..a5f4e36 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -1087,22 +1087,6 @@ mpc52xx_uart_start_tx(struct uart_port *port) } static void -mpc52xx_uart_send_xchar(struct uart_port *port, char ch) -{ - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); - - port->x_char = ch; - if (ch) { - /* Make sure tx interrupts are on */ - /* Truly necessary ??? They should be anyway */ - psc_ops->start_tx(port); - } - - spin_unlock_irqrestore(&port->lock, flags); -} - -static void mpc52xx_uart_stop_rx(struct uart_port *port) { /* port->lock taken by caller */ @@ -1361,7 +1345,6 @@ static struct uart_ops mpc52xx_uart_ops = { .get_mctrl = mpc52xx_uart_get_mctrl, .stop_tx = mpc52xx_uart_stop_tx, .start_tx = mpc52xx_uart_start_tx, - .send_xchar = mpc52xx_uart_send_xchar, .stop_rx = mpc52xx_uart_stop_rx, .enable_ms = mpc52xx_uart_enable_ms, .break_ctl = mpc52xx_uart_break_ctl, -- cgit v0.10.2 From 98f8b83d6a504615f52a095acf92a2bdad985ae0 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 2 Sep 2014 17:39:17 -0400 Subject: serial: sunsab: Don't enable tx if tx stopped The serial core may call the UART driver's start_tx() even if tx is stopped; the UART driver must verify tx should be enabled before transmitting. Reported-by: Sam Ravnborg cc: David S. Miller cc: Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index 0af75f8..b339fe4 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -436,7 +436,7 @@ static void sunsab_start_tx(struct uart_port *port) struct circ_buf *xmit = &up->port.state->xmit; int i; - if (uart_circ_empty(xmit)) + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return; up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR); -- cgit v0.10.2 From 16020b989ac72b0b02654612a7b70398a7faac21 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 2 Sep 2014 17:39:18 -0400 Subject: serial: blackfin: Fix missing gpio.h If CONFIG_SERIAL_BFIN_CTSRTS is set, compile fails because of missing declarations for the gpio_* api. Include necessary header. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c index 7810aa2..d62d8da 100644 --- a/drivers/tty/serial/bfin_sport_uart.c +++ b/drivers/tty/serial/bfin_sport_uart.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include -- cgit v0.10.2 From 8a949b07e4062cbd07e04e6a47249e69ca65b944 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 2 Sep 2014 17:39:19 -0400 Subject: serial: core: Document lock requirement for UPF_* flags updates The flags field of struct uart_port can only be safely modified if the port mutex is held; no other lock prevents concurrent changes from corrupting the field. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index cf3a1e7..8cb267b 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -152,6 +152,7 @@ struct uart_port { unsigned long sysrq; /* sysrq timeout */ #endif + /* flags must be updated while holding port mutex */ upf_t flags; #define UPF_FOURPORT ((__force upf_t) (1 << 1)) -- cgit v0.10.2 From 5435d20f506f16352a4e1637b8e8fb7c0d422bb9 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 2 Sep 2014 17:39:20 -0400 Subject: serial: 8250: Document serial8250_modem_status() locking Existing callers of serial8250_modem_status() [1] hold the uart port lock; document. [1] In-tree callers of serial8250_modem_status() drivers/tty/serial/8250/8250_fsl.c fsl8250_handle_irq() drivers/tty/serial/8250/8250_core.c serial8250_handle_irq() serial8250_console_write() serial8250_get_mctrl() * * Call graphs for callers of serial8250_get_mctrl() from the function which acquires the uart port lock drivers/tty/serial/serial_core.c uart_port_startup() uart_tiocmget() uart_set_termios() uart_carrier_raised() ops->get_mctrl() ---> serial8250_get_mctrl() Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 46c1f5f..b597e73 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1463,6 +1463,7 @@ void serial8250_tx_chars(struct uart_8250_port *up) } EXPORT_SYMBOL_GPL(serial8250_tx_chars); +/* Caller holds uart port lock */ unsigned int serial8250_modem_status(struct uart_8250_port *up) { struct uart_port *port = &up->port; -- cgit v0.10.2 From c993257bf7571945201205491de86268437ea174 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 2 Sep 2014 17:39:21 -0400 Subject: serial: core: Unwrap tertiary assignment in uart_handle_dcd_change() Prepare for spin lock assertion; move non-trivial assignment into function body. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index bdc543c..5a78f69 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2748,12 +2748,15 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) { struct tty_port *port = &uport->state->port; struct tty_struct *tty = port->tty; - struct tty_ldisc *ld = tty ? tty_ldisc_ref(tty) : NULL; + struct tty_ldisc *ld; - if (ld) { - if (ld->ops->dcd_change) - ld->ops->dcd_change(tty, status); - tty_ldisc_deref(ld); + if (tty) { + ld = tty_ldisc_ref(tty); + if (ld) { + if (ld->ops->dcd_change) + ld->ops->dcd_change(tty, status); + tty_ldisc_deref(ld); + } } uport->icount.dcd++; -- cgit v0.10.2 From 26c474128b96d277234f35a7fd870ed12bef96ca Mon Sep 17 00:00:00 2001 From: Dirk Behme Date: Wed, 3 Sep 2014 12:33:53 +0100 Subject: serial: imx: clean up imx_poll_get_char() Looking at the get_poll_char() function of the 8250.c serial driver, we learn: * poll_get_char() doesn't have to save/disable/restore the interrupt registers. No interrupt handling is needed in this function at all. Remove it. * Don't block in case there is no data available. So instead blocking in the do {} while loop, just return with NO_POLL_CHAR, immediately . Additionally, while the i.MX6 register URXD[7-0] contain the RX_DATA, the upper bits of this register (URXD[15-10]) might contain some control flags. To ensure that these are not returned with the data read, just mask out URXD[7-0]. These changes fix the 'hang' working with kdb: $ echo ttymxc3 > /sys/module/kgdboc/parameters/kgdboc $ echo g >/proc/sysrq-trigger [0]kdb> help ... Signed-off-by: Dirk Behme Signed-off-by: Daniel Thompson Cc: Jiri Slaby Cc: linux-serial@vger.kernel.org Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index c86f153..be13eb3 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -80,6 +80,7 @@ #define URXD_FRMERR (1<<12) #define URXD_BRK (1<<11) #define URXD_PRERR (1<<10) +#define URXD_RX_DATA (0xFF<<0) #define UCR1_ADEN (1<<15) /* Auto detect interrupt */ #define UCR1_ADBR (1<<14) /* Auto detect baud rate */ #define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */ @@ -1499,32 +1500,10 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser) #if defined(CONFIG_CONSOLE_POLL) static int imx_poll_get_char(struct uart_port *port) { - struct imx_port_ucrs old_ucr; - unsigned int status; - unsigned char c; - - /* save control registers */ - imx_port_ucrs_save(port, &old_ucr); - - /* disable interrupts */ - writel(UCR1_UARTEN, port->membase + UCR1); - writel(old_ucr.ucr2 & ~(UCR2_ATEN | UCR2_RTSEN | UCR2_ESCI), - port->membase + UCR2); - writel(old_ucr.ucr3 & ~(UCR3_DCD | UCR3_RI | UCR3_DTREN), - port->membase + UCR3); - - /* poll */ - do { - status = readl(port->membase + USR2); - } while (~status & USR2_RDR); - - /* read */ - c = readl(port->membase + URXD0); - - /* restore control registers */ - imx_port_ucrs_restore(port, &old_ucr); + if (!(readl(port->membase + USR2) & USR2_RDR)) + return NO_POLL_CHAR; - return c; + return readl(port->membase + URXD0) & URXD_RX_DATA; } static void imx_poll_put_char(struct uart_port *port, unsigned char c) -- cgit v0.10.2 From a5820c24a817e7bbebf5c4366989d5f243b809ab Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Wed, 3 Sep 2014 12:51:55 +0100 Subject: serial: amba-pl011: Use container_of() to get uart_amba_port Universally adopt container_of() for all pointer conversion from uart_port to uart_amba_port. Signed-off-by: Daniel Thompson Cc: Peter Hurley Cc: Russell King Cc: Jiri Slaby Cc: linux-serial@vger.kernel.org Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 8572f2a..02016fc 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -678,7 +678,8 @@ static void pl011_dma_flush_buffer(struct uart_port *port) __releases(&uap->port.lock) __acquires(&uap->port.lock) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); if (!uap->using_tx_dma) return; @@ -1163,7 +1164,8 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) static void pl011_stop_tx(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); uap->im &= ~UART011_TXIM; writew(uap->im, uap->port.membase + UART011_IMSC); @@ -1172,7 +1174,8 @@ static void pl011_stop_tx(struct uart_port *port) static void pl011_start_tx(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); if (!pl011_dma_tx_start(uap)) { uap->im |= UART011_TXIM; @@ -1182,7 +1185,8 @@ static void pl011_start_tx(struct uart_port *port) static void pl011_stop_rx(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); uap->im &= ~(UART011_RXIM|UART011_RTIM|UART011_FEIM| UART011_PEIM|UART011_BEIM|UART011_OEIM); @@ -1193,7 +1197,8 @@ static void pl011_stop_rx(struct uart_port *port) static void pl011_enable_ms(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); uap->im |= UART011_RIMIM|UART011_CTSMIM|UART011_DCDMIM|UART011_DSRMIM; writew(uap->im, uap->port.membase + UART011_IMSC); @@ -1349,14 +1354,16 @@ static irqreturn_t pl011_int(int irq, void *dev_id) static unsigned int pl011_tx_empty(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int status = readw(uap->port.membase + UART01x_FR); return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT; } static unsigned int pl011_get_mctrl(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int result = 0; unsigned int status = readw(uap->port.membase + UART01x_FR); @@ -1374,7 +1381,8 @@ static unsigned int pl011_get_mctrl(struct uart_port *port) static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int cr; cr = readw(uap->port.membase + UART011_CR); @@ -1402,7 +1410,8 @@ static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl) static void pl011_break_ctl(struct uart_port *port, int break_state) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned long flags; unsigned int lcr_h; @@ -1420,7 +1429,8 @@ static void pl011_break_ctl(struct uart_port *port, int break_state) static void pl011_quiesce_irqs(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned char __iomem *regs = uap->port.membase; writew(readw(regs + UART011_MIS), regs + UART011_ICR); @@ -1442,7 +1452,8 @@ static void pl011_quiesce_irqs(struct uart_port *port) static int pl011_get_poll_char(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int status; /* @@ -1461,7 +1472,8 @@ static int pl011_get_poll_char(struct uart_port *port) static void pl011_put_poll_char(struct uart_port *port, unsigned char ch) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) barrier(); @@ -1473,7 +1485,8 @@ static void pl011_put_poll_char(struct uart_port *port, static int pl011_hwinit(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); int retval; /* Optionaly enable pins to be muxed in and configured */ @@ -1526,7 +1539,8 @@ static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h) static int pl011_startup(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int cr, lcr_h, fbrd, ibrd; int retval; @@ -1618,7 +1632,8 @@ static void pl011_shutdown_channel(struct uart_amba_port *uap, static void pl011_shutdown(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int cr; /* @@ -1680,7 +1695,8 @@ static void pl011_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int lcr_h, old_cr; unsigned long flags; unsigned int baud, quot, clkdiv; @@ -1822,7 +1838,8 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, static const char *pl011_type(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); return uap->port.type == PORT_AMBA ? uap->type : NULL; } @@ -1900,7 +1917,8 @@ static struct uart_amba_port *amba_ports[UART_NR]; static void pl011_console_putchar(struct uart_port *port, int ch) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) barrier(); -- cgit v0.10.2 From 287f03c0d4fbf0b57f88867a02632376b63ef0c8 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Wed, 3 Sep 2014 12:57:52 +0100 Subject: serial: kgdb_nmi: No CON_ENABLED by default At present this console is optionally registered by NULL checking arch_kgdb_ops.enable_nmi. In practice this requires the architecture dependant code to implement some kind of control (e.g. module arguments) to enable/disable this feature. The kernel already provides us the perfectly adequate console= argument to enable/disable consoles. Let's use that instead! Signed-off-by: Daniel Thompson Cc: Jiri Slaby Cc: linux-serial@vger.kernel.org Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c index 6ec7501..129dc5b 100644 --- a/drivers/tty/serial/kgdb_nmi.c +++ b/drivers/tty/serial/kgdb_nmi.c @@ -46,6 +46,8 @@ static atomic_t kgdb_nmi_num_readers = ATOMIC_INIT(0); static int kgdb_nmi_console_setup(struct console *co, char *options) { + arch_kgdb_ops.enable_nmi(1); + /* The NMI console uses the dbg_io_ops to issue console messages. To * avoid duplicate messages during kdb sessions we must inform kdb's * I/O utilities that messages sent to the console will automatically @@ -77,7 +79,7 @@ static struct console kgdb_nmi_console = { .setup = kgdb_nmi_console_setup, .write = kgdb_nmi_console_write, .device = kgdb_nmi_console_device, - .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED, + .flags = CON_PRINTBUFFER | CON_ANYTIME, .index = -1, }; @@ -354,7 +356,6 @@ int kgdb_register_nmi_console(void) } register_console(&kgdb_nmi_console); - arch_kgdb_ops.enable_nmi(1); return 0; err_drv_reg: -- cgit v0.10.2 From 7d480ef776b53c43ec70155bfefbddc71b4d0883 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Fri, 5 Sep 2014 10:35:14 +0800 Subject: doc: dt-binding: of-serial: add Freescale 64-byte FIFO mode uart binding This add the 64-byte FIFO mode device tree binding for Freescale DUART. Signed-off-by: Jingchang Lu Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/of-serial.txt b/Documentation/devicetree/bindings/serial/of-serial.txt index 1928a3e..8c4fd03 100644 --- a/Documentation/devicetree/bindings/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/serial/of-serial.txt @@ -14,6 +14,7 @@ Required properties: - "altr,16550-FIFO32" - "altr,16550-FIFO64" - "altr,16550-FIFO128" + - "fsl,16550-FIFO64" - "serial" if the port type is unknown. - reg : offset and length of the register set for the device. - interrupts : should contain uart interrupt. -- cgit v0.10.2 From ae382735247e2daebeed3bafd400ff71039d2241 Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Sat, 6 Sep 2014 21:21:12 +0400 Subject: tty: vt8500_serial: add support for UART in WM8880 chips Newer WonderMedia chips introduced another flag in the UART line control register, which controls whether RTS/CTS signalling should be handled in the driver or by the hardware itself. This patch ensures that the kernel can control RTS/CTS (including disabling it altogether) by forcing this flag to software mode on affected chips (only WM8880 so far). Also remove the redundant copy of the binding doc, while we are here. Signed-off-by: Alexey Charkov Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/via,vt8500-uart.txt b/Documentation/devicetree/bindings/serial/via,vt8500-uart.txt deleted file mode 100644 index 5feef1e..0000000 --- a/Documentation/devicetree/bindings/serial/via,vt8500-uart.txt +++ /dev/null @@ -1,17 +0,0 @@ -VIA/Wondermedia VT8500 UART Controller ------------------------------------------------------ - -Required properties: -- compatible : "via,vt8500-uart" -- reg : Should contain 1 register ranges(address and length) -- interrupts : UART interrupt -- clocks : phandle to the uart source clock (usually a 24Mhz fixed clock) - -Example: - - uart@d8210000 { - compatible = "via,vt8500-uart"; - reg = <0xd8210000 0x1040>; - interrupts = <47>; - clocks = <&ref24>; - }; diff --git a/Documentation/devicetree/bindings/serial/vt8500-uart.txt b/Documentation/devicetree/bindings/serial/vt8500-uart.txt index 795c393..2b64e61 100644 --- a/Documentation/devicetree/bindings/serial/vt8500-uart.txt +++ b/Documentation/devicetree/bindings/serial/vt8500-uart.txt @@ -1,7 +1,8 @@ * VIA VT8500 and WonderMedia WM8xxx UART Controller Required properties: -- compatible: should be "via,vt8500-uart" +- compatible: should be "via,vt8500-uart" (for VIA/WonderMedia chips up to and + including WM8850/WM8950), or "wm,wm8880-uart" (for WM8880 and later) - reg: base physical address of the controller and length of memory mapped region. diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 15ad6fc..f225719 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -33,8 +33,8 @@ #include #include #include -#include #include +#include #include /* @@ -78,6 +78,29 @@ #define RX_FIFO_INTS (RXFAF | RXFF | RXOVER | PER | FER | RXTOUT) #define TX_FIFO_INTS (TXFAE | TXFE | TXUDR) +/* + * Line control bits + */ + +#define VT8500_TXEN (1 << 0) /* Enable transmit logic */ +#define VT8500_RXEN (1 << 1) /* Enable receive logic */ +#define VT8500_CS8 (1 << 2) /* 8-bit data length (vs. 7-bit) */ +#define VT8500_CSTOPB (1 << 3) /* 2 stop bits (vs. 1) */ +#define VT8500_PARENB (1 << 4) /* Enable parity */ +#define VT8500_PARODD (1 << 5) /* Odd parity (vs. even) */ +#define VT8500_RTS (1 << 6) /* Ready to send */ +#define VT8500_LOOPBK (1 << 7) /* Enable internal loopback */ +#define VT8500_DMA (1 << 8) /* Enable DMA mode (needs FIFO) */ +#define VT8500_BREAK (1 << 9) /* Initiate break signal */ +#define VT8500_PSLVERR (1 << 10) /* APB error upon empty RX FIFO read */ +#define VT8500_SWRTSCTS (1 << 11) /* Software-controlled RTS/CTS */ + +/* + * Capability flags (driver-internal) + */ + +#define VT8500_HAS_SWRTSCTS_SWITCH (1 << 1) + #define VT8500_MAX_PORTS 6 struct vt8500_port { @@ -85,6 +108,7 @@ struct vt8500_port { char name[16]; struct clk *clk; unsigned int ier; + unsigned int vt8500_uart_flags; }; /* @@ -272,7 +296,8 @@ static void vt8500_set_mctrl(struct uart_port *port, unsigned int mctrl) static void vt8500_break_ctl(struct uart_port *port, int break_ctl) { if (break_ctl) - vt8500_write(port, vt8500_read(port, VT8500_URLCR) | (1 << 9), + vt8500_write(port, + vt8500_read(port, VT8500_URLCR) | VT8500_BREAK, VT8500_URLCR); } @@ -347,31 +372,35 @@ static void vt8500_set_termios(struct uart_port *port, /* calculate parity */ lcr = vt8500_read(&vt8500_port->uart, VT8500_URLCR); - lcr &= ~((1 << 5) | (1 << 4)); + lcr &= ~(VT8500_PARENB | VT8500_PARODD); if (termios->c_cflag & PARENB) { - lcr |= (1 << 4); + lcr |= VT8500_PARENB; termios->c_cflag &= ~CMSPAR; if (termios->c_cflag & PARODD) - lcr |= (1 << 5); + lcr |= VT8500_PARODD; } /* calculate bits per char */ - lcr &= ~(1 << 2); + lcr &= ~VT8500_CS8; switch (termios->c_cflag & CSIZE) { case CS7: break; case CS8: default: - lcr |= (1 << 2); + lcr |= VT8500_CS8; termios->c_cflag &= ~CSIZE; termios->c_cflag |= CS8; break; } /* calculate stop bits */ - lcr &= ~(1 << 3); + lcr &= ~VT8500_CSTOPB; if (termios->c_cflag & CSTOPB) - lcr |= (1 << 3); + lcr |= VT8500_CSTOPB; + + lcr &= ~VT8500_SWRTSCTS; + if (vt8500_port->vt8500_uart_flags & VT8500_HAS_SWRTSCTS_SWITCH) + lcr |= VT8500_SWRTSCTS; /* set parity, bits per char, and stop bit */ vt8500_write(&vt8500_port->uart, lcr, VT8500_URLCR); @@ -548,14 +577,31 @@ static struct uart_driver vt8500_uart_driver = { .cons = VT8500_CONSOLE, }; +static unsigned int vt8500_flags; /* none required so far */ +static unsigned int wm8880_flags = VT8500_HAS_SWRTSCTS_SWITCH; + +static const struct of_device_id wmt_dt_ids[] = { + { .compatible = "via,vt8500-uart", .data = &vt8500_flags}, + { .compatible = "wm,wm8880-uart", .data = &wm8880_flags}, + {} +}; + static int vt8500_serial_probe(struct platform_device *pdev) { struct vt8500_port *vt8500_port; struct resource *mmres, *irqres; struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + const unsigned int *flags; int ret; int port; + match = of_match_device(wmt_dt_ids, &pdev->dev); + if (!match) + return -EINVAL; + + flags = match->data; + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!mmres || !irqres) @@ -605,6 +651,7 @@ static int vt8500_serial_probe(struct platform_device *pdev) return ret; } + vt8500_port->vt8500_uart_flags = *flags; vt8500_port->uart.type = PORT_VT8500; vt8500_port->uart.iotype = UPIO_MEM; vt8500_port->uart.mapbase = mmres->start; @@ -639,11 +686,6 @@ static int vt8500_serial_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id wmt_dt_ids[] = { - { .compatible = "via,vt8500-uart", }, - {} -}; - static struct platform_driver vt8500_platform_driver = { .probe = vt8500_serial_probe, .remove = vt8500_serial_remove, -- cgit v0.10.2 From 8c986d3e20bcbf33c680130a5798f25429c53e31 Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Sat, 6 Sep 2014 21:21:13 +0400 Subject: tty: vt8500_serial: add missing support for RTS setting Signed-off-by: Alexey Charkov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index f225719..47e74f9 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -291,6 +291,14 @@ static unsigned int vt8500_get_mctrl(struct uart_port *port) static void vt8500_set_mctrl(struct uart_port *port, unsigned int mctrl) { + unsigned int lcr = vt8500_read(port, VT8500_URLCR); + + if (mctrl & TIOCM_RTS) + lcr |= VT8500_RTS; + else + lcr &= ~VT8500_RTS; + + vt8500_write(port, lcr, VT8500_URLCR); } static void vt8500_break_ctl(struct uart_port *port, int break_ctl) -- cgit v0.10.2 From 1db894ecfa73759b1fde359562b9b4ef0e66e23d Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Sat, 6 Sep 2014 21:21:15 +0400 Subject: tty: vt8500_serial: add polled console functions This adds simple polling functions for single-character transmit and receive, as used by kgdb. Signed-off-by: Alexey Charkov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 47e74f9..4203862 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -558,6 +558,33 @@ static struct console vt8500_console = { #define VT8500_CONSOLE NULL #endif +#ifdef CONFIG_CONSOLE_POLL +static int vt8500_get_poll_char(struct uart_port *port) +{ + unsigned int status = vt8500_read(port, VT8500_URFIDX); + + if (!(status & 0x1f00)) + return NO_POLL_CHAR; + + return vt8500_read(port, VT8500_RXFIFO) & 0xff; +} + +static void vt8500_put_poll_char(struct uart_port *port, unsigned char c) +{ + unsigned int status, tmout = 10000; + + do { + status = vt8500_read(port, VT8500_URFIDX); + + if (--tmout == 0) + break; + udelay(1); + } while (status & 0x10); + + vt8500_write(port, c, VT8500_TXFIFO); +} +#endif + static struct uart_ops vt8500_uart_pops = { .tx_empty = vt8500_tx_empty, .set_mctrl = vt8500_set_mctrl, @@ -575,6 +602,10 @@ static struct uart_ops vt8500_uart_pops = { .request_port = vt8500_request_port, .config_port = vt8500_config_port, .verify_port = vt8500_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = vt8500_get_poll_char, + .poll_put_char = vt8500_put_poll_char, +#endif }; static struct uart_driver vt8500_uart_driver = { -- cgit v0.10.2 From 5aa387c152fce7ae5cb29f2b191a1570cd04db5f Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Sat, 6 Sep 2014 21:21:14 +0400 Subject: tty: vt8500_serial: explicitly calculate base baud rate Current code relies on the UART clock pre-divisor to be already configured in the baud rate register. Calculate it in the driver and set explicitly instead, also return the "real" effective baud rate, which is generally slightly different from the requested value. While at this, also ensure that break signal timing is updated when baud rate changes. Signed-off-by: Alexey Charkov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 4203862..b2bc9e8 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -101,12 +101,15 @@ #define VT8500_HAS_SWRTSCTS_SWITCH (1 << 1) +#define VT8500_RECOMMENDED_CLK 12000000 +#define VT8500_OVERSAMPLING_DIVISOR 13 #define VT8500_MAX_PORTS 6 struct vt8500_port { struct uart_port uart; char name[16]; struct clk *clk; + unsigned int clk_predivisor; unsigned int ier; unsigned int vt8500_uart_flags; }; @@ -311,20 +314,25 @@ static void vt8500_break_ctl(struct uart_port *port, int break_ctl) static int vt8500_set_baud_rate(struct uart_port *port, unsigned int baud) { + struct vt8500_port *vt8500_port = + container_of(port, struct vt8500_port, uart); unsigned long div; unsigned int loops = 1000; - div = vt8500_read(port, VT8500_URDIV) & ~(0x3ff); + div = ((vt8500_port->clk_predivisor - 1) & 0xf) << 16; + div |= (uart_get_divisor(port, baud) - 1) & 0x3ff; - if (unlikely((baud < 900) || (baud > 921600))) - div |= 7; - else - div |= (921600 / baud) - 1; + /* Effective baud rate */ + baud = port->uartclk / 16 / ((div & 0x3ff) + 1); while ((vt8500_read(port, VT8500_URUSR) & (1 << 5)) && --loops) cpu_relax(); + vt8500_write(port, div, VT8500_URDIV); + /* Break signal timing depends on baud rate, update accordingly */ + vt8500_write(port, mult_frac(baud, 4096, 1000000), VT8500_URBKR); + return baud; } @@ -691,6 +699,10 @@ static int vt8500_serial_probe(struct platform_device *pdev) } vt8500_port->vt8500_uart_flags = *flags; + vt8500_port->clk_predivisor = DIV_ROUND_CLOSEST( + clk_get_rate(vt8500_port->clk), + VT8500_RECOMMENDED_CLK + ); vt8500_port->uart.type = PORT_VT8500; vt8500_port->uart.iotype = UPIO_MEM; vt8500_port->uart.mapbase = mmres->start; @@ -701,7 +713,10 @@ static int vt8500_serial_probe(struct platform_device *pdev) vt8500_port->uart.dev = &pdev->dev; vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; - vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk); + /* Serial core uses the magic "16" everywhere - adjust for it */ + vt8500_port->uart.uartclk = 16 * clk_get_rate(vt8500_port->clk) / + vt8500_port->clk_predivisor / + VT8500_OVERSAMPLING_DIVISOR; snprintf(vt8500_port->name, sizeof(vt8500_port->name), "VT8500 UART%d", pdev->id); -- cgit v0.10.2 From 9d329c1c68d2cc625bb4b8191f37297db6061448 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Fri, 5 Sep 2014 18:57:57 -0700 Subject: tty/vt/keyboard: Resolve many shadow warnings Many local variables were given the same name as a global. This is valid, but generates many shadow warnings in W=2 builds. Resolve them by changing the local names. Also change local variables named "up" because they shadow the semaphore "up" function. Also moved the outer declaration of the variable "a" because it is only used in one block, and that resolves all of the shadow warnings for the other declarations of "a" that have different types. Change diacr => dia, kbd => kb, rep => rpt, up => udp. Signed-off-by: Mark Rustad Signed-off-by: Jeff Kirsher Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index d0e3a44..c039cfe 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -268,30 +268,30 @@ EXPORT_SYMBOL(kd_mksound); static int kbd_rate_helper(struct input_handle *handle, void *data) { struct input_dev *dev = handle->dev; - struct kbd_repeat *rep = data; + struct kbd_repeat *rpt = data; if (test_bit(EV_REP, dev->evbit)) { - if (rep[0].delay > 0) + if (rpt[0].delay > 0) input_inject_event(handle, - EV_REP, REP_DELAY, rep[0].delay); - if (rep[0].period > 0) + EV_REP, REP_DELAY, rpt[0].delay); + if (rpt[0].period > 0) input_inject_event(handle, - EV_REP, REP_PERIOD, rep[0].period); + EV_REP, REP_PERIOD, rpt[0].period); - rep[1].delay = dev->rep[REP_DELAY]; - rep[1].period = dev->rep[REP_PERIOD]; + rpt[1].delay = dev->rep[REP_DELAY]; + rpt[1].period = dev->rep[REP_PERIOD]; } return 0; } -int kbd_rate(struct kbd_repeat *rep) +int kbd_rate(struct kbd_repeat *rpt) { - struct kbd_repeat data[2] = { *rep }; + struct kbd_repeat data[2] = { *rpt }; input_handler_for_each_handle(&kbd_handler, data, kbd_rate_helper); - *rep = data[1]; /* Copy currently used settings */ + *rpt = data[1]; /* Copy currently used settings */ return 0; } @@ -971,15 +971,15 @@ static unsigned char getledstate(void) return ledstate; } -void setledstate(struct kbd_struct *kbd, unsigned int led) +void setledstate(struct kbd_struct *kb, unsigned int led) { unsigned long flags; spin_lock_irqsave(&led_lock, flags); if (!(led & ~7)) { ledioctl = led; - kbd->ledmode = LED_SHOW_IOCTL; + kb->ledmode = LED_SHOW_IOCTL; } else - kbd->ledmode = LED_SHOW_FLAGS; + kb->ledmode = LED_SHOW_FLAGS; set_leds(); spin_unlock_irqrestore(&led_lock, flags); @@ -987,12 +987,12 @@ void setledstate(struct kbd_struct *kbd, unsigned int led) static inline unsigned char getleds(void) { - struct kbd_struct *kbd = kbd_table + fg_console; + struct kbd_struct *kb = kbd_table + fg_console; - if (kbd->ledmode == LED_SHOW_IOCTL) + if (kb->ledmode == LED_SHOW_IOCTL) return ledioctl; - return kbd->ledflagstate; + return kb->ledflagstate; } static int kbd_update_leds_helper(struct input_handle *handle, void *data) @@ -1018,12 +1018,12 @@ static int kbd_update_leds_helper(struct input_handle *handle, void *data) */ int vt_get_leds(int console, int flag) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; int ret; unsigned long flags; spin_lock_irqsave(&led_lock, flags); - ret = vc_kbd_led(kbd, flag); + ret = vc_kbd_led(kb, flag); spin_unlock_irqrestore(&led_lock, flags); return ret; @@ -1040,8 +1040,8 @@ EXPORT_SYMBOL_GPL(vt_get_leds); */ void vt_set_led_state(int console, int leds) { - struct kbd_struct * kbd = kbd_table + console; - setledstate(kbd, leds); + struct kbd_struct *kb = kbd_table + console; + setledstate(kb, leds); } /** @@ -1059,10 +1059,10 @@ void vt_set_led_state(int console, int leds) */ void vt_kbd_con_start(int console) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; unsigned long flags; spin_lock_irqsave(&led_lock, flags); - clr_vc_kbd_led(kbd, VC_SCROLLOCK); + clr_vc_kbd_led(kb, VC_SCROLLOCK); set_leds(); spin_unlock_irqrestore(&led_lock, flags); } @@ -1076,10 +1076,10 @@ void vt_kbd_con_start(int console) */ void vt_kbd_con_stop(int console) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; unsigned long flags; spin_lock_irqsave(&led_lock, flags); - set_vc_kbd_led(kbd, VC_SCROLLOCK); + set_vc_kbd_led(kb, VC_SCROLLOCK); set_leds(); spin_unlock_irqrestore(&led_lock, flags); } @@ -1512,15 +1512,14 @@ int __init kbd_init(void) /** * vt_do_diacrit - diacritical table updates * @cmd: ioctl request - * @up: pointer to user data for ioctl + * @udp: pointer to user data for ioctl * @perm: permissions check computed by caller * * Update the diacritical tables atomically and safely. Lock them * against simultaneous keypresses */ -int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) +int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm) { - struct kbdiacrs __user *a = up; unsigned long flags; int asize; int ret = 0; @@ -1528,12 +1527,13 @@ int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) switch (cmd) { case KDGKBDIACR: { - struct kbdiacr *diacr; + struct kbdiacrs __user *a = udp; + struct kbdiacr *dia; int i; - diacr = kmalloc(MAX_DIACR * sizeof(struct kbdiacr), + dia = kmalloc(MAX_DIACR * sizeof(struct kbdiacr), GFP_KERNEL); - if (diacr == NULL) + if (!dia) return -ENOMEM; /* Lock the diacriticals table, make a copy and then @@ -1542,26 +1542,26 @@ int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) asize = accent_table_size; for (i = 0; i < asize; i++) { - diacr[i].diacr = conv_uni_to_8bit( + dia[i].diacr = conv_uni_to_8bit( accent_table[i].diacr); - diacr[i].base = conv_uni_to_8bit( + dia[i].base = conv_uni_to_8bit( accent_table[i].base); - diacr[i].result = conv_uni_to_8bit( + dia[i].result = conv_uni_to_8bit( accent_table[i].result); } spin_unlock_irqrestore(&kbd_event_lock, flags); if (put_user(asize, &a->kb_cnt)) ret = -EFAULT; - else if (copy_to_user(a->kbdiacr, diacr, + else if (copy_to_user(a->kbdiacr, dia, asize * sizeof(struct kbdiacr))) ret = -EFAULT; - kfree(diacr); + kfree(dia); return ret; } case KDGKBDIACRUC: { - struct kbdiacrsuc __user *a = up; + struct kbdiacrsuc __user *a = udp; void *buf; buf = kmalloc(MAX_DIACR * sizeof(struct kbdiacruc), @@ -1589,8 +1589,8 @@ int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) case KDSKBDIACR: { - struct kbdiacrs __user *a = up; - struct kbdiacr *diacr = NULL; + struct kbdiacrs __user *a = udp; + struct kbdiacr *dia = NULL; unsigned int ct; int i; @@ -1602,14 +1602,14 @@ int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) return -EINVAL; if (ct) { - diacr = kmalloc(sizeof(struct kbdiacr) * ct, + dia = kmalloc(sizeof(struct kbdiacr) * ct, GFP_KERNEL); - if (diacr == NULL) + if (!dia) return -ENOMEM; - if (copy_from_user(diacr, a->kbdiacr, + if (copy_from_user(dia, a->kbdiacr, sizeof(struct kbdiacr) * ct)) { - kfree(diacr); + kfree(dia); return -EFAULT; } } @@ -1618,20 +1618,20 @@ int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) accent_table_size = ct; for (i = 0; i < ct; i++) { accent_table[i].diacr = - conv_8bit_to_uni(diacr[i].diacr); + conv_8bit_to_uni(dia[i].diacr); accent_table[i].base = - conv_8bit_to_uni(diacr[i].base); + conv_8bit_to_uni(dia[i].base); accent_table[i].result = - conv_8bit_to_uni(diacr[i].result); + conv_8bit_to_uni(dia[i].result); } spin_unlock_irqrestore(&kbd_event_lock, flags); - kfree(diacr); + kfree(dia); return 0; } case KDSKBDIACRUC: { - struct kbdiacrsuc __user *a = up; + struct kbdiacrsuc __user *a = udp; unsigned int ct; void *buf = NULL; @@ -1679,28 +1679,28 @@ int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) */ int vt_do_kdskbmode(int console, unsigned int arg) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; int ret = 0; unsigned long flags; spin_lock_irqsave(&kbd_event_lock, flags); switch(arg) { case K_RAW: - kbd->kbdmode = VC_RAW; + kb->kbdmode = VC_RAW; break; case K_MEDIUMRAW: - kbd->kbdmode = VC_MEDIUMRAW; + kb->kbdmode = VC_MEDIUMRAW; break; case K_XLATE: - kbd->kbdmode = VC_XLATE; + kb->kbdmode = VC_XLATE; do_compute_shiftstate(); break; case K_UNICODE: - kbd->kbdmode = VC_UNICODE; + kb->kbdmode = VC_UNICODE; do_compute_shiftstate(); break; case K_OFF: - kbd->kbdmode = VC_OFF; + kb->kbdmode = VC_OFF; break; default: ret = -EINVAL; @@ -1719,17 +1719,17 @@ int vt_do_kdskbmode(int console, unsigned int arg) */ int vt_do_kdskbmeta(int console, unsigned int arg) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; int ret = 0; unsigned long flags; spin_lock_irqsave(&kbd_event_lock, flags); switch(arg) { case K_METABIT: - clr_vc_kbd_mode(kbd, VC_META); + clr_vc_kbd_mode(kb, VC_META); break; case K_ESCPREFIX: - set_vc_kbd_mode(kbd, VC_META); + set_vc_kbd_mode(kb, VC_META); break; default: ret = -EINVAL; @@ -1768,7 +1768,7 @@ int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, int console) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; struct kbentry tmp; ushort *key_map, *new_map, val, ov; unsigned long flags; @@ -1786,7 +1786,7 @@ int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, key_map = key_maps[s]; if (key_map) { val = U(key_map[i]); - if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES) + if (kb->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES) val = K_HOLE; } else val = (i ? K_HOLE : K_NOSUCHMAP); @@ -1814,7 +1814,7 @@ int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, if (KVAL(v) > max_vals[KTYP(v)]) return -EINVAL; } else - if (kbd->kbdmode != VC_UNICODE) + if (kb->kbdmode != VC_UNICODE) return -EINVAL; /* ++Geert: non-PC keyboards may generate keycode zero */ @@ -1985,7 +1985,7 @@ reterr: int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; unsigned long flags; unsigned char ucval; @@ -1994,7 +1994,7 @@ int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm) /* don't use them - they will go away without warning */ case KDGKBLED: spin_lock_irqsave(&kbd_event_lock, flags); - ucval = kbd->ledflagstate | (kbd->default_ledflagstate << 4); + ucval = kb->ledflagstate | (kb->default_ledflagstate << 4); spin_unlock_irqrestore(&kbd_event_lock, flags); return put_user(ucval, (char __user *)arg); @@ -2004,8 +2004,8 @@ int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm) if (arg & ~0x77) return -EINVAL; spin_lock_irqsave(&led_lock, flags); - kbd->ledflagstate = (arg & 7); - kbd->default_ledflagstate = ((arg >> 4) & 7); + kb->ledflagstate = (arg & 7); + kb->default_ledflagstate = ((arg >> 4) & 7); set_leds(); spin_unlock_irqrestore(&led_lock, flags); return 0; @@ -2019,7 +2019,7 @@ int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm) case KDSETLED: if (!perm) return -EPERM; - setledstate(kbd, arg); + setledstate(kb, arg); return 0; } return -ENOIOCTLCMD; @@ -2027,9 +2027,9 @@ int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm) int vt_do_kdgkbmode(int console) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; /* This is a spot read so needs no locking */ - switch (kbd->kbdmode) { + switch (kb->kbdmode) { case VC_RAW: return K_RAW; case VC_MEDIUMRAW: @@ -2051,9 +2051,9 @@ int vt_do_kdgkbmode(int console) */ int vt_do_kdgkbmeta(int console) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; /* Again a spot read so no locking */ - return vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT; + return vc_kbd_mode(kb, VC_META) ? K_ESCPREFIX : K_METABIT; } /** @@ -2092,19 +2092,19 @@ int vt_get_shift_state(void) */ void vt_reset_keyboard(int console) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; unsigned long flags; spin_lock_irqsave(&kbd_event_lock, flags); - set_vc_kbd_mode(kbd, VC_REPEAT); - clr_vc_kbd_mode(kbd, VC_CKMODE); - clr_vc_kbd_mode(kbd, VC_APPLIC); - clr_vc_kbd_mode(kbd, VC_CRLF); - kbd->lockstate = 0; - kbd->slockstate = 0; + set_vc_kbd_mode(kb, VC_REPEAT); + clr_vc_kbd_mode(kb, VC_CKMODE); + clr_vc_kbd_mode(kb, VC_APPLIC); + clr_vc_kbd_mode(kb, VC_CRLF); + kb->lockstate = 0; + kb->slockstate = 0; spin_lock(&led_lock); - kbd->ledmode = LED_SHOW_FLAGS; - kbd->ledflagstate = kbd->default_ledflagstate; + kb->ledmode = LED_SHOW_FLAGS; + kb->ledflagstate = kb->default_ledflagstate; spin_unlock(&led_lock); /* do not do set_leds here because this causes an endless tasklet loop when the keyboard hasn't been initialized yet */ @@ -2122,8 +2122,8 @@ void vt_reset_keyboard(int console) int vt_get_kbd_mode_bit(int console, int bit) { - struct kbd_struct * kbd = kbd_table + console; - return vc_kbd_mode(kbd, bit); + struct kbd_struct *kb = kbd_table + console; + return vc_kbd_mode(kb, bit); } /** @@ -2137,11 +2137,11 @@ int vt_get_kbd_mode_bit(int console, int bit) void vt_set_kbd_mode_bit(int console, int bit) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; unsigned long flags; spin_lock_irqsave(&kbd_event_lock, flags); - set_vc_kbd_mode(kbd, bit); + set_vc_kbd_mode(kb, bit); spin_unlock_irqrestore(&kbd_event_lock, flags); } @@ -2156,10 +2156,10 @@ void vt_set_kbd_mode_bit(int console, int bit) void vt_clr_kbd_mode_bit(int console, int bit) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; unsigned long flags; spin_lock_irqsave(&kbd_event_lock, flags); - clr_vc_kbd_mode(kbd, bit); + clr_vc_kbd_mode(kb, bit); spin_unlock_irqrestore(&kbd_event_lock, flags); } -- cgit v0.10.2 From ae14a7954f5124208e6e93cafb3099f83acd43f5 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 5 Sep 2014 21:02:36 +0200 Subject: tty: serial: 8250_core: provide a function to export uart_8250_port There is no way to access a struct uart_8250_port for a specific line. This is only required outside of the 8250/uart callbacks like for devices' suspend & remove callbacks. For those the 8250-core provides a wrapper like serial8250_unregister_port() which passes the struct to the proper function based on the line argument. For run time suspend I need access to this struct not only to make serial_out() work but also to properly restore up->ier and up->mcr. Acked-by: Alan Cox Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 1b08c91..85bfec5 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -112,6 +112,8 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value) up->dl_write(up, value); } +struct uart_8250_port *serial8250_get_port(int line); + #if defined(__alpha__) && !defined(CONFIG_PCI) /* * Digital did something really horribly wrong with the OUT1 and OUT2 diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index b597e73..90e0d5e 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2916,6 +2916,24 @@ static struct uart_ops serial8250_pops = { static struct uart_8250_port serial8250_ports[UART_NR]; +/** + * serial8250_get_port - retrieve struct uart_8250_port + * @line: serial line number + * + * This function retrieves struct uart_8250_port for the specific line. + * This struct *must* *not* be used to perform a 8250 or serial core operation + * which is not accessible otherwise. Its only purpose is to make the struct + * accessible to the runtime-pm callbacks for context suspend/restore. + * The lock assumption made here is none because runtime-pm suspend/resume + * callbacks should not be invoked if there is any operation performed on the + * port. + */ +struct uart_8250_port *serial8250_get_port(int line) +{ + return &serial8250_ports[line]; +} +EXPORT_SYMBOL_GPL(serial8250_get_port); + static void (*serial8250_isa_config)(int port, struct uart_port *up, unsigned short *capabilities); -- cgit v0.10.2 From b99b121b2aa42e60e5b73fdd3a49863337839c7b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 5 Sep 2014 21:02:37 +0200 Subject: tty: serial: 8250_core: allow to overwrite & export serial8250_startup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OMAP version of the 8250 can actually use 1:1 serial8250_startup(). However it needs to be extended by a wake up irq which should to be requested & enabled at ->startup() time and disabled at ->shutdown() time. v2…v3: properly copy callbacks v1…v2: add shutdown callback Acked-by: Alan Cox Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 90e0d5e..872c020 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1930,7 +1930,7 @@ static void serial8250_put_poll_char(struct uart_port *port, #endif /* CONFIG_CONSOLE_POLL */ -static int serial8250_startup(struct uart_port *port) +int serial8250_do_startup(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; @@ -2181,8 +2181,16 @@ dont_test_tx_en: return 0; } +EXPORT_SYMBOL_GPL(serial8250_do_startup); -static void serial8250_shutdown(struct uart_port *port) +static int serial8250_startup(struct uart_port *port) +{ + if (port->startup) + return port->startup(port); + return serial8250_do_startup(port); +} + +void serial8250_do_shutdown(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; @@ -2232,6 +2240,15 @@ static void serial8250_shutdown(struct uart_port *port) if (port->irq) serial_unlink_irq_chain(up); } +EXPORT_SYMBOL_GPL(serial8250_do_shutdown); + +static void serial8250_shutdown(struct uart_port *port) +{ + if (port->shutdown) + port->shutdown(port); + else + serial8250_do_shutdown(port); +} static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud) { @@ -3466,6 +3483,10 @@ int serial8250_register_8250_port(struct uart_8250_port *up) /* Possibly override set_termios call */ if (up->port.set_termios) uart->port.set_termios = up->port.set_termios; + if (up->port.startup) + uart->port.startup = up->port.startup; + if (up->port.shutdown) + uart->port.shutdown = up->port.shutdown; if (up->port.pm) uart->port.pm = up->port.pm; if (up->port.handle_break) diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 6dd6717..6fc9d7b 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -124,6 +124,8 @@ extern void serial8250_early_out(struct uart_port *port, int offset, int value); extern int setup_early_serial8250_console(char *cmdline); extern void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old); +extern int serial8250_do_startup(struct uart_port *port); +extern void serial8250_do_shutdown(struct uart_port *port); extern void serial8250_do_pm(struct uart_port *port, unsigned int state, unsigned int oldstate); extern int fsl8250_handle_irq(struct uart_port *port); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 8cb267b..3bd7d55 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -122,6 +122,8 @@ struct uart_port { void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old); + int (*startup)(struct uart_port *port); + void (*shutdown)(struct uart_port *port); int (*handle_irq)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, unsigned int old); -- cgit v0.10.2 From 0b4af1d94903143f88e541b00f028fa449a26f73 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Tue, 9 Sep 2014 07:17:45 +0200 Subject: serial/8250_core: Add reference to uacess.h Commit: e676253b19b2d269cccf67fdb1592120a0cd0676 [3/21] serial/8250: Add support for RS485 IOCTLs, adds a building error on arch m32r. All error/warnings: drivers/tty/serial/8250/8250_core.c: In function 'serial8250_ioctl': >> drivers/tty/serial/8250/8250_core.c:2859:3: error: implicit declaration of function 'copy_from_user' [-Werror=implicit-function-declaration] if (copy_from_user(&rs485_config, (void __user *)arg, ^ >> drivers/tty/serial/8250/8250_core.c:2871:3: error: implicit declaration of function 'copy_to_user' [-Werror=implicit-function-declaration] if (copy_to_user((void __user *)arg, &up->rs485, ^ cc1: some warnings being treated as errors Reported-by: kbuild test robot Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 872c020..a0c1d64 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef CONFIG_SPARC #include #endif -- cgit v0.10.2 From a9a2eab5fb65232512adac58898eef835124a40e Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Tue, 9 Sep 2014 21:39:24 +0200 Subject: xtensa/uapi: Add definition of TIOC[SG]RS485 Commit: e676253b19b2d269cccf67fdb1592120a0cd0676 [3/21] serial/8250: Add support for RS485 IOCTLs, adds support for RS485 ioctls for 825_core on all the archs. Unfortunaltely the definition of TIOCSRS485 and TIOCGRS485 was missing on the ioctls.h file Reported-by: kbuild test robot Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/arch/xtensa/include/uapi/asm/ioctls.h b/arch/xtensa/include/uapi/asm/ioctls.h index a47909f..518954e 100644 --- a/arch/xtensa/include/uapi/asm/ioctls.h +++ b/arch/xtensa/include/uapi/asm/ioctls.h @@ -95,6 +95,8 @@ #define TCSETS2 _IOW('T', 43, struct termios2) #define TCSETSW2 _IOW('T', 44, struct termios2) #define TCSETSF2 _IOW('T', 45, struct termios2) +#define TIOCGRS485 _IOR('T', 46, struct serial_rs485) +#define TIOCSRS485 _IOWR('T', 47, struct serial_rs485) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ -- cgit v0.10.2 From 12b46b66f098de4b72ea6f14b8228d1e71ab9fd1 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Tue, 9 Sep 2014 20:58:15 +0200 Subject: parisc/uapi: Add definition of TIOC[SG]RS485 Commit: e676253b19b2d269cccf67fdb1592120a0cd0676 (serial/8250: Add support for RS485 IOCTLs), adds support for RS485 ioctls for 825_core on all the archs. Unfortunaltely the definition of TIOCSRS485 and TIOCGRS485 was missing on the ioctls.h file Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/arch/parisc/include/uapi/asm/ioctls.h b/arch/parisc/include/uapi/asm/ioctls.h index 66719c3..b6572f0 100644 --- a/arch/parisc/include/uapi/asm/ioctls.h +++ b/arch/parisc/include/uapi/asm/ioctls.h @@ -50,6 +50,8 @@ #define TCSETS2 _IOW('T',0x2B, struct termios2) #define TCSETSW2 _IOW('T',0x2C, struct termios2) #define TCSETSF2 _IOW('T',0x2D, struct termios2) +#define TIOCGRS485 _IOR('T', 0x2E, struct serial_rs485) +#define TIOCSRS485 _IOWR('T', 0x2F, struct serial_rs485) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ #define TIOCGDEV _IOR('T',0x32, int) /* Get primary device node of /dev/console */ -- cgit v0.10.2 From 8e63aee564229f95d1e1d7e5e21ffe2622f28f16 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Tue, 9 Sep 2014 20:59:50 +0200 Subject: sh/uapi: Add definition of TIOC[SG]RS485 Commit: e676253b19b2d269cccf67fdb1592120a0cd0676 (serial/8250: Add support for RS485 IOCTLs), adds support for RS485 ioctls for 825_core on all the archs. Unfortunaltely the definition of TIOCSRS485 and TIOCGRS485 was missing on the ioctls.h file Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/arch/sh/include/uapi/asm/ioctls.h b/arch/sh/include/uapi/asm/ioctls.h index 3422410..c9903e5 100644 --- a/arch/sh/include/uapi/asm/ioctls.h +++ b/arch/sh/include/uapi/asm/ioctls.h @@ -83,6 +83,8 @@ #define TCSETS2 _IOW('T', 43, struct termios2) #define TCSETSW2 _IOW('T', 44, struct termios2) #define TCSETSF2 _IOW('T', 45, struct termios2) +#define TIOCGRS485 _IOR('T', 46, struct serial_rs485) +#define TIOCSRS485 _IOWR('T', 47, struct serial_rs485) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ -- cgit v0.10.2 From 999156ada570cb4a2eaee42e47c9e659b5c577fb Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Tue, 9 Sep 2014 20:37:59 +0200 Subject: sparc/uapi: Add definition of TIOC[SG]RS485 Commit: e676253b19b2d269cccf67fdb1592120a0cd0676 (serial/8250: Add support for RS485 IOCTLs), adds support for RS485 ioctls for 825_core on all the archs. Unfortunaltely the definition of TIOCSRS485 and TIOCGRS485 was missing on the ioctls.h file Signed-off-by: Ricardo Ribalda Delgado Acked-by: David S. Miller Signed-off-by: Greg Kroah-Hartman diff --git a/arch/sparc/include/uapi/asm/ioctls.h b/arch/sparc/include/uapi/asm/ioctls.h index 897d172..06b3f6c 100644 --- a/arch/sparc/include/uapi/asm/ioctls.h +++ b/arch/sparc/include/uapi/asm/ioctls.h @@ -24,6 +24,8 @@ #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ +#define TIOCGRS485 _IOR('T', 0x41, struct serial_rs485) +#define TIOCSRS485 _IOWR('T', 0x42, struct serial_rs485) /* Note that all the ioctls that are not available in Linux have a * double underscore on the front to: a) avoid some programs to -- cgit v0.10.2 From efb089517d2f34a58aceb600ab58b0ca3db1fa07 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 9 Sep 2014 08:14:36 +0400 Subject: serial: clps711x: Fix COMPILE_TEST build for target without GPIOLIB support The patch fixes the following build error of CLPS711X serial driver for targets without GPIOLIB support: >> drivers/tty/serial/serial_mctrl_gpio.c:44:6: error: redefinition of 'mctrl_gpio_set' void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) ^ In file included from drivers/tty/serial/serial_mctrl_gpio.c:23:0: drivers/tty/serial/serial_mctrl_gpio.h:80:6: note: previous definition of 'mctrl_gpio_set' was here void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) ^ Reported-by: kbuild test robot Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 8079f52..81f6ee7 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -222,7 +222,7 @@ config SERIAL_CLPS711X tristate "CLPS711X serial port support" depends on ARCH_CLPS711X || COMPILE_TEST select SERIAL_CORE - select SERIAL_MCTRL_GPIO + select SERIAL_MCTRL_GPIO if GPIOLIB help This enables the driver for the on-chip UARTs of the Cirrus Logic EP711x/EP721x/EP731x processors. -- cgit v0.10.2 From 08177ece596ccc9b9c194542c095c863c101fd11 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 9 Sep 2014 11:03:57 +0100 Subject: serial: asc: Adopt readl_/writel_relaxed() The architectures supported by this driver, arm and sh, have expensive implementations of writel(), reliant on spin locks and explicit L2 cache management. These architectures provide a cheaper writel_relaxed() which is much better suited to peripherals that do not perform DMA. The situation with readl()/readl_relaxed()is similar although less acute. This driver does not use DMA and will be more power efficient and more robust (due to absence of spin locks during console I/O) if it uses the relaxed variants. The driver supports COMPILE_TEST and therefore falls back to writel() when writel_relaxed() does not exist. Signed-off-by: Daniel Thompson Acked-by: Srinivas Kandagatla Cc: Maxime Coquelin Cc: Patrice Chotard Cc: Jiri Slaby Cc: kernel@stlinux.com Cc: linux-serial@vger.kernel.org Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index 8b2d735..a3fc167 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -151,12 +151,16 @@ static inline struct asc_port *to_asc_port(struct uart_port *port) static inline u32 asc_in(struct uart_port *port, u32 offset) { - return readl(port->membase + offset); + return readl_relaxed(port->membase + offset); } static inline void asc_out(struct uart_port *port, u32 offset, u32 value) { +#ifdef writel_relaxed + writel_relaxed(value, port->membase + offset); +#else writel(value, port->membase + offset); +#endif } /* -- cgit v0.10.2 From b4756f4f0d773c31e59f203e7f19fd3d5c490193 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Tue, 9 Sep 2014 17:31:42 +0200 Subject: tty: serial: 8250: Add Mediatek UART driver The device has a highspeed register which influences the calcualtion of the divisor. The chip lacks support for some baudrates. When requested, we set the divisor to the next smaller baudrate and adjust the c_cflag accordingly. Signed-off-by: Matthias Brugger Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c new file mode 100644 index 0000000..8f37d57 --- /dev/null +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -0,0 +1,294 @@ +/* + * Mediatek 8250 driver. + * + * Copyright (c) 2014 MundoReader S.L. + * Author: Matthias Brugger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "8250.h" + +#define UART_MTK_HIGHS 0x09 /* Highspeed register */ +#define UART_MTK_SAMPLE_COUNT 0x0a /* Sample count register */ +#define UART_MTK_SAMPLE_POINT 0x0b /* Sample point register */ +#define MTK_UART_RATE_FIX 0x0d /* UART Rate Fix Register */ + +struct mtk8250_data { + int line; + struct clk *uart_clk; +}; + +static void +mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, quot; + + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + serial8250_do_set_termios(port, termios, old); + + /* + * Mediatek UARTs use an extra highspeed register (UART_MTK_HIGHS) + * + * We need to recalcualte the quot register, as the claculation depends + * on the vaule in the highspeed register. + * + * Some baudrates are not supported by the chip, so we use the next + * lower rate supported and update termios c_flag. + * + * If highspeed register is set to 3, we need to specify sample count + * and sample point to increase accuracy. If not, we reset the + * registers to their default values. + */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 0xffff, + port->uartclk / 16); + + if (baud <= 115200) { + serial_port_out(port, UART_MTK_HIGHS, 0x0); + quot = uart_get_divisor(port, baud); + } else if (baud <= 576000) { + serial_port_out(port, UART_MTK_HIGHS, 0x2); + + /* Set to next lower baudrate supported */ + if ((baud == 500000) || (baud == 576000)) + baud = 460800; + quot = DIV_ROUND_CLOSEST(port->uartclk, 4 * baud); + } else { + serial_port_out(port, UART_MTK_HIGHS, 0x3); + + /* Set to highest baudrate supported */ + if (baud >= 1152000) + baud = 921600; + quot = DIV_ROUND_CLOSEST(port->uartclk, 256 * baud); + } + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&port->lock, flags); + + /* set DLAB we have cval saved in up->lcr from the call to the core */ + serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB); + serial_dl_write(up, quot); + + /* reset DLAB */ + serial_port_out(port, UART_LCR, up->lcr); + + if (baud > 460800) { + unsigned int tmp; + + tmp = DIV_ROUND_CLOSEST(port->uartclk, quot * baud); + serial_port_out(port, UART_MTK_SAMPLE_COUNT, tmp - 1); + serial_port_out(port, UART_MTK_SAMPLE_POINT, + (tmp - 2) >> 1); + } else { + serial_port_out(port, UART_MTK_SAMPLE_COUNT, 0x00); + serial_port_out(port, UART_MTK_SAMPLE_POINT, 0xff); + } + + spin_unlock_irqrestore(&port->lock, flags); + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +static void +mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) +{ + if (!state) + pm_runtime_get_sync(port->dev); + + serial8250_do_pm(port, state, old); + + if (state) + pm_runtime_put_sync_suspend(port->dev); +} + +static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p, + struct mtk8250_data *data) +{ + int err; + struct device_node *np = pdev->dev.of_node; + + data->uart_clk = of_clk_get(np, 0); + if (IS_ERR(data->uart_clk)) { + dev_warn(&pdev->dev, "Can't get timer clock\n"); + return PTR_ERR(data->uart_clk); + } + + err = clk_prepare_enable(data->uart_clk); + if (err) { + dev_warn(&pdev->dev, "Can't prepare clock\n"); + clk_put(data->uart_clk); + return err; + } + p->uartclk = clk_get_rate(data->uart_clk); + + return 0; +} + +static int mtk8250_probe(struct platform_device *pdev) +{ + struct uart_8250_port uart = {}; + struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + struct mtk8250_data *data; + int err; + + if (!regs || !irq) { + dev_err(&pdev->dev, "no registers/irq defined\n"); + return -EINVAL; + } + + uart.port.membase = devm_ioremap(&pdev->dev, regs->start, + resource_size(regs)); + if (!uart.port.membase) + return -ENOMEM; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (pdev->dev.of_node) { + err = mtk8250_probe_of(pdev, &uart.port, data); + if (err) + return err; + } else + return -ENODEV; + + spin_lock_init(&uart.port.lock); + uart.port.mapbase = regs->start; + uart.port.irq = irq->start; + uart.port.pm = mtk8250_do_pm; + uart.port.type = PORT_16550; + uart.port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; + uart.port.dev = &pdev->dev; + uart.port.iotype = UPIO_MEM32; + uart.port.regshift = 2; + uart.port.private_data = data; + uart.port.set_termios = mtk8250_set_termios; + + /* Disable Rate Fix function */ + writel(0x0, uart.port.membase + + (MTK_UART_RATE_FIX << uart.port.regshift)); + + data->line = serial8250_register_8250_port(&uart); + if (data->line < 0) + return data->line; + + platform_set_drvdata(pdev, data); + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + return 0; +} + +static int mtk8250_remove(struct platform_device *pdev) +{ + struct mtk8250_data *data = platform_get_drvdata(pdev); + + pm_runtime_get_sync(&pdev->dev); + + serial8250_unregister_port(data->line); + if (!IS_ERR(data->uart_clk)) { + clk_disable_unprepare(data->uart_clk); + clk_put(data->uart_clk); + } + + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mtk8250_suspend(struct device *dev) +{ + struct mtk8250_data *data = dev_get_drvdata(dev); + + serial8250_suspend_port(data->line); + + return 0; +} + +static int mtk8250_resume(struct device *dev) +{ + struct mtk8250_data *data = dev_get_drvdata(dev); + + serial8250_resume_port(data->line); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM_RUNTIME +static int mtk8250_runtime_suspend(struct device *dev) +{ + struct mtk8250_data *data = dev_get_drvdata(dev); + + if (!IS_ERR(data->uart_clk)) + clk_disable_unprepare(data->uart_clk); + + return 0; +} + +static int mtk8250_runtime_resume(struct device *dev) +{ + struct mtk8250_data *data = dev_get_drvdata(dev); + + if (!IS_ERR(data->uart_clk)) + clk_prepare_enable(data->uart_clk); + + return 0; +} +#endif + +static const struct dev_pm_ops mtk8250_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mtk8250_suspend, mtk8250_resume) + SET_RUNTIME_PM_OPS(mtk8250_runtime_suspend, mtk8250_runtime_resume, + NULL) +}; + +static const struct of_device_id mtk8250_of_match[] = { + { .compatible = "mediatek,mt6577-uart" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mtk8250_of_match); + +static struct platform_driver mtk8250_platform_driver = { + .driver = { + .name = "mt6577-uart", + .pm = &mtk8250_pm_ops, + .of_match_table = mtk8250_of_match, + }, + .probe = mtk8250_probe, + .remove = mtk8250_remove, +}; +module_platform_driver(mtk8250_platform_driver); + +MODULE_AUTHOR("Matthias Brugger"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Mediatek 8250 serial port driver"); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 8b5c40a..21eca79 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -307,3 +307,9 @@ config SERIAL_8250_FINTEK LPC to 4 UART. This device has some RS485 functionality not available through the PNP driver. If unsure, say N. +config SERIAL_8250_MT6577 + bool "Mediatek serial port support" + depends on SERIAL_8250 && ARCH_MEDIATEK + help + If you have a Mediatek based board and want to use the + serial port, say Y to this option. If unsure, say N. diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index e08407d..5256b89 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -21,3 +21,4 @@ obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o obj-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o +obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o -- cgit v0.10.2 From 76ce677063e9194d48d9d44377c4f7f7c1c2e3d3 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Tue, 9 Sep 2014 17:31:43 +0200 Subject: DTS: serial: Add bindings documention for the Mediatek UARTs This patch adds the devicetree documentation for the Mediatek UART. Signed-off-by: Matthias Brugger Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/mtk-uart.txt b/Documentation/devicetree/bindings/serial/mtk-uart.txt new file mode 100644 index 0000000..48358a3 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/mtk-uart.txt @@ -0,0 +1,22 @@ +* Mediatek Universal Asynchronous Receiver/Transmitter (UART) + +Required properties: +- compatible should contain: + * "mediatek,mt6589-uart" for MT6589 compatible UARTS + * "mediatek,mt6582-uart" for MT6582 compatible UARTS + * "mediatek,mt6577-uart" for all compatible UARTS (MT6589, MT6582, MT6577) + +- reg: The base address of the UART register bank. + +- interrupts: A single interrupt specifier. + +- clocks: Clock driving the hardware. + +Example: + + uart0: serial@11006000 { + compatible = "mediatek,mt6589-uart", "mediatek,mt6577-uart"; + reg = <0x11006000 0x400>; + interrupts = ; + clocks = <&uart_clk>; + }; -- cgit v0.10.2 From 1c84cd48a117486166f3597c081b170b76e5bd81 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Wed, 10 Sep 2014 10:57:08 +0200 Subject: mips/uapi: Add definition of TIOC[SG]RS485 Commit: e676253b19b2d269cccf67fdb1592120a0cd0676 (serial/8250: Add support for RS485 IOCTLs), adds support for RS485 ioctls for 825_core on all the archs. Unfortunaltely the definition of TIOCSRS485 and TIOCGRS485 was missing on the ioctls.h file Reported-by: Markos Chandras Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/arch/mips/include/uapi/asm/ioctls.h b/arch/mips/include/uapi/asm/ioctls.h index b1e6377..740219c 100644 --- a/arch/mips/include/uapi/asm/ioctls.h +++ b/arch/mips/include/uapi/asm/ioctls.h @@ -81,6 +81,8 @@ #define TCSETS2 _IOW('T', 0x2B, struct termios2) #define TCSETSW2 _IOW('T', 0x2C, struct termios2) #define TCSETSF2 _IOW('T', 0x2D, struct termios2) +#define TIOCGRS485 _IOR('T', 0x2E, struct serial_rs485) +#define TIOCSRS485 _IOWR('T', 0x2F, struct serial_rs485) #define TIOCGPTN _IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */ #define TIOCGDEV _IOR('T', 0x32, unsigned int) /* Get primary device node of /dev/console */ -- cgit v0.10.2 From 93b8877471796c04c16fdef755d4e5c0f521509f Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 20 Sep 2014 09:34:45 +0400 Subject: tty: serial_mctrl_gpio: Fix COMPILE_TEST build for architectures with custom termios.h This patch fixes COMPILE_TEST build of serial_mctrl_gpio module for architectures with custom termios.h header. sparc64:allmodconfig: In file included from drivers/tty/serial/serial_mctrl_gpio.c:21:0: include/uapi/asm-generic/termios.h:22:8: error: redefinition of 'struct termio' ./arch/sparc/include/uapi/asm/termbits.h:16:8: note: originally defined here make[3]: *** [drivers/tty/serial/serial_mctrl_gpio.o] Error 1 Reported-by: Guenter Roeck Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index bf9560f..a3035f9 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include "serial_mctrl_gpio.h" -- cgit v0.10.2 From 9a37110d20c95d1ebf6c04881177fe8f62831db2 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 14:31:39 -0400 Subject: locking: Add WARN_ON_ONCE lock assertion An interface may need to assert a lock invariant and not flood the system logs; add a lockdep helper macro equivalent to lockdep_assert_held() which only WARNs once. Signed-off-by: Peter Hurley Acked-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 008388f9..64c7425 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -362,6 +362,10 @@ extern void lockdep_trace_alloc(gfp_t mask); WARN_ON(debug_locks && !lockdep_is_held(l)); \ } while (0) +#define lockdep_assert_held_once(l) do { \ + WARN_ON_ONCE(debug_locks && !lockdep_is_held(l)); \ + } while (0) + #define lockdep_recursing(tsk) ((tsk)->lockdep_recursion) #else /* !CONFIG_LOCKDEP */ @@ -412,6 +416,7 @@ struct lock_class_key { }; #define lockdep_depth(tsk) (0) #define lockdep_assert_held(l) do { (void)(l); } while (0) +#define lockdep_assert_held_once(l) do { (void)(l); } while (0) #define lockdep_recursing(tsk) (0) -- cgit v0.10.2 From 4d90bb147ef6b91f529a21b498ff2b5fdc6785b4 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 15:06:23 -0400 Subject: serial: core: Document and assert lock requirements for irq helpers The serial core provides two helper functions, uart_handle_dcd_change() and uart_handle_cts_change(), for UART drivers to use at interrupt time. The serial core expects the UART driver to hold the uart port lock when calling these helpers to prevent state corruption. If lockdep enabled, trigger a warning if the uart port lock is not held when calling these helper functions. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 5a78f69..f764de3 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2743,6 +2743,8 @@ EXPORT_SYMBOL(uart_match_port); * uart_handle_dcd_change - handle a change of carrier detect state * @uport: uart_port structure for the open port * @status: new carrier detect status, nonzero if active + * + * Caller must hold uport->lock */ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) { @@ -2750,6 +2752,8 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) struct tty_struct *tty = port->tty; struct tty_ldisc *ld; + lockdep_assert_held_once(&uport->lock); + if (tty) { ld = tty_ldisc_ref(tty); if (ld) { @@ -2774,12 +2778,16 @@ EXPORT_SYMBOL_GPL(uart_handle_dcd_change); * uart_handle_cts_change - handle a change of clear-to-send state * @uport: uart_port structure for the open port * @status: new clear to send status, nonzero if active + * + * Caller must hold uport->lock */ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) { struct tty_port *port = &uport->state->port; struct tty_struct *tty = port->tty; + lockdep_assert_held_once(&uport->lock); + uport->icount.cts++; if (tty_port_cts_enabled(port)) { -- cgit v0.10.2 From 299245a145b2ad4cfb4c5432eb1264299f55e7e0 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 15:06:24 -0400 Subject: serial: core: Privatize modem status enable flags The serial core uses the tty port flags, ASYNC_CTS_FLOW and ASYNC_CD_CHECK, to track whether CTS and DCD changes should be ignored or handled. However, the tty port flags are not safe for atomic bit operations and no lock provides serialized updates. Introduce the struct uart_port status field to track CTS and DCD enable states, and serialize access with uart port lock. Substitute uart_cts_enabled() helper for tty_port_cts_enabled(). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index b5c3292..10c2933 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -408,7 +408,7 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) ctrl &= ~(AUART_CTRL2_RTSEN | AUART_CTRL2_RTS); if (mctrl & TIOCM_RTS) { - if (tty_port_cts_enabled(&u->state->port)) + if (uart_cts_enabled(u)) ctrl |= AUART_CTRL2_RTSEN; else ctrl |= AUART_CTRL2_RTS; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index f764de3..dd21ed9 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -59,6 +59,11 @@ static void uart_change_pm(struct uart_state *state, static void uart_port_shutdown(struct tty_port *port); +static int uart_dcd_enabled(struct uart_port *uport) +{ + return uport->status & UPSTAT_DCD_ENABLE; +} + /* * This routine is used by the interrupt handler to schedule processing in * the software interrupt portion of the driver. @@ -130,7 +135,6 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, int init_hw) { struct uart_port *uport = state->uart_port; - struct tty_port *port = &state->port; unsigned long page; int retval = 0; @@ -176,12 +180,12 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); } - if (tty_port_cts_enabled(port)) { - spin_lock_irq(&uport->lock); + spin_lock_irq(&uport->lock); + if (uart_cts_enabled(uport)) { if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) tty->hw_stopped = 1; - spin_unlock_irq(&uport->lock); } + spin_unlock_irq(&uport->lock); } /* @@ -435,7 +439,6 @@ EXPORT_SYMBOL(uart_get_divisor); static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, struct ktermios *old_termios) { - struct tty_port *port = &state->port; struct uart_port *uport = state->uart_port; struct ktermios *termios; @@ -450,17 +453,19 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, uport->ops->set_termios(uport, termios, old_termios); /* - * Set flags based on termios cflag + * Set modem status enables based on termios cflag */ + spin_lock_irq(&uport->lock); if (termios->c_cflag & CRTSCTS) - set_bit(ASYNCB_CTS_FLOW, &port->flags); + uport->status |= UPSTAT_CTS_ENABLE; else - clear_bit(ASYNCB_CTS_FLOW, &port->flags); + uport->status &= ~UPSTAT_CTS_ENABLE; if (termios->c_cflag & CLOCAL) - clear_bit(ASYNCB_CHECK_CD, &port->flags); + uport->status &= ~UPSTAT_DCD_ENABLE; else - set_bit(ASYNCB_CHECK_CD, &port->flags); + uport->status |= UPSTAT_DCD_ENABLE; + spin_unlock_irq(&uport->lock); } static inline int __uart_put_char(struct uart_port *port, @@ -2765,7 +2770,7 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) uport->icount.dcd++; - if (port->flags & ASYNC_CHECK_CD) { + if (uart_dcd_enabled(uport)) { if (status) wake_up_interruptible(&port->open_wait); else if (tty) @@ -2790,7 +2795,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) uport->icount.cts++; - if (tty_port_cts_enabled(port)) { + if (uart_cts_enabled(uport)) { if (tty->hw_stopped) { if (status) { tty->hw_stopped = 0; diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 3bd7d55..452c9cc 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -112,6 +112,7 @@ struct uart_icount { }; typedef unsigned int __bitwise__ upf_t; +typedef unsigned int __bitwise__ upstat_t; struct uart_port { spinlock_t lock; /* port lock */ @@ -190,6 +191,12 @@ struct uart_port { #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff)) #define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY)) + /* status must be updated while holding port lock */ + upstat_t status; + +#define UPSTAT_CTS_ENABLE ((__force upstat_t) (1 << 0)) +#define UPSTAT_DCD_ENABLE ((__force upstat_t) (1 << 1)) + unsigned int mctrl; /* current modem ctrl settings */ unsigned int timeout; /* character-based timeout */ unsigned int type; /* port type */ @@ -355,6 +362,11 @@ static inline int uart_tx_stopped(struct uart_port *port) return 0; } +static inline bool uart_cts_enabled(struct uart_port *uport) +{ + return uport->status & UPSTAT_CTS_ENABLE; +} + /* * The following are helper functions for the low level drivers. */ -- cgit v0.10.2 From 317c1360200059a7a8a832294a58409c73b784bf Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 15:06:25 -0400 Subject: isdn: i4l: Remove ASYNC_CTS_FLOW ISDN4Linux always enables CTS flow control and does not use the tty_port_cts_enabled() helper function; remove ASYNC_CTS_FLOW state enable/disable. cc: Karsten Keil cc: Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 3c5f249..bc91261 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1043,11 +1043,6 @@ isdn_tty_change_speed(modem_info *info) if (!(cflag & PARODD)) cval |= UART_LCR_EPAR; - /* CTS flow control flag and modem status interrupts */ - if (cflag & CRTSCTS) { - port->flags |= ASYNC_CTS_FLOW; - } else - port->flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) port->flags &= ~ASYNC_CHECK_CD; else { -- cgit v0.10.2 From d01f4d181c92877ecc678adce248a30cb7077ff1 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 15:06:26 -0400 Subject: serial: core: Privatize tty->hw_stopped tty->hw_stopped is not used by the tty core and is thread-unsafe; hw_stopped is a member of a bitfield whose fields are updated non-atomically and no lock is suitable for serializing updates. Replace serial core usage of tty->hw_stopped with uport->hw_stopped. Use int storage which works around Alpha EV4/5 non-atomic byte storage, since uart_port uses different locks to protect certain fields within the structure. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index dec0fd7..fc9fbf3 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -108,22 +108,22 @@ static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) static irqreturn_t bfin_serial_mctrl_cts_int(int irq, void *dev_id) { struct bfin_serial_port *uart = dev_id; - unsigned int status = bfin_serial_get_mctrl(&uart->port); + struct uart_port *uport = &uart->port; + unsigned int status = bfin_serial_get_mctrl(uport); #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - struct tty_struct *tty = uart->port.state->port.tty; UART_CLEAR_SCTS(uart); - if (tty->hw_stopped) { + if (uport->hw_stopped) { if (status) { - tty->hw_stopped = 0; - uart_write_wakeup(&uart->port); + uport->hw_stopped = 0; + uart_write_wakeup(uport); } } else { if (!status) - tty->hw_stopped = 1; + uport->hw_stopped = 1; } #endif - uart_handle_cts_change(&uart->port, status & TIOCM_CTS); + uart_handle_cts_change(uport, status & TIOCM_CTS); return IRQ_HANDLED; } diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index dd21ed9..7d51e26 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -95,7 +95,7 @@ static void __uart_start(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct uart_port *port = state->uart_port; - if (!tty->stopped && !tty->hw_stopped) + if (!uart_tx_stopped(port)) port->ops->start_tx(port); } @@ -181,10 +181,11 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, } spin_lock_irq(&uport->lock); - if (uart_cts_enabled(uport)) { - if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) - tty->hw_stopped = 1; - } + if (uart_cts_enabled(uport) && + !(uport->ops->get_mctrl(uport) & TIOCM_CTS)) + uport->hw_stopped = 1; + else + uport->hw_stopped = 0; spin_unlock_irq(&uport->lock); } @@ -949,7 +950,7 @@ static int uart_get_lsr_info(struct tty_struct *tty, */ if (uport->x_char || ((uart_circ_chars_pending(&state->xmit) > 0) && - !tty->stopped && !tty->hw_stopped)) + !uart_tx_stopped(uport))) result &= ~TIOCSER_TEMT; return put_user(result, value); @@ -1295,7 +1296,7 @@ static void uart_set_termios(struct tty_struct *tty, /* * If the port is doing h/w assisted flow control, do nothing. - * We assume that tty->hw_stopped has never been set. + * We assume that port->hw_stopped has never been set. */ if (uport->flags & UPF_HARD_FLOW) return; @@ -1303,7 +1304,7 @@ static void uart_set_termios(struct tty_struct *tty, /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { spin_lock_irqsave(&uport->lock, flags); - tty->hw_stopped = 0; + uport->hw_stopped = 0; __uart_start(tty); spin_unlock_irqrestore(&uport->lock, flags); } @@ -1311,7 +1312,7 @@ static void uart_set_termios(struct tty_struct *tty, else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { spin_lock_irqsave(&uport->lock, flags); if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) { - tty->hw_stopped = 1; + uport->hw_stopped = 1; uport->ops->stop_tx(uport); } spin_unlock_irqrestore(&uport->lock, flags); @@ -2788,23 +2789,20 @@ EXPORT_SYMBOL_GPL(uart_handle_dcd_change); */ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) { - struct tty_port *port = &uport->state->port; - struct tty_struct *tty = port->tty; - lockdep_assert_held_once(&uport->lock); uport->icount.cts++; if (uart_cts_enabled(uport)) { - if (tty->hw_stopped) { + if (uport->hw_stopped) { if (status) { - tty->hw_stopped = 0; + uport->hw_stopped = 0; uport->ops->start_tx(uport); uart_write_wakeup(uport); } } else { if (!status) { - tty->hw_stopped = 1; + uport->hw_stopped = 1; uport->ops->stop_tx(uport); } } diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 452c9cc..204c452 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -197,6 +197,7 @@ struct uart_port { #define UPSTAT_CTS_ENABLE ((__force upstat_t) (1 << 0)) #define UPSTAT_DCD_ENABLE ((__force upstat_t) (1 << 1)) + int hw_stopped; /* sw-assisted CTS flow state */ unsigned int mctrl; /* current modem ctrl settings */ unsigned int timeout; /* character-based timeout */ unsigned int type; /* port type */ @@ -357,7 +358,7 @@ int uart_resume_port(struct uart_driver *reg, struct uart_port *port); static inline int uart_tx_stopped(struct uart_port *port) { struct tty_struct *tty = port->state->port.tty; - if(tty->stopped || tty->hw_stopped) + if (tty->stopped || port->hw_stopped) return 1; return 0; } -- cgit v0.10.2 From d95e3caea2d1424dfdec30b46bd94087da94f928 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 15:06:27 -0400 Subject: usb: serial: Remove unused tty->hw_stopped The tty core does not test tty->hw_stopped; remove from drivers which don't test it themselves. Acked-by: Johan Hovold Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 8a23c53..12b0e67 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -834,7 +834,6 @@ static void digi_set_termios(struct tty_struct *tty, arg |= DIGI_OUTPUT_FLOW_CONTROL_CTS; } else { arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS; - tty->hw_stopped = 0; } buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; @@ -1500,15 +1499,11 @@ static int digi_read_oob_callback(struct urb *urb) if (val & DIGI_READ_INPUT_SIGNALS_CTS) { priv->dp_modem_signals |= TIOCM_CTS; /* port must be open to use tty struct */ - if (rts) { - tty->hw_stopped = 0; + if (rts) tty_port_tty_wakeup(&port->port); - } } else { priv->dp_modem_signals &= ~TIOCM_CTS; /* port must be open to use tty struct */ - if (rts) - tty->hw_stopped = 1; } if (val & DIGI_READ_INPUT_SIGNALS_DSR) priv->dp_modem_signals |= TIOCM_DSR; diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index c0a42e9..ddbb8fe 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1456,12 +1456,8 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) tty = tty_port_tty_get(&edge_port->port->port); /* handle CTS flow control */ if (tty && C_CRTSCTS(tty)) { - if (msr & EDGEPORT_MSR_CTS) { - tty->hw_stopped = 0; + if (msr & EDGEPORT_MSR_CTS) tty_wakeup(tty); - } else { - tty->hw_stopped = 1; - } } tty_kref_put(tty); } @@ -2177,7 +2173,6 @@ static void change_port_settings(struct tty_struct *tty, dev_dbg(dev, "%s - RTS/CTS is enabled\n", __func__); } else { dev_dbg(dev, "%s - RTS/CTS is disabled\n", __func__); - tty->hw_stopped = 0; restart_read(edge_port); } diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 3dd3ff8..e9da41d 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -773,7 +773,6 @@ static void ti_set_termios(struct tty_struct *tty, config->wFlags |= TI_UART_ENABLE_RTS_IN; config->wFlags |= TI_UART_ENABLE_CTS_OUT; } else { - tty->hw_stopped = 0; ti_restart_read(tport, tty); } @@ -1291,12 +1290,8 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) /* handle CTS flow control */ tty = tty_port_tty_get(&tport->tp_port->port); if (tty && C_CRTSCTS(tty)) { - if (msr & TI_MSR_CTS) { - tty->hw_stopped = 0; + if (msr & TI_MSR_CTS) tty_wakeup(tty); - } else { - tty->hw_stopped = 1; - } } tty_kref_put(tty); } -- cgit v0.10.2 From 8620d3e5382a3c172a7885fa67a70955ca930425 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 15:06:28 -0400 Subject: serial: bfin-uart: Fix auto CTS Commit 64851636d568ae9f167cd5d1dcdbfe17e6eef73c, serial: bfin-uart: Remove ASYNC_CTS_FLOW flag for hardware automatic CTS, open-codes uart_handle_cts_change() when CONFIG_SERIAL_BFIN_HARD_CTSRTS to skip start and stop tx. But the CTS interrupt handler _still_ calls uart_handle_cts_change(); only call uart_handle_cts_change() if !CONFIG_SERIAL_BFIN_HARD_CTSRTS. cc: Sonic Zhang Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index fc9fbf3..7da9911 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -122,8 +122,9 @@ static irqreturn_t bfin_serial_mctrl_cts_int(int irq, void *dev_id) if (!status) uport->hw_stopped = 1; } -#endif +#else uart_handle_cts_change(uport, status & TIOCM_CTS); +#endif return IRQ_HANDLED; } -- cgit v0.10.2 From 938f7e13b55a76ad98964509f6d13bbcf852e617 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 15:06:29 -0400 Subject: serial: core: Use spin_lock_irq() in uart_set_termios() uart_set_termios() is called with interrupts enabled; no need to save and restore the interrupt state when taking the uart port lock. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 7d51e26..df3a8c7 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1249,7 +1249,6 @@ static void uart_set_termios(struct tty_struct *tty, { struct uart_state *state = tty->driver_data; struct uart_port *uport = state->uart_port; - unsigned long flags; unsigned int cflag = tty->termios.c_cflag; unsigned int iflag_mask = IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK; bool sw_changed = false; @@ -1303,19 +1302,19 @@ static void uart_set_termios(struct tty_struct *tty, /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { - spin_lock_irqsave(&uport->lock, flags); + spin_lock_irq(&uport->lock); uport->hw_stopped = 0; __uart_start(tty); - spin_unlock_irqrestore(&uport->lock, flags); + spin_unlock_irq(&uport->lock); } /* Handle turning on CRTSCTS */ else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { - spin_lock_irqsave(&uport->lock, flags); + spin_lock_irq(&uport->lock); if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) { uport->hw_stopped = 1; uport->ops->stop_tx(uport); } - spin_unlock_irqrestore(&uport->lock, flags); + spin_unlock_irq(&uport->lock); } } -- cgit v0.10.2 From d7a855bd6ab25d10d5e3b6aeb53d9c57fa17b808 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 15:06:30 -0400 Subject: tty: Convert tty_struct bitfield to ints The stopped, hw_stopped, flow_stopped and packet bits are smp-unsafe and interrupt-unsafe. For example, CPU 0 | CPU 1 | tty->flow_stopped = 1 | tty->hw_stopped = 0 One of these updates will be corrupted, as the bitwise operation on the bitfield is non-atomic. Ensure each flag has a separate memory location, so concurrent updates do not corrupt orthogonal states. Because DEC Alpha EV4 and EV5 cpus (from 1995) perform RMW on smaller-than-machine-word storage, "separate memory location" must be int instead of byte. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/tty.h b/include/linux/tty.h index 8413294..4cfd4a8 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -261,7 +261,10 @@ struct tty_struct { unsigned long flags; int count; struct winsize winsize; /* winsize_mutex */ - unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1; + int stopped; + int flow_stopped; + int hw_stopped; + int packet; unsigned char ctrl_status; /* ctrl_lock */ unsigned int receive_room; /* Bytes free for queue */ int flow_change; -- cgit v0.10.2 From f9e053dcfc02b0ad29daec8524fb1afe09774976 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 15:06:31 -0400 Subject: tty: Serialize tty flow control changes with flow_lock Without serialization, the flow control state can become inverted wrt. the actual hardware state. For example, CPU 0 | CPU 1 stop_tty() | lock ctrl_lock | tty->stopped = 1 | unlock ctrl_lock | | start_tty() | lock ctrl_lock | tty->stopped = 0 | unlock ctrl_lock | driver->start() driver->stop() | In this case, the flow control state now indicates the tty has been started, but the actual hardware state has actually been stopped. Introduce tty->flow_lock spinlock to serialize tty flow control changes. Split out unlocked __start_tty()/__stop_tty() flavors for use by ioctl(TCXONC) in follow-on patch. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index d4eb2a8..b8ddfef 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -919,18 +919,18 @@ void no_tty(void) * but not always. * * Locking: - * Uses the tty control lock internally + * ctrl_lock + * flow_lock */ -void stop_tty(struct tty_struct *tty) +void __stop_tty(struct tty_struct *tty) { unsigned long flags; - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->stopped) { - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + + if (tty->stopped) return; - } tty->stopped = 1; + spin_lock_irqsave(&tty->ctrl_lock, flags); if (tty->link && tty->link->packet) { tty->ctrl_status &= ~TIOCPKT_START; tty->ctrl_status |= TIOCPKT_STOP; @@ -941,6 +941,14 @@ void stop_tty(struct tty_struct *tty) (tty->ops->stop)(tty); } +void stop_tty(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&tty->flow_lock, flags); + __stop_tty(tty); + spin_unlock_irqrestore(&tty->flow_lock, flags); +} EXPORT_SYMBOL(stop_tty); /** @@ -954,17 +962,17 @@ EXPORT_SYMBOL(stop_tty); * * Locking: * ctrl_lock + * flow_lock */ -void start_tty(struct tty_struct *tty) +void __start_tty(struct tty_struct *tty) { unsigned long flags; - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (!tty->stopped || tty->flow_stopped) { - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + + if (!tty->stopped || tty->flow_stopped) return; - } tty->stopped = 0; + spin_lock_irqsave(&tty->ctrl_lock, flags); if (tty->link && tty->link->packet) { tty->ctrl_status &= ~TIOCPKT_STOP; tty->ctrl_status |= TIOCPKT_START; @@ -977,6 +985,14 @@ void start_tty(struct tty_struct *tty) tty_wakeup(tty); } +void start_tty(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&tty->flow_lock, flags); + __start_tty(tty); + spin_unlock_irqrestore(&tty->flow_lock, flags); +} EXPORT_SYMBOL(start_tty); /* We limit tty time update visibility to every 8 seconds or so. */ @@ -3019,6 +3035,7 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx) INIT_WORK(&tty->hangup_work, do_tty_hangup); mutex_init(&tty->atomic_write_lock); spin_lock_init(&tty->ctrl_lock); + spin_lock_init(&tty->flow_lock); INIT_LIST_HEAD(&tty->tty_files); INIT_WORK(&tty->SAK_work, do_SAK_work); diff --git a/include/linux/tty.h b/include/linux/tty.h index 4cfd4a8..fd4148d 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -252,6 +252,7 @@ struct tty_struct { struct rw_semaphore termios_rwsem; struct mutex winsize_mutex; spinlock_t ctrl_lock; + spinlock_t flow_lock; /* Termios values are protected by the termios rwsem */ struct ktermios termios, termios_locked; struct termiox *termiox; /* May be NULL for unsupported */ @@ -261,7 +262,7 @@ struct tty_struct { unsigned long flags; int count; struct winsize winsize; /* winsize_mutex */ - int stopped; + int stopped; /* flow_lock */ int flow_stopped; int hw_stopped; int packet; @@ -400,7 +401,9 @@ extern int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, extern char *tty_name(struct tty_struct *tty, char *buf); extern void tty_wait_until_sent(struct tty_struct *tty, long timeout); extern int tty_check_change(struct tty_struct *tty); +extern void __stop_tty(struct tty_struct *tty); extern void stop_tty(struct tty_struct *tty); +extern void __start_tty(struct tty_struct *tty); extern void start_tty(struct tty_struct *tty); extern int tty_register_driver(struct tty_driver *driver); extern int tty_unregister_driver(struct tty_driver *driver); diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index e48c608..92e337c 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -152,6 +152,8 @@ * This routine notifies the tty driver that it should stop * outputting characters to the tty device. * + * Called with ->flow_lock held. Serialized with start() method. + * * Optional: * * Note: Call stop_tty not this method. @@ -161,6 +163,8 @@ * This routine notifies the tty driver that it resume sending * characters to the tty device. * + * Called with ->flow_lock held. Serialized with stop() method. + * * Optional: * * Note: Call start_tty not this method. -- cgit v0.10.2 From 01adc80706f80a583948db6768c5571204cd5f99 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 15:06:32 -0400 Subject: tty: Move packet mode flow control notifications to pty driver When a master pty is set to packet mode, flow control changes to the slave pty cause notifications to the master pty via reads and polls. However, these tests are occurring for all ttys, not just ptys. Implement flow control packet mode notifications in the pty driver. Only the slave side implements the flow control handlers since packet mode is asymmetric; the master pty receives notifications for slave-side changes, but not vice versa. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 9bbdb1d..7c4447a 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -24,6 +24,7 @@ #include #include #include +#include #ifdef CONFIG_UNIX98_PTYS @@ -313,6 +314,42 @@ done: } /** + * pty_start - start() handler + * pty_stop - stop() handler + * @tty: tty being flow-controlled + * + * Propagates the TIOCPKT status to the master pty. + * + * NB: only the master pty can be in packet mode so only the slave + * needs start()/stop() handlers + */ +static void pty_start(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&tty->ctrl_lock, flags); + if (tty->link && tty->link->packet) { + tty->ctrl_status &= ~TIOCPKT_STOP; + tty->ctrl_status |= TIOCPKT_START; + wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); + } + spin_unlock_irqrestore(&tty->ctrl_lock, flags); +} + +static void pty_stop(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&tty->ctrl_lock, flags); + if (tty->link && tty->link->packet) { + tty->ctrl_status &= ~TIOCPKT_START; + tty->ctrl_status |= TIOCPKT_STOP; + wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); + } + spin_unlock_irqrestore(&tty->ctrl_lock, flags); +} + +/** * pty_common_install - set up the pty pair * @driver: the pty driver * @tty: the tty being instantiated @@ -471,6 +508,8 @@ static const struct tty_operations slave_pty_ops_bsd = { .set_termios = pty_set_termios, .cleanup = pty_cleanup, .resize = pty_resize, + .start = pty_start, + .stop = pty_stop, .remove = pty_remove }; @@ -646,6 +685,8 @@ static const struct tty_operations pty_unix98_ops = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, + .start = pty_start, + .stop = pty_stop, .shutdown = pty_unix98_shutdown, .cleanup = pty_cleanup, }; diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index b8ddfef..c249ff1 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -908,8 +908,7 @@ void no_tty(void) * stop_tty - propagate flow control * @tty: tty to stop * - * Perform flow control to the driver. For PTY/TTY pairs we - * must also propagate the TIOCKPKT status. May be called + * Perform flow control to the driver. May be called * on an already stopped device and will not re-call the driver * method. * @@ -919,24 +918,14 @@ void no_tty(void) * but not always. * * Locking: - * ctrl_lock * flow_lock */ void __stop_tty(struct tty_struct *tty) { - unsigned long flags; - if (tty->stopped) return; tty->stopped = 1; - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->link && tty->link->packet) { - tty->ctrl_status &= ~TIOCPKT_START; - tty->ctrl_status |= TIOCPKT_STOP; - wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); - } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); if (tty->ops->stop) (tty->ops->stop)(tty); } @@ -955,33 +944,21 @@ EXPORT_SYMBOL(stop_tty); * start_tty - propagate flow control * @tty: tty to start * - * Start a tty that has been stopped if at all possible. Perform - * any necessary wakeups and propagate the TIOCPKT status. If this - * is the tty was previous stopped and is being started then the - * driver start method is invoked and the line discipline woken. + * Start a tty that has been stopped if at all possible. If this + * tty was previous stopped and is now being started, the driver + * start method is invoked and the line discipline woken. * * Locking: - * ctrl_lock * flow_lock */ void __start_tty(struct tty_struct *tty) { - unsigned long flags; - if (!tty->stopped || tty->flow_stopped) return; tty->stopped = 0; - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->link && tty->link->packet) { - tty->ctrl_status &= ~TIOCPKT_STOP; - tty->ctrl_status |= TIOCPKT_START; - wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); - } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); if (tty->ops->start) (tty->ops->start)(tty); - /* If we have a running line discipline it may need kicking */ tty_wakeup(tty); } -- cgit v0.10.2 From c545b66c6922b002b5fe224a6eaec58c913650b5 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 15:06:33 -0400 Subject: tty: Serialize tcflow() with other tty flow control changes Use newly-introduced tty->flow_lock to serialize updates to tty->flow_stopped (via tcflow()) and with concurrent tty flow control changes from other sources. Merge the storage for ->stopped and ->flow_stopped, now that both flags are serialized by ->flow_lock. The padding bits are necessary to force the compiler to allocate the type specified; otherwise, gcc will ignore the type specifier and allocate the minimum number of bytes necessary to store the bitfield. In turn, this would allow Alpha EV4 and EV5 cpus to corrupt adjacent byte storage because those cpus use RMW to store byte and short data. gcc versions < 4.7.2 will also corrupt storage adjacent to bitfields smaller than unsigned long on ia64, ppc64, hppa64 and sparc64, thus requiring more than unsigned int storage (which would otherwise be sufficient to workaround the Alpha non-atomic byte/short storage problem). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index cd1285c..dcf5c0a 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -1177,16 +1177,20 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, return retval; switch (arg) { case TCOOFF: + spin_lock_irq(&tty->flow_lock); if (!tty->flow_stopped) { tty->flow_stopped = 1; - stop_tty(tty); + __stop_tty(tty); } + spin_unlock_irq(&tty->flow_lock); break; case TCOON: + spin_lock_irq(&tty->flow_lock); if (tty->flow_stopped) { tty->flow_stopped = 0; - start_tty(tty); + __start_tty(tty); } + spin_unlock_irq(&tty->flow_lock); break; case TCIOFF: if (STOP_CHAR(tty) != __DISABLED_CHAR) diff --git a/include/linux/tty.h b/include/linux/tty.h index fd4148d..d80b536 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -262,8 +262,9 @@ struct tty_struct { unsigned long flags; int count; struct winsize winsize; /* winsize_mutex */ - int stopped; /* flow_lock */ - int flow_stopped; + unsigned long stopped:1, /* flow_lock */ + flow_stopped:1, + unused:62; int hw_stopped; int packet; unsigned char ctrl_status; /* ctrl_lock */ -- cgit v0.10.2 From 136d5258b2bc4ffae99cb69874a76624c26fbfad Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 15:06:34 -0400 Subject: tty: Move and rename send_prio_char() as tty_send_xchar() Relocate the file-scope function, send_prio_char(), as a global helper tty_send_xchar(). Remove the global declarations for tty_write_lock()/tty_write_unlock(), as these are file-scope only now. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index c249ff1..2f6f9b5 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1023,14 +1023,14 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, return i; } -void tty_write_unlock(struct tty_struct *tty) +static void tty_write_unlock(struct tty_struct *tty) __releases(&tty->atomic_write_lock) { mutex_unlock(&tty->atomic_write_lock); wake_up_interruptible_poll(&tty->write_wait, POLLOUT); } -int tty_write_lock(struct tty_struct *tty, int ndelay) +static int tty_write_lock(struct tty_struct *tty, int ndelay) __acquires(&tty->atomic_write_lock) { if (!mutex_trylock(&tty->atomic_write_lock)) { @@ -1217,6 +1217,35 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf, return tty_write(file, buf, count, ppos); } +/** + * tty_send_xchar - send priority character + * + * Send a high priority character to the tty even if stopped + * + * Locking: none for xchar method, write ordering for write method. + */ + +int tty_send_xchar(struct tty_struct *tty, char ch) +{ + int was_stopped = tty->stopped; + + if (tty->ops->send_xchar) { + tty->ops->send_xchar(tty, ch); + return 0; + } + + if (tty_write_lock(tty, 0) < 0) + return -ERESTARTSYS; + + if (was_stopped) + start_tty(tty); + tty->ops->write(tty, &ch, 1); + if (was_stopped) + stop_tty(tty); + tty_write_unlock(tty); + return 0; +} + static char ptychar[] = "pqrstuvwxyzabcde"; /** diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index dcf5c0a..ad9120d 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -912,35 +912,6 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) #endif /** - * send_prio_char - send priority character - * - * Send a high priority character to the tty even if stopped - * - * Locking: none for xchar method, write ordering for write method. - */ - -static int send_prio_char(struct tty_struct *tty, char ch) -{ - int was_stopped = tty->stopped; - - if (tty->ops->send_xchar) { - tty->ops->send_xchar(tty, ch); - return 0; - } - - if (tty_write_lock(tty, 0) < 0) - return -ERESTARTSYS; - - if (was_stopped) - start_tty(tty); - tty->ops->write(tty, &ch, 1); - if (was_stopped) - stop_tty(tty); - tty_write_unlock(tty); - return 0; -} - -/** * tty_change_softcar - carrier change ioctl helper * @tty: tty to update * @arg: enable/disable CLOCAL @@ -1194,11 +1165,11 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, break; case TCIOFF: if (STOP_CHAR(tty) != __DISABLED_CHAR) - return send_prio_char(tty, STOP_CHAR(tty)); + return tty_send_xchar(tty, STOP_CHAR(tty)); break; case TCION: if (START_CHAR(tty) != __DISABLED_CHAR) - return send_prio_char(tty, START_CHAR(tty)); + return tty_send_xchar(tty, START_CHAR(tty)); break; default: return -EINVAL; diff --git a/include/linux/tty.h b/include/linux/tty.h index d80b536..4c6b9fd 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -418,6 +418,7 @@ extern void tty_unregister_device(struct tty_driver *driver, unsigned index); extern int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp, int buflen); extern void tty_write_message(struct tty_struct *tty, char *msg); +extern int tty_send_xchar(struct tty_struct *tty, char ch); extern int tty_put_char(struct tty_struct *tty, unsigned char c); extern int tty_chars_in_buffer(struct tty_struct *tty); extern int tty_write_room(struct tty_struct *tty); @@ -502,8 +503,6 @@ extern struct tty_struct *tty_pair_get_pty(struct tty_struct *tty); extern struct mutex tty_mutex; extern spinlock_t tty_files_lock; -extern void tty_write_unlock(struct tty_struct *tty); -extern int tty_write_lock(struct tty_struct *tty, int ndelay); #define tty_is_writelocked(tty) (mutex_is_locked(&tty->atomic_write_lock)) extern void tty_port_init(struct tty_port *port); -- cgit v0.10.2 From c274f6ef1c6665632767d32e4ab912aad839ce27 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 15:06:35 -0400 Subject: tty: Hold termios_rwsem for tcflow(TCIxxx) While transmitting a START/STOP char for tcflow(TCION/TCIOFF), prevent a termios change. Otherwise, a garbage in-band flow control char may be sent, if the termios change overlaps the transmission setup. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index ad9120d..62380cc 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -1164,17 +1164,21 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, spin_unlock_irq(&tty->flow_lock); break; case TCIOFF: + down_read(&tty->termios_rwsem); if (STOP_CHAR(tty) != __DISABLED_CHAR) - return tty_send_xchar(tty, STOP_CHAR(tty)); + retval = tty_send_xchar(tty, STOP_CHAR(tty)); + up_read(&tty->termios_rwsem); break; case TCION: + down_read(&tty->termios_rwsem); if (START_CHAR(tty) != __DISABLED_CHAR) - return tty_send_xchar(tty, START_CHAR(tty)); + retval = tty_send_xchar(tty, START_CHAR(tty)); + up_read(&tty->termios_rwsem); break; default: return -EINVAL; } - return 0; + return retval; case TCFLSH: retval = tty_check_change(tty); if (retval) -- cgit v0.10.2 From 99416322dd16b810ba74098cc50ef2a844091d35 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 10 Sep 2014 15:06:36 -0400 Subject: tty: Workaround Alpha non-atomic byte storage in tty_struct The Alpha EV4/EV5 cpus can corrupt adjacent byte and short data because those cpus use RMW to store byte and short data. Thus, concurrent adjacent byte stores could become corrupted, if serialized by a different lock. tty_struct uses different locks to protect certain fields within the structure, and thus is vulnerable to byte stores which are not atomic. Merge the ->ctrl_status byte and packet mode bit, both protected by the ->ctrl_lock, into an unsigned long. The padding bits are necessary to force the compiler to allocate the type specified; otherwise, gcc will ignore the type specifier and allocate the minimum number of bytes required to store the bitfield. In turn, this would allow Alpha EV4/EV5 cpus to corrupt adjacent byte or short storage (because those cpus use RMW to store byte and short data). gcc versions < 4.7.2 will also corrupt storage adjacent to bitfields smaller than unsigned long on ia64, ppc64, hppa64, and sparc64, thus requiring more than unsigned int storage (which would otherwise be sufficient to fix the Alpha non-atomic storage problem). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/tty.h b/include/linux/tty.h index 4c6b9fd..546e945 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -266,8 +266,9 @@ struct tty_struct { flow_stopped:1, unused:62; int hw_stopped; - int packet; - unsigned char ctrl_status; /* ctrl_lock */ + unsigned long ctrl_status:8, /* ctrl_lock */ + packet:1, + unused_ctrl:55; unsigned int receive_room; /* Bytes free for queue */ int flow_change; -- cgit v0.10.2 From cc952e7017fa2e8871ee6a94f2c606ff5911f61e Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 24 Sep 2014 06:26:21 -0400 Subject: tty: Fix width of unsigned long bitfield padding Commit c545b66c6922b002b5fe224a6eaec58c913650b5, 'tty: Serialize tcflow() with other tty flow control changes' and commit 99416322dd16b810ba74098cc50ef2a844091d35, 'tty: Workaround Alpha non-atomic byte storage in tty_struct' work around compiler bugs and non-atomic storage on multiple arches by padding bitfields out to the declared type which is unsigned long. However, the width varies by arch. Pad bitfields to actual width of unsigned long (which is BITS_PER_LONG). Reported-by: Fengguang Wu Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/tty.h b/include/linux/tty.h index 546e945..5171ef8 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -264,11 +264,11 @@ struct tty_struct { struct winsize winsize; /* winsize_mutex */ unsigned long stopped:1, /* flow_lock */ flow_stopped:1, - unused:62; + unused:BITS_PER_LONG - 2; int hw_stopped; unsigned long ctrl_status:8, /* ctrl_lock */ packet:1, - unused_ctrl:55; + unused_ctrl:BITS_PER_LONG - 9; unsigned int receive_room; /* Bytes free for queue */ int flow_change; -- cgit v0.10.2 From 234abab143aef82c0ef1f2de409c0db96b666f3c Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 10 Sep 2014 21:29:56 +0200 Subject: tty: serial: 8250_core: allow to set ->throttle / ->unthrottle callbacks The OMAP UART provides support for HW assisted flow control. What is missing is the support to throttle / unthrottle callbacks which are used by the omap-serial driver at the moment. This patch adds the callbacks. It should be safe to add them since they are only invoked from the serial_core (uart_throttle()) if the feature flags are set. Reviewed-by: Tony Lindgren Tested-by: Tony Lindgren Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index a0c1d64..68c44d9 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1319,6 +1319,16 @@ static void serial8250_start_tx(struct uart_port *port) } } +static void serial8250_throttle(struct uart_port *port) +{ + port->throttle(port); +} + +static void serial8250_unthrottle(struct uart_port *port) +{ + port->unthrottle(port); +} + static void serial8250_stop_rx(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); @@ -2912,6 +2922,8 @@ static struct uart_ops serial8250_pops = { .get_mctrl = serial8250_get_mctrl, .stop_tx = serial8250_stop_tx, .start_tx = serial8250_start_tx, + .throttle = serial8250_throttle, + .unthrottle = serial8250_unthrottle, .stop_rx = serial8250_stop_rx, .enable_ms = serial8250_enable_ms, .break_ctl = serial8250_break_ctl, @@ -3462,6 +3474,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->capabilities = up->capabilities; uart->rs485_config = up->rs485_config; uart->rs485 = up->rs485; + uart->port.throttle = up->port.throttle; + uart->port.unthrottle = up->port.unthrottle; /* Take tx_loadsz from fifosize if it wasn't set separately */ if (uart->port.fifosize && !uart->tx_loadsz) diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 204c452..21c2e05 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -125,6 +125,8 @@ struct uart_port { struct ktermios *old); int (*startup)(struct uart_port *port); void (*shutdown)(struct uart_port *port); + void (*throttle)(struct uart_port *port); + void (*unthrottle)(struct uart_port *port); int (*handle_irq)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, unsigned int old); -- cgit v0.10.2 From d74d5d1b7288ff9d4439c8c7e0e314cde9743467 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 10 Sep 2014 21:29:57 +0200 Subject: tty: serial: 8250_core: add run time pm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While comparing the OMAP-serial and the 8250 part of this I noticed that the latter does not use run time-pm. Here are the pieces. It is basically a get before first register access and a last_busy + put after last access. This has to be enabled from userland _and_ UART_CAP_RPM is required for this. The runtime PM can usually work transparently in the background however there is one exception to this: After serial8250_tx_chars() completes there still may be unsent bytes in the FIFO (depending on CPU speed vs baud rate + flow control). Even if the TTY-buffer is empty we do not want RPM to disable the device because it won't send the remaining bytes. Instead we leave serial8250_tx_chars() with RPM enabled and wait for the FIFO empty interrupt. Once we enter serial8250_tx_chars() with an empty buffer we know that the FIFO is empty and since we are not going to send anything, we can disable the device. That xchg() is to ensure that serial8250_tx_chars() can be called multiple times and only the first invocation will actually invoke the runtime PM function. So that the last invocation of __stop_tx() will disable runtime pm. NOTE: do not enable RPM on the device unless you know what you do! If the device goes idle, it won't be woken up by incomming RX data _unless_ there is a wakeup irq configured which is usually the RX pin configure for wakeup via the reset module. The RX activity will then wake up the device from idle. However the first character is garbage and lost. The following bytes will be received once the device is up in time. On the beagle board xm (omap3) it takes approx 13ms from the first wakeup byte until the first byte that is received properly if the device was in core-off. v5…v8: - drop RPM from serial8250_set_mctrl() it will be used in restore path which already has RPM active and holds dev->power.lock v4…v5: - add a wrapper around rpm function and introduce UART_CAP_RPM to ensure RPM put is invoked after the TX FIFO is empty. v3…v4: - added runtime to the console code - removed device_may_wakeup() from serial8250_set_sleep() Cc: mika.westerberg@linux.intel.com Reviewed-by: Tony Lindgren Tested-by: Tony Lindgren Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 85bfec5..1bcb4b2 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -72,6 +72,7 @@ struct serial8250_config { #define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */ #define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */ #define UART_CAP_HFIFO (1 << 14) /* UART has a "hidden" FIFO */ +#define UART_CAP_RPM (1 << 15) /* Runtime PM is active while idle */ #define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */ #define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 68c44d9..3cf5c98 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -38,6 +38,7 @@ #include #include #include +#include #ifdef CONFIG_SPARC #include #endif @@ -540,6 +541,53 @@ void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p) } EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos); +static void serial8250_rpm_get(struct uart_8250_port *p) +{ + if (!(p->capabilities & UART_CAP_RPM)) + return; + pm_runtime_get_sync(p->port.dev); +} + +static void serial8250_rpm_put(struct uart_8250_port *p) +{ + if (!(p->capabilities & UART_CAP_RPM)) + return; + pm_runtime_mark_last_busy(p->port.dev); + pm_runtime_put_autosuspend(p->port.dev); +} + +/* + * This two wrapper ensure, that enable_runtime_pm_tx() can be called more than + * once and disable_runtime_pm_tx() will still disable RPM because the fifo is + * empty and the HW can idle again. + */ +static void serial8250_rpm_get_tx(struct uart_8250_port *p) +{ + unsigned char rpm_active; + + if (!(p->capabilities & UART_CAP_RPM)) + return; + + rpm_active = xchg(&p->rpm_tx_active, 1); + if (rpm_active) + return; + pm_runtime_get_sync(p->port.dev); +} + +static void serial8250_rpm_put_tx(struct uart_8250_port *p) +{ + unsigned char rpm_active; + + if (!(p->capabilities & UART_CAP_RPM)) + return; + + rpm_active = xchg(&p->rpm_tx_active, 0); + if (!rpm_active) + return; + pm_runtime_mark_last_busy(p->port.dev); + pm_runtime_put_autosuspend(p->port.dev); +} + /* * IER sleep support. UARTs which have EFRs need the "extended * capability" bit enabled. Note that on XR16C850s, we need to @@ -554,10 +602,11 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) * offset but the UART channel may only write to the corresponding * bit. */ + serial8250_rpm_get(p); if ((p->port.type == PORT_XR17V35X) || (p->port.type == PORT_XR17D15X)) { serial_out(p, UART_EXAR_SLEEP, sleep ? 0xff : 0); - return; + goto out; } if (p->capabilities & UART_CAP_SLEEP) { @@ -573,6 +622,8 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) serial_out(p, UART_LCR, 0); } } +out: + serial8250_rpm_put(p); } #ifdef CONFIG_SERIAL_8250_RSA @@ -1273,6 +1324,7 @@ static inline void __stop_tx(struct uart_8250_port *p) if (p->ier & UART_IER_THRI) { p->ier &= ~UART_IER_THRI; serial_out(p, UART_IER, p->ier); + serial8250_rpm_put_tx(p); } } @@ -1280,6 +1332,7 @@ static void serial8250_stop_tx(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); + serial8250_rpm_get(up); __stop_tx(up); /* @@ -1289,12 +1342,14 @@ static void serial8250_stop_tx(struct uart_port *port) up->acr |= UART_ACR_TXDIS; serial_icr_write(up, UART_ACR, up->acr); } + serial8250_rpm_put(up); } static void serial8250_start_tx(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); + serial8250_rpm_get_tx(up); if (up->dma && !serial8250_tx_dma(up)) { return; } else if (!(up->ier & UART_IER_THRI)) { @@ -1333,9 +1388,13 @@ static void serial8250_stop_rx(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); + serial8250_rpm_get(up); + up->ier &= ~UART_IER_RLSI; up->port.read_status_mask &= ~UART_LSR_DR; serial_port_out(port, UART_IER, up->ier); + + serial8250_rpm_put(up); } static void serial8250_enable_ms(struct uart_port *port) @@ -1347,7 +1406,10 @@ static void serial8250_enable_ms(struct uart_port *port) return; up->ier |= UART_IER_MSI; + + serial8250_rpm_get(up); serial_port_out(port, UART_IER, up->ier); + serial8250_rpm_put(up); } /* @@ -1469,7 +1531,12 @@ void serial8250_tx_chars(struct uart_8250_port *up) DEBUG_INTR("THRE..."); - if (uart_circ_empty(xmit)) + /* + * With RPM enabled, we have to wait once the FIFO is empty before the + * HW can go idle. So we get here once again with empty FIFO and disable + * the interrupt and RPM in __stop_tx() + */ + if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM)) __stop_tx(up); } EXPORT_SYMBOL_GPL(serial8250_tx_chars); @@ -1537,9 +1604,17 @@ EXPORT_SYMBOL_GPL(serial8250_handle_irq); static int serial8250_default_handle_irq(struct uart_port *port) { - unsigned int iir = serial_port_in(port, UART_IIR); + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int iir; + int ret; + + serial8250_rpm_get(up); - return serial8250_handle_irq(port, iir); + iir = serial_port_in(port, UART_IIR); + ret = serial8250_handle_irq(port, iir); + + serial8250_rpm_put(up); + return ret; } /* @@ -1796,11 +1871,15 @@ static unsigned int serial8250_tx_empty(struct uart_port *port) unsigned long flags; unsigned int lsr; + serial8250_rpm_get(up); + spin_lock_irqsave(&port->lock, flags); lsr = serial_port_in(port, UART_LSR); up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); + return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0; } @@ -1810,7 +1889,9 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port) unsigned int status; unsigned int ret; + serial8250_rpm_get(up); status = serial8250_modem_status(up); + serial8250_rpm_put(up); ret = 0; if (status & UART_MSR_DCD) @@ -1850,6 +1931,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; + serial8250_rpm_get(up); spin_lock_irqsave(&port->lock, flags); if (break_state == -1) up->lcr |= UART_LCR_SBC; @@ -1857,6 +1939,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) up->lcr &= ~UART_LCR_SBC; serial_port_out(port, UART_LCR, up->lcr); spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); } /* @@ -1901,12 +1984,23 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits) static int serial8250_get_poll_char(struct uart_port *port) { - unsigned char lsr = serial_port_in(port, UART_LSR); + struct uart_8250_port *up = up_to_u8250p(port); + unsigned char lsr; + int status; + + serial8250_rpm_get(up); - if (!(lsr & UART_LSR_DR)) - return NO_POLL_CHAR; + lsr = serial_port_in(port, UART_LSR); - return serial_port_in(port, UART_RX); + if (!(lsr & UART_LSR_DR)) { + status = NO_POLL_CHAR; + goto out; + } + + status = serial_port_in(port, UART_RX); +out: + serial8250_rpm_put(up); + return status; } @@ -1916,6 +2010,7 @@ static void serial8250_put_poll_char(struct uart_port *port, unsigned int ier; struct uart_8250_port *up = up_to_u8250p(port); + serial8250_rpm_get(up); /* * First save the IER then disable the interrupts */ @@ -1937,6 +2032,7 @@ static void serial8250_put_poll_char(struct uart_port *port, */ wait_for_xmitr(up, BOTH_EMPTY); serial_port_out(port, UART_IER, ier); + serial8250_rpm_put(up); } #endif /* CONFIG_CONSOLE_POLL */ @@ -1962,6 +2058,7 @@ int serial8250_do_startup(struct uart_port *port) if (port->iotype != up->cur_iotype) set_io_from_upio(port); + serial8250_rpm_get(up); if (port->type == PORT_16C950) { /* Wake up and initialize UART */ up->acr = 0; @@ -1982,7 +2079,6 @@ int serial8250_do_startup(struct uart_port *port) */ enable_rsa(up); #endif - /* * Clear the FIFO buffers and disable them. * (they will be reenabled in set_termios()) @@ -2006,7 +2102,8 @@ int serial8250_do_startup(struct uart_port *port) (serial_port_in(port, UART_LSR) == 0xff)) { printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n", serial_index(port)); - return -ENODEV; + retval = -ENODEV; + goto out; } /* @@ -2091,7 +2188,7 @@ int serial8250_do_startup(struct uart_port *port) } else { retval = serial_link_irq_chain(up); if (retval) - return retval; + goto out; } /* @@ -2189,8 +2286,10 @@ dont_test_tx_en: outb_p(0x80, icp); inb_p(icp); } - - return 0; + retval = 0; +out: + serial8250_rpm_put(up); + return retval; } EXPORT_SYMBOL_GPL(serial8250_do_startup); @@ -2206,6 +2305,7 @@ void serial8250_do_shutdown(struct uart_port *port) struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; + serial8250_rpm_get(up); /* * Disable interrupts from this port */ @@ -2245,6 +2345,7 @@ void serial8250_do_shutdown(struct uart_port *port) * the IRQ chain. */ serial_port_in(port, UART_RX); + serial8250_rpm_put(up); del_timer_sync(&up->timer); up->timer.function = serial8250_timeout; @@ -2360,6 +2461,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, * Ok, we're now changing the port state. Do it with * interrupts disabled. */ + serial8250_rpm_get(up); spin_lock_irqsave(&port->lock, flags); /* @@ -2481,6 +2583,8 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, } serial8250_set_mctrl(port, port->mctrl); spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); + /* Don't rewrite B0 */ if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); @@ -3091,6 +3195,8 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) touch_nmi_watchdog(); + serial8250_rpm_get(up); + if (port->sysrq || oops_in_progress) locked = spin_trylock_irqsave(&port->lock, flags); else @@ -3127,6 +3233,7 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) if (locked) spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); } static int __init serial8250_console_setup(struct console *co, char *options) diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 6fc9d7b..c267412 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -84,6 +84,7 @@ struct uart_8250_port { unsigned char mcr_mask; /* mask of user bits */ unsigned char mcr_force; /* mask of forced bits */ unsigned char cur_iotype; /* Running I/O type */ + unsigned char rpm_tx_active; /* * Some bits in registers are cleared on a read, so they must -- cgit v0.10.2 From 0aa525d11859c1a4d5b78fdc704148e2ae03ae13 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 10 Sep 2014 21:29:58 +0200 Subject: tty: serial: 8250_core: read only RX if there is something in the FIFO The serial8250_do_startup() function unconditionally clears the interrupts and for that it reads from the RX-FIFO without checking if there is a byte in the FIFO or not. This works fine on OMAP4+ HW like AM335x or DRA7. OMAP3630 ES1.1 (which means probably all OMAP3 and earlier) does not like this: |Unhandled fault: external abort on non-linefetch (0x1028) at 0xfb020000 |Internal error: : 1028 [#1] ARM |Modules linked in: |CPU: 0 PID: 1 Comm: swapper Not tainted 3.16.0-00022-g7edcb57-dirty #1213 |task: de0572c0 ti: de058000 task.ti: de058000 |PC is at mem32_serial_in+0xc/0x1c |LR is at serial8250_do_startup+0x220/0x85c |Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel |Control: 10c5387d Table: 80004019 DAC: 00000015 |[] (mem32_serial_in) from [] (serial8250_do_startup+0x220/0x85c) |[] (serial8250_do_startup) from [] (omap_8250_startup+0x5c/0xe0) |[] (omap_8250_startup) from [] (serial8250_startup+0x18/0x2c) |[] (serial8250_startup) from [] (uart_startup+0x78/0x1d8) |[] (uart_startup) from [] (uart_open+0xe8/0x114) |[] (uart_open) from [] (tty_open+0x1a8/0x5a4) Reviewed-by: Tony Lindgren Tested-by: Tony Lindgren Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 3cf5c98..547afde 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2088,8 +2088,8 @@ int serial8250_do_startup(struct uart_port *port) /* * Clear the interrupt registers. */ - serial_port_in(port, UART_LSR); - serial_port_in(port, UART_RX); + if (serial_port_in(port, UART_LSR) & UART_LSR_DR) + serial_port_in(port, UART_RX); serial_port_in(port, UART_IIR); serial_port_in(port, UART_MSR); @@ -2250,8 +2250,8 @@ dont_test_tx_en: * saved flags to avoid getting false values from polling * routines or the previous session. */ - serial_port_in(port, UART_LSR); - serial_port_in(port, UART_RX); + if (serial_port_in(port, UART_LSR) & UART_LSR_DR) + serial_port_in(port, UART_RX); serial_port_in(port, UART_IIR); serial_port_in(port, UART_MSR); up->lsr_saved_flags = 0; @@ -2344,7 +2344,8 @@ void serial8250_do_shutdown(struct uart_port *port) * Read data port to reset things, and then unlink from * the IRQ chain. */ - serial_port_in(port, UART_RX); + if (serial_port_in(port, UART_LSR) & UART_LSR_DR) + serial_port_in(port, UART_RX); serial8250_rpm_put(up); del_timer_sync(&up->timer); -- cgit v0.10.2 From 59b3e898ddfc81a65975043b5eb44103cc29ff6e Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 10 Sep 2014 21:29:59 +0200 Subject: tty: serial: 8250_core: use the ->line argument as a hint in serial8250_find_match_or_unused() Tony noticed that the old omap-serial driver picked the uart "number" based on the hint given from device tree or platform device's id. The 8250 based omap driver doesn't do this because the core code does not honour the ->line argument which is passed by the driver. This patch aims to keep the same behaviour as with omap-serial. The function will first try to use the line suggested ->line argument and then fallback to the old strategy in case the port is taken. That means the the third uart will always be ttyS2 even if the previous two have not been enabled in DT. Reviewed-by: Tony Lindgren Tested-by: Tony Lindgren Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 547afde..ac88e66 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -3516,6 +3516,11 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port * if (uart_match_port(&serial8250_ports[i].port, port)) return &serial8250_ports[i]; + /* try line number first if still available */ + i = port->line; + if (i < nr_uarts && serial8250_ports[i].port.type == PORT_UNKNOWN && + serial8250_ports[i].port.iobase == 0) + return &serial8250_ports[i]; /* * We didn't find a matching entry, so look for the first * free entry. We look for one which hasn't been previously -- cgit v0.10.2 From 9137568e73c1d132bc786676d9c4e00b91a1c627 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 10 Sep 2014 21:30:00 +0200 Subject: tty: serial: 8250_core: remove UART_IER_RDI in serial8250_stop_rx() serial8250_do_startup() adds UART_IER_RDI and UART_IER_RLSI to ier. serial8250_stop_rx() should remove both. This is what the serial-omap driver has been doing and is now moved to the 8250-core since it does no look to be *that* omap specific. Reviewed-by: Heikki Krogerus Reviewed-by: Tony Lindgren Tested-by: Tony Lindgren Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index ac88e66..139f3d2 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1390,7 +1390,7 @@ static void serial8250_stop_rx(struct uart_port *port) serial8250_rpm_get(up); - up->ier &= ~UART_IER_RLSI; + up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); up->port.read_status_mask &= ~UART_LSR_DR; serial_port_out(port, UART_IER, up->ier); -- cgit v0.10.2 From fb498df0787db0fb10a4a2cf91d0d3da13cc18f7 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Tue, 12 Aug 2014 10:05:22 +0100 Subject: xen_hvc: no reason to write the type key on xenstore The backend type is chosen by the toolstack. Regardless, the frontend should not care, especially given that it cannot have an effect as the backend is started before this code is run. Signed-off-by: Stefano Stabellini Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index 2dc2831..2967f03 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -402,9 +402,6 @@ static int xencons_connect_backend(struct xenbus_device *dev, evtchn); if (ret) goto error_xenbus; - ret = xenbus_printf(xbt, dev->nodename, "type", "ioemu"); - if (ret) - goto error_xenbus; ret = xenbus_transaction_end(xbt, 0); if (ret) { if (ret == -EAGAIN) -- cgit v0.10.2 From dc3187564e61260f49eceb21a4e7eb5e4428e90a Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Thu, 25 Sep 2014 11:19:51 +0200 Subject: tty: omap-serial: fix division by zero If the chosen baud rate is large enough (e.g. 3.5 megabaud), the calculated n values in serial_omap_is_baud_mode16() may become 0. This causes a division by zero when calculating the difference between calculated and desired baud rates. To prevent this, cap the n13 and n16 values on 1. Division by zero in kernel. [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (Ldiv0+0x8/0x10) [] (Ldiv0) from [] (serial_omap_baud_is_mode16+0x4c/0x68) [] (serial_omap_baud_is_mode16) from [] (serial_omap_set_termios+0x90/0x8d8) [] (serial_omap_set_termios) from [] (uart_change_speed+0xa4/0xa8) [] (uart_change_speed) from [] (uart_set_termios+0xa0/0x1fc) [] (uart_set_termios) from [] (tty_set_termios+0x248/0x2c0) [] (tty_set_termios) from [] (set_termios+0x248/0x29c) [] (set_termios) from [] (tty_mode_ioctl+0x1c8/0x4e8) [] (tty_mode_ioctl) from [] (tty_ioctl+0xa94/0xb18) [] (tty_ioctl) from [] (do_vfs_ioctl+0x4a0/0x560) [] (do_vfs_ioctl) from [] (SyS_ioctl+0x4c/0x74) [] (SyS_ioctl) from [] (ret_fast_syscall+0x0/0x30) Signed-off-by: Frans Klaver Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index d017cec..e454b7c 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -254,8 +254,16 @@ serial_omap_baud_is_mode16(struct uart_port *port, unsigned int baud) { unsigned int n13 = port->uartclk / (13 * baud); unsigned int n16 = port->uartclk / (16 * baud); - int baudAbsDiff13 = baud - (port->uartclk / (13 * n13)); - int baudAbsDiff16 = baud - (port->uartclk / (16 * n16)); + int baudAbsDiff13; + int baudAbsDiff16; + + if (n13 == 0) + n13 = 1; + if (n16 == 0) + n16 = 1; + + baudAbsDiff13 = baud - (port->uartclk / (13 * n13)); + baudAbsDiff16 = baud - (port->uartclk / (16 * n16)); if (baudAbsDiff13 < 0) baudAbsDiff13 = -baudAbsDiff13; if (baudAbsDiff16 < 0) -- cgit v0.10.2 From 13d6ceb4c4fe1e9688e19b15b123b1830c596cf1 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 24 Sep 2014 09:55:22 +0200 Subject: tty: omap-serial: pull out calculation from baud_is_mode16 To determine the correct divisor, we need to know the difference between the desired baud rate and the actual baud rate. The calculation for this difference is implemented twice within omap_serial_baud_is_mode16(). Pull out the calculation for easier maintenance. While at it, remove the CamelCasing from the variable names. Signed-off-by: Frans Klaver Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index e454b7c..18c30ca 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -239,6 +239,26 @@ static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable) } /* + * Calculate the absolute difference between the desired and actual baud + * rate for the given mode. + */ +static inline int calculate_baud_abs_diff(struct uart_port *port, + unsigned int baud, unsigned int mode) +{ + unsigned int n = port->uartclk / (mode * baud); + int abs_diff; + + if (n == 0) + n = 1; + + abs_diff = baud - (port->uartclk / (mode * n)); + if (abs_diff < 0) + abs_diff = -abs_diff; + + return abs_diff; +} + +/* * serial_omap_baud_is_mode16 - check if baud rate is MODE16X * @port: uart port info * @baud: baudrate for which mode needs to be determined @@ -252,24 +272,10 @@ static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable) static bool serial_omap_baud_is_mode16(struct uart_port *port, unsigned int baud) { - unsigned int n13 = port->uartclk / (13 * baud); - unsigned int n16 = port->uartclk / (16 * baud); - int baudAbsDiff13; - int baudAbsDiff16; - - if (n13 == 0) - n13 = 1; - if (n16 == 0) - n16 = 1; - - baudAbsDiff13 = baud - (port->uartclk / (13 * n13)); - baudAbsDiff16 = baud - (port->uartclk / (16 * n16)); - if (baudAbsDiff13 < 0) - baudAbsDiff13 = -baudAbsDiff13; - if (baudAbsDiff16 < 0) - baudAbsDiff16 = -baudAbsDiff16; - - return (baudAbsDiff13 >= baudAbsDiff16); + int abs_diff_13 = calculate_baud_abs_diff(port, baud, 13); + int abs_diff_16 = calculate_baud_abs_diff(port, baud, 16); + + return (abs_diff_13 >= abs_diff_16); } /* -- cgit v0.10.2 From 1ede7dcca3c4fa15a518ab0473126f9c3e621e4c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 23 Sep 2014 01:21:11 +0100 Subject: serial: 8250: Add Quark X1000 to 8250_pci.c Quark X1000 contains two designware derived 8250 serial ports. Each port has a unique PCI configuration space consisting of BAR0:UART BAR1:DMA respectively. Unlike the standard 8250 the register width is 32 bits for RHR,IER etc The Quark UART has a fundamental clock @ 44.2368 MHz allowing for a bitrate of up to about 2.76 megabits per second. This patch enables standard 8250 mode Signed-off-by: Bryan O'Donoghue Reviewed-by: Heikki Krogerus Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 61830b1..14d3e6b 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1788,6 +1788,7 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 #define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a #define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e +#define PCI_DEVICE_ID_INTEL_QRK_UART 0x0936 #define PCI_VENDOR_ID_SUNIX 0x1fd4 #define PCI_DEVICE_ID_SUNIX_1999 0x1999 @@ -1898,6 +1899,13 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = byt_serial_setup, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_QRK_UART, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + }, /* * ITE */ @@ -2740,6 +2748,7 @@ enum pci_board_num_t { pbn_ADDIDATA_PCIe_8_3906250, pbn_ce4100_1_115200, pbn_byt, + pbn_qrk, pbn_omegapci, pbn_NETMOS9900_2s_115200, pbn_brcm_trumanage, @@ -3490,6 +3499,12 @@ static struct pciserial_board pci_boards[] = { .uart_offset = 0x80, .reg_shift = 2, }, + [pbn_qrk] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 2764800, + .reg_shift = 2, + }, [pbn_omegapci] = { .flags = FL_BASE0, .num_ports = 8, @@ -5192,6 +5207,12 @@ static struct pci_device_id serial_pci_tbl[] = { pbn_byt }, /* + * Intel Quark x1000 + */ + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QRK_UART, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_qrk }, + /* * Cronyx Omega PCI */ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_CRONYX_OMEGA, -- cgit v0.10.2 From bb2f861ad00fc09dab955d22280c51cead795bca Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Fri, 19 Sep 2014 15:26:40 +0800 Subject: serial: imx: fix throttle/unthrottle callbacks for hardware assisted flow control when the 'CTSC' bit is negated. 'CTS' has no function when 'CTSC' is asserted. 0: The CTS pin is high (inactive) 1: The CTS pin is low (active) For throttle, it needs to clear 'CTS' and 'CTSC' bits. For unthrottle, it needs to enable 'CTS' and 'CTSC' bits. The patch just fix the issue. Signed-off-by: Fugang Duan Signed-off-by: Huang Shijie Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index be13eb3..c7683d7 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -817,11 +817,9 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl) struct imx_port *sport = (struct imx_port *)port; unsigned long temp; - temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS; - + temp = readl(sport->port.membase + UCR2) & ~(UCR2_CTS | UCR2_CTSC); if (mctrl & TIOCM_RTS) - if (!sport->dma_is_enabled) - temp |= UCR2_CTS; + temp |= UCR2_CTS | UCR2_CTSC; writel(temp, sport->port.membase + UCR2); -- cgit v0.10.2 From 45564a6656b4944fa90aabd59f8ec082e8f7a969 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 19 Sep 2014 15:33:12 +0800 Subject: serial: imx: terminate the RX DMA when the UART is suspending When the uart port is suspending, the RX data is useless. So in this case, we can terminate the RX DMA right now. Signed-off-by: Huang Shijie Signed-off-by: Fugang Duan Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index c7683d7..db749f7 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -436,12 +436,14 @@ static void imx_stop_rx(struct uart_port *port) struct imx_port *sport = (struct imx_port *)port; unsigned long temp; - /* - * We are maybe in the SMP context, so if the DMA TX thread is running - * on other cpu, we have to wait for it to finish. - */ - if (sport->dma_is_enabled && sport->dma_is_rxing) - return; + if (sport->dma_is_enabled && sport->dma_is_rxing) { + if (sport->port.suspended) { + dmaengine_terminate_all(sport->dma_chan_rx); + sport->dma_is_rxing = 0; + } else { + return; + } + } temp = readl(sport->port.membase + UCR2); writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2); -- cgit v0.10.2 From a4688bcd34bd2f4fe7fb88856c85d3091270ab53 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 19 Sep 2014 15:42:57 +0800 Subject: serial: imx: change the wait even to interruptiable The wait_event() makes the application hang for ever in the following case: [1] the hardware flow control is enabled. [2] the other end (or the remote end) is terminated, and the TX is still waiting for the hardware flow control signal to become asserted. This patch fixes it by changing the wait_event to wait_event_interruptible. Signed-off-by: Huang Shijie Signed-off-by: Fugang Duan Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index db749f7..8f62a3c 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1219,9 +1219,18 @@ static void imx_shutdown(struct uart_port *port) unsigned long flags; if (sport->dma_is_enabled) { + int ret; + /* We have to wait for the DMA to finish. */ - wait_event(sport->dma_wait, + ret = wait_event_interruptible(sport->dma_wait, !sport->dma_is_rxing && !sport->dma_is_txing); + if (ret != 0) { + sport->dma_is_rxing = 0; + sport->dma_is_txing = 0; + dmaengine_terminate_all(sport->dma_chan_tx); + dmaengine_terminate_all(sport->dma_chan_rx); + } + imx_stop_tx(port); imx_stop_rx(port); imx_disable_dma(sport); imx_uart_dma_exit(sport); -- cgit v0.10.2 From 6fa62fc46e10f34aed70d2cfcf573ba8d3833e18 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 10 Sep 2014 12:43:02 +0200 Subject: serial: cadence: Add generic earlycon support Add earlycon support for the cadence serial port. This is based on recent patches: "tty/serial: pl011: add generic earlycon support" (sha1: 0d3c673e7881e691991b2a4745bd4f149603baa2) "tty/serial: add arm/arm64 semihosting earlycon" (sha1: d50d7269ebcb438afa346cdffce0f4e2a1b9e831) Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 10d51c2..adc1cd7 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -921,6 +921,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted. earlycon= [KNL] Output early console device and options. + cdns, + Start an early, polled-mode console on a cadence serial + port at the specified address. The cadence serial port + must already be setup and configured. Options are not + yet supported. + uart[8250],io,[,options] uart[8250],mmio,[,options] uart[8250],mmio32,[,options] diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 81f6ee7..02a896f 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1429,6 +1429,7 @@ config SERIAL_XILINX_PS_UART_CONSOLE bool "Cadence UART console support" depends on SERIAL_XILINX_PS_UART=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON help Enable a Cadence UART port to be the system console. diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 7f8027f..200c1af 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1051,6 +1051,25 @@ static void cdns_uart_console_putchar(struct uart_port *port, int ch) cdns_uart_writel(ch, CDNS_UART_FIFO_OFFSET); } +static void cdns_early_write(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, cdns_uart_console_putchar); +} + +static int __init cdns_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = cdns_early_write; + + return 0; +} +EARLYCON_DECLARE(cdns, cdns_early_console_setup); + /** * cdns_uart_console_write - perform write operation * @co: Console handle -- cgit v0.10.2 From 716e115cd7f75e3ab717f467432fd4b8cd23ee2c Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 11 Sep 2014 15:26:12 +0300 Subject: serial: 8250_pci: remove rts_n override from Baytrail quirk It should not be used together with Auto Flow Control, and Auto Flow Control is always enabled on Baytrail. Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 14d3e6b..4f1cd29 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1355,9 +1355,6 @@ ce4100_serial_setup(struct serial_private *priv, #define BYT_PRV_CLK_N_VAL_SHIFT 16 #define BYT_PRV_CLK_UPDATE (1 << 31) -#define BYT_GENERAL_REG 0x808 -#define BYT_GENERAL_DIS_RTS_N_OVERRIDE (1 << 3) - #define BYT_TX_OVF_INT 0x820 #define BYT_TX_OVF_INT_MASK (1 << 1) @@ -1412,16 +1409,6 @@ byt_set_termios(struct uart_port *p, struct ktermios *termios, reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE; writel(reg, p->membase + BYT_PRV_CLK); - /* - * If auto-handshake mechanism is not enabled, - * disable rts_n override - */ - reg = readl(p->membase + BYT_GENERAL_REG); - reg &= ~BYT_GENERAL_DIS_RTS_N_OVERRIDE; - if (termios->c_cflag & CRTSCTS) - reg |= BYT_GENERAL_DIS_RTS_N_OVERRIDE; - writel(reg, p->membase + BYT_GENERAL_REG); - serial8250_do_set_termios(p, termios, old); } -- cgit v0.10.2 From c3f43eec80d54088fd9f02b223a1f972c5968110 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Fri, 12 Sep 2014 23:15:22 +0100 Subject: tty/metag_da: Add console_poll module parameter Currently the metag_da console channel is write only, it doesn't get polled for incoming data. This is for performance & latency reasons as polling blocks the processor for a significant amount of time waiting for the JTAG probe to respond to the request and restart the thread. This means that the console channel cannot be used for a login prompt, so ttyDA2 is usually used instead. In a virtual environment with a virtual DA such as QEMU however the polling is much cheaper as the request can be handled immediately. It is useful to be able to enable polling in such an environment in order to get both the kernel log and a usable login prompt on the same DA channel. Add a console_poll module parameter which allows polling to be enabled for the console channel. It defaults to off as that is the current behaviour for good reason on real hardware. It can be enabled by appending metag_da.console_poll to the kernel command line. Signed-off-by: James Hogan Cc: Jiri Slaby Tested-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/metag_da.c b/drivers/tty/metag_da.c index 7332e2c..3774600 100644 --- a/drivers/tty/metag_da.c +++ b/drivers/tty/metag_da.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,15 @@ static struct tty_driver *channel_driver; static struct timer_list put_timer; static struct task_struct *dashtty_thread; +/* + * The console_poll parameter determines whether the console channel should be + * polled for input. + * By default the console channel isn't polled at all, in order to avoid the + * overhead, but that means it isn't possible to have a login on /dev/console. + */ +static bool console_poll; +module_param(console_poll, bool, S_IRUGO); + #define RX_BUF_SIZE 1024 enum { @@ -353,7 +363,7 @@ static int dashtty_port_activate(struct tty_port *port, struct tty_struct *tty) * possible to have a login on /dev/console. * */ - if (dport != &dashtty_ports[CONSOLE_CHANNEL]) + if (console_poll || dport != &dashtty_ports[CONSOLE_CHANNEL]) if (atomic_inc_return(&num_channels_need_poll) == 1) add_poll_timer(&poll_timer); @@ -372,7 +382,7 @@ static void dashtty_port_shutdown(struct tty_port *port) unsigned int count; /* stop reading */ - if (dport != &dashtty_ports[CONSOLE_CHANNEL]) + if (console_poll || dport != &dashtty_ports[CONSOLE_CHANNEL]) if (atomic_dec_and_test(&num_channels_need_poll)) del_timer_sync(&poll_timer); -- cgit v0.10.2 From 195311761e5492afdb2ab0454f51223bc75649dd Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 19 Sep 2014 09:18:13 +0200 Subject: asm/uapi: Add definition of TIOC[SG]RS485 Commit: e676253b19b2d269cccf67fdb1592120a0cd0676 (serial/8250: Add support for RS485 IOCTLs), adds support for RS485 ioctls for 825_core on all the archs. Unfortunately the definition of TIOCSRS485 and TIOCGRS485 was missing on the ioctls.h file Reported-by: Guenter Roeck Reviewed-by: Guenter Roeck Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/arch/alpha/include/uapi/asm/ioctls.h b/arch/alpha/include/uapi/asm/ioctls.h index 92c557b..f30c94a 100644 --- a/arch/alpha/include/uapi/asm/ioctls.h +++ b/arch/alpha/include/uapi/asm/ioctls.h @@ -90,6 +90,8 @@ #define TIOCSBRK 0x5427 /* BSD compatibility */ #define TIOCCBRK 0x5428 /* BSD compatibility */ #define TIOCGSID 0x5429 /* Return the session ID of FD */ +#define TIOCGRS485 _IOR('T', 0x2E, struct serial_rs485) +#define TIOCSRS485 _IOWR('T', 0x2F, struct serial_rs485) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ -- cgit v0.10.2 From 2c8faf3d652b6b2dffc29e48d968349bd16aa32f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 14 Sep 2014 11:42:11 +0200 Subject: m68k: AMIGA_BUILTIN_SERIAL should depend on TTY If CONFIG_TTY=n: drivers/built-in.o: In function `rs_flush_buffer': amiserial.c:(.text+0x1f626): undefined reference to `tty_wakeup' drivers/built-in.o: In function `transmit_chars': amiserial.c:(.text+0x1f6c8): undefined reference to `tty_wakeup' drivers/built-in.o: In function `change_speed': amiserial.c:(.text+0x1f80a): undefined reference to `tty_termios_baud_rate' amiserial.c:(.text+0x1f88c): undefined reference to `tty_termios_baud_rate' drivers/built-in.o: In function `check_modem_status': amiserial.c:(.text+0x1faa6): undefined reference to `tty_hangup' amiserial.c:(.text+0x1faec): undefined reference to `tty_wakeup' drivers/built-in.o: In function `get_serial_info': amiserial.c:(.text+0x1fb88): undefined reference to `tty_lock' amiserial.c:(.text+0x1fbc0): undefined reference to `tty_unlock' drivers/built-in.o: In function `rs_open': amiserial.c:(.text+0x1fdc6): undefined reference to `tty_port_block_til_ready' drivers/built-in.o: In function `set_serial_info': amiserial.c:(.text+0x1fe0c): undefined reference to `tty_lock' amiserial.c:(.text+0x1fea0): undefined reference to `tty_unlock' amiserial.c:(.text+0x1fed0): undefined reference to `tty_unlock' amiserial.c:(.text+0x1ffaa): undefined reference to `tty_unlock' drivers/built-in.o: In function `receive_chars': amiserial.c:(.text+0x2005c): undefined reference to `do_SAK' amiserial.c:(.text+0x200e0): undefined reference to `tty_insert_flip_string_flags' amiserial.c:(.text+0x2013c): undefined reference to `tty_insert_flip_string_flags' amiserial.c:(.text+0x20148): undefined reference to `tty_flip_buffer_push' drivers/built-in.o: In function `rs_close': amiserial.c:(.text+0x20744): undefined reference to `tty_port_close_start' amiserial.c:(.text+0x2078a): undefined reference to `tty_ldisc_flush' amiserial.c:(.text+0x20798): undefined reference to `tty_port_close_end' drivers/built-in.o: In function `amiga_serial_probe': amiserial.c:(.init.text+0x96a): undefined reference to `__tty_alloc_driver' amiserial.c:(.init.text+0x9c0): undefined reference to `tty_std_termios' amiserial.c:(.init.text+0x9e6): undefined reference to `tty_set_operations' amiserial.c:(.init.text+0xa3e): undefined reference to `tty_port_init' amiserial.c:(.init.text+0xa5c): undefined reference to `tty_port_link_device' amiserial.c:(.init.text+0xa6c): undefined reference to `tty_register_driver' amiserial.c:(.init.text+0xb4a): undefined reference to `tty_unregister_driver' amiserial.c:(.init.text+0xb58): undefined reference to `tty_port_destroy' amiserial.c:(.init.text+0xb64): undefined reference to `put_tty_driver' Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman diff --git a/arch/m68k/Kconfig.devices b/arch/m68k/Kconfig.devices index d163991..2361930 100644 --- a/arch/m68k/Kconfig.devices +++ b/arch/m68k/Kconfig.devices @@ -95,7 +95,7 @@ config ATARI_DSP56K config AMIGA_BUILTIN_SERIAL tristate "Amiga builtin serial support" - depends on AMIGA + depends on AMIGA && TTY help If you want to use your Amiga's built-in serial port in Linux, answer Y. -- cgit v0.10.2 From 2dea53bf57783f243c892e99c10c6921e956aa7e Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Tue, 23 Sep 2014 16:34:12 +0800 Subject: serial: of-serial: add PM suspend/resume support This adds PM suspend/resume support for the of-serial driver to provide power management support on devices attatched to it. Signed-off-by: Jingchang Lu Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 27981e2..8bc2563 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -240,6 +240,32 @@ static int of_platform_serial_remove(struct platform_device *ofdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int of_serial_suspend(struct device *dev) +{ + struct of_serial_info *info = dev_get_drvdata(dev); + + serial8250_suspend_port(info->line); + if (info->clk) + clk_disable_unprepare(info->clk); + + return 0; +} + +static int of_serial_resume(struct device *dev) +{ + struct of_serial_info *info = dev_get_drvdata(dev); + + if (info->clk) + clk_prepare_enable(info->clk); + + serial8250_resume_port(info->line); + + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(of_serial_pm_ops, of_serial_suspend, of_serial_resume); + /* * A few common types, add more as needed. */ @@ -271,6 +297,7 @@ static struct platform_driver of_platform_serial_driver = { .name = "of_serial", .owner = THIS_MODULE, .of_match_table = of_platform_serial_table, + .pm = &of_serial_pm_ops, }, .probe = of_platform_serial_probe, .remove = of_platform_serial_remove, -- cgit v0.10.2 From daea65a7be76953bb80d13d0ac43e7441fd42cdf Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 23 Sep 2014 08:31:30 +0100 Subject: serial: asc: Conditionally use readl_relaxed (COMPILE_TEST) Commit 08177ece596c ("serial: asc: Adopt readl_/writel_relaxed()) is regressing the m68k allmodconfig build. This is due to the unconditional use of readl_relaxed() which, although documented, does not currently exist for m68k. This is trivially fixable for st-asc because we can just update the asc_in() accessor to make this conditional. Reported-by: Guenter Roeck Signed-off-by: Daniel Thompson Cc: Srinivas Kandagatla Cc: Maxime Coquelin Cc: Patrice Chotard Cc: Jiri Slaby Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index a3fc167..a316584 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -151,7 +151,11 @@ static inline struct asc_port *to_asc_port(struct uart_port *port) static inline u32 asc_in(struct uart_port *port, u32 offset) { +#ifdef readl_relaxed return readl_relaxed(port->membase + offset); +#else + return readl(port->membase + offset); +#endif } static inline void asc_out(struct uart_port *port, u32 offset, u32 value) -- cgit v0.10.2 From a86713b1536c818972675e6dd8c6e738f0379f1d Mon Sep 17 00:00:00 2001 From: Sudhir Sreedharan Date: Mon, 22 Sep 2014 12:00:22 +0530 Subject: serial/core: Initialize the console pm state For console devices having UART_CAP_SLEEP capability, the uart_pm_state has to be initialized to UART_PM_STATE_ON. Otherwise the LCR regiser values are reinitialized when uart_change_pm is called from uart_configure_port. Signed-off-by: Sudhir Sreedharan Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index df3a8c7..e7d9d65 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2606,6 +2606,9 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) if (uport->cons && uport->dev) of_console_check(uport->dev->of_node, uport->cons->name, uport->line); + if (uart_console(uport)) + state->pm_state = UART_PM_STATE_ON; + uart_configure_port(drv, state, uport); num_groups = 2; -- cgit v0.10.2 From 0efe72963409739778e93d8e2046305bc8310e83 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 15 Sep 2014 17:22:51 -0700 Subject: tty: serial: msm: Add earlycon support Add support for DT based and command line based early console on platforms with the msm serial hardware. Cc: Rob Herring Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index adc1cd7..67e93f1 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -942,6 +942,18 @@ bytes respectively. Such letter suffixes can also be entirely omitted. must already be setup and configured. Options are not yet supported. + msm_serial, + Start an early, polled-mode console on an msm serial + port at the specified address. The serial port + must already be setup and configured. Options are not + yet supported. + + msm_serial_dm, + Start an early, polled-mode console on an msm serial + dm port at the specified address. The serial port + must already be setup and configured. Options are not + yet supported. + smh Use ARM semihosting calls for early console. earlyprintk= [X86,SH,BLACKFIN,ARM,M68k] diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 02a896f..649b784 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1070,6 +1070,7 @@ config SERIAL_MSM_CONSOLE bool "MSM serial console support" depends on SERIAL_MSM=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON config SERIAL_MSM_HS tristate "MSM UART High Speed: Serial Driver" diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 4f9640d..4b6c783 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -845,22 +845,15 @@ static inline struct uart_port *get_port_from_line(unsigned int line) } #ifdef CONFIG_SERIAL_MSM_CONSOLE -static void msm_console_write(struct console *co, const char *s, - unsigned int count) +static void __msm_console_write(struct uart_port *port, const char *s, + unsigned int count, bool is_uartdm) { int i; - struct uart_port *port; - struct msm_port *msm_port; int num_newlines = 0; bool replaced = false; void __iomem *tf; - BUG_ON(co->index < 0 || co->index >= UART_NR); - - port = get_port_from_line(co->index); - msm_port = UART_TO_MSM(port); - - if (msm_port->is_uartdm) + if (is_uartdm) tf = port->membase + UARTDM_TF; else tf = port->membase + UART_TF; @@ -872,7 +865,7 @@ static void msm_console_write(struct console *co, const char *s, count += num_newlines; spin_lock(&port->lock); - if (msm_port->is_uartdm) + if (is_uartdm) reset_dm_count(port, count); i = 0; @@ -881,7 +874,7 @@ static void msm_console_write(struct console *co, const char *s, unsigned int num_chars; char buf[4] = { 0 }; - if (msm_port->is_uartdm) + if (is_uartdm) num_chars = min(count - i, (unsigned int)sizeof(buf)); else num_chars = 1; @@ -910,6 +903,20 @@ static void msm_console_write(struct console *co, const char *s, spin_unlock(&port->lock); } +static void msm_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port; + struct msm_port *msm_port; + + BUG_ON(co->index < 0 || co->index >= UART_NR); + + port = get_port_from_line(co->index); + msm_port = UART_TO_MSM(port); + + __msm_console_write(port, s, count, msm_port->is_uartdm); +} + static int __init msm_console_setup(struct console *co, char *options) { struct uart_port *port; @@ -952,6 +959,49 @@ static int __init msm_console_setup(struct console *co, char *options) return uart_set_options(port, co, baud, parity, bits, flow); } +static void +msm_serial_early_write(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + + __msm_console_write(&dev->port, s, n, false); +} + +static int __init +msm_serial_early_console_setup(struct earlycon_device *device, const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = msm_serial_early_write; + return 0; +} +EARLYCON_DECLARE(msm_serial, msm_serial_early_console_setup); +OF_EARLYCON_DECLARE(msm_serial, "qcom,msm-uart", + msm_serial_early_console_setup); + +static void +msm_serial_early_write_dm(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + + __msm_console_write(&dev->port, s, n, true); +} + +static int __init +msm_serial_early_console_setup_dm(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = msm_serial_early_write_dm; + return 0; +} +EARLYCON_DECLARE(msm_serial_dm, msm_serial_early_console_setup_dm); +OF_EARLYCON_DECLARE(msm_serial_dm, "qcom,msm-uartdm", + msm_serial_early_console_setup_dm); + static struct uart_driver msm_uart_driver; static struct console msm_console = { -- cgit v0.10.2 From baeb7ef34952f523a129e5d1369aa42ecbe7f8c9 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Sep 2014 10:21:03 +0200 Subject: tty: serial: 8250: use 32bit variable for rpm_tx_active The kbuild test robot wrote me: | make.cross ARCH=powerpc |>> ERROR: ".__xchg_called_with_bad_pointer" [drivers/tty/serial/8250/8250.ko] undefined! The generic implementation of xchg() on arm and x86 works for variables of size one bye (char). According to the report powerpc does not support xchg() for one byte sized variables and looking further it seems also to be the same case for sparc and tile (or for 10 out of 26 architectures which provide a custom implementation). For that reason I increase the size of the variable from one to four bytes to get it work on powerpc (and the others). Reported-By: kbuild test robot Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index c267412..3df10d5 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -84,7 +84,7 @@ struct uart_8250_port { unsigned char mcr_mask; /* mask of user bits */ unsigned char mcr_force; /* mask of forced bits */ unsigned char cur_iotype; /* Running I/O type */ - unsigned char rpm_tx_active; + unsigned int rpm_tx_active; /* * Some bits in registers are cleared on a read, so they must -- cgit v0.10.2 From 69784fa539efa477f39b5a849c27d5e630e0db27 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 2 Oct 2014 21:33:50 -0700 Subject: Revert "serial/core: Initialize the console pm state" This reverts commit a86713b1536c818972675e6dd8c6e738f0379f1d. Kevin Hilman writes: Multiple boot failures on ARM[1] were bisected down to this patch. How was this patch tested, and on which platforms? Also, the changelog states that this should be done only for UART_CAP_SLEEP, but the patch does it for every UART. Greg, I suggest this patch be dropped from tty-next until it has been better described and tested. [1] http://lists.linaro.org/pipermail/kernel-build-reports/2014-October/005550.html Reported-by: Kevin Hilman Cc: Sudhir Sreedharan Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index e7d9d65..df3a8c7 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2606,9 +2606,6 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) if (uport->cons && uport->dev) of_console_check(uport->dev->of_node, uport->cons->name, uport->line); - if (uart_console(uport)) - state->pm_state = UART_PM_STATE_ON; - uart_configure_port(drv, state, uport); num_groups = 2; -- cgit v0.10.2