diff options
Diffstat (limited to 'drivers/tty')
33 files changed, 1956 insertions, 319 deletions
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 6b78399..58ad1c0 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_TTY) += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \ - tty_buffer.o tty_port.o tty_mutex.o + tty_buffer.o tty_port.o tty_mutex.o tty_ldsem.o obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o obj-$(CONFIG_AUDIT) += tty_audit.o diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index b6f7d52..9d47f50 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -1328,7 +1328,7 @@ out_error: */ static int __init hvc_iucv_config(char *val) { - return strict_strtoul(val, 10, &hvc_iucv_devices); + return kstrtoul(val, 10, &hvc_iucv_devices); } diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 6c7fe90..4bf0fc0 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -89,6 +89,7 @@ struct n_tty_data { int read_head; int read_tail; int read_cnt; + int minimum_to_wake; unsigned char *echo_buf; unsigned int echo_pos; @@ -114,22 +115,25 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, } /** - * n_tty_set__room - receive space + * n_tty_set_room - receive space * @tty: terminal * - * Called by the driver to find out how much data it is - * permitted to feed to the line discipline without any being lost - * and thus to manage flow control. Not serialized. Answers for the - * "instant". + * Updates tty->receive_room to reflect the currently available space + * in the input buffer, and re-schedules the flip buffer work if space + * just became available. + * + * Locks: Concurrent update is protected with read_lock */ -static void n_tty_set_room(struct tty_struct *tty) +static int set_room(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; int left; int old_left; + unsigned long flags; + + raw_spin_lock_irqsave(&ldata->read_lock, flags); - /* ldata->read_cnt is not read locked ? */ if (I_PARMRK(tty)) { /* Multiply read_cnt by 3, since each byte might take up to * three times as many spaces when PARMRK is set (depending on @@ -149,8 +153,15 @@ static void n_tty_set_room(struct tty_struct *tty) old_left = tty->receive_room; tty->receive_room = left; + raw_spin_unlock_irqrestore(&ldata->read_lock, flags); + + return left && !old_left; +} + +static void n_tty_set_room(struct tty_struct *tty) +{ /* Did this open up the receive buffer? We may need to flip */ - if (left && !old_left) { + if (set_room(tty)) { WARN_RATELIMIT(tty->port->itty == NULL, "scheduling with invalid itty\n"); /* see if ldisc has been killed - if so, this means that @@ -647,8 +658,7 @@ static void process_echoes(struct tty_struct *tty) if (no_space_left) break; } else { - if (O_OPOST(tty) && - !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { + if (O_OPOST(tty)) { int retval = do_output_char(c, tty, space); if (retval < 0) break; @@ -1454,9 +1464,9 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, tty->ops->flush_chars(tty); } - n_tty_set_room(tty); + set_room(tty); - if ((!ldata->icanon && (ldata->read_cnt >= tty->minimum_to_wake)) || + if ((!ldata->icanon && (ldata->read_cnt >= ldata->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) @@ -1516,12 +1526,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) wake_up_interruptible(&tty->read_wait); ldata->icanon = (L_ICANON(tty) != 0); - if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { - ldata->raw = 1; - ldata->real_raw = 1; - n_tty_set_room(tty); - return; - } + if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || @@ -1642,7 +1647,7 @@ static int n_tty_open(struct tty_struct *tty) tty->disc_data = ldata; reset_buffer_flags(tty->disc_data); ldata->column = 0; - tty->minimum_to_wake = 1; + ldata->minimum_to_wake = 1; tty->closing = 0; /* indicate buffer work may resume */ clear_bit(TTY_LDISC_HALTED, &tty->flags); @@ -1806,21 +1811,17 @@ do_it_again: minimum = time = 0; timeout = MAX_SCHEDULE_TIMEOUT; if (!ldata->icanon) { - time = (HZ / 10) * TIME_CHAR(tty); minimum = MIN_CHAR(tty); if (minimum) { + time = (HZ / 10) * TIME_CHAR(tty); if (time) - tty->minimum_to_wake = 1; + ldata->minimum_to_wake = 1; else if (!waitqueue_active(&tty->read_wait) || - (tty->minimum_to_wake > minimum)) - tty->minimum_to_wake = minimum; + (ldata->minimum_to_wake > minimum)) + ldata->minimum_to_wake = minimum; } else { - timeout = 0; - if (time) { - timeout = time; - time = 0; - } - tty->minimum_to_wake = minimum = 1; + timeout = (HZ / 10) * TIME_CHAR(tty); + ldata->minimum_to_wake = minimum = 1; } } @@ -1860,9 +1861,9 @@ do_it_again: TASK_RUNNING. */ set_current_state(TASK_INTERRUPTIBLE); - if (((minimum - (b - buf)) < tty->minimum_to_wake) && + if (((minimum - (b - buf)) < ldata->minimum_to_wake) && ((minimum - (b - buf)) >= 1)) - tty->minimum_to_wake = (minimum - (b - buf)); + ldata->minimum_to_wake = (minimum - (b - buf)); if (!input_available_p(tty, 0)) { if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { @@ -1881,7 +1882,6 @@ do_it_again: retval = -ERESTARTSYS; break; } - /* FIXME: does n_tty_set_room need locking ? */ n_tty_set_room(tty); timeout = schedule_timeout(timeout); continue; @@ -1979,7 +1979,7 @@ do_it_again: remove_wait_queue(&tty->read_wait, &wait); if (!waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = minimum; + ldata->minimum_to_wake = minimum; __set_current_state(TASK_RUNNING); size = b - buf; @@ -2045,7 +2045,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, retval = -EIO; break; } - if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { + if (O_OPOST(tty)) { while (nr > 0) { ssize_t num = process_output_block(tty, b, nr); if (num < 0) { @@ -2111,6 +2111,7 @@ break_out: static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) { + struct n_tty_data *ldata = tty->disc_data; unsigned int mask = 0; poll_wait(file, &tty->read_wait, wait); @@ -2125,9 +2126,9 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, mask |= POLLHUP; if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) { if (MIN_CHAR(tty) && !TIME_CHAR(tty)) - tty->minimum_to_wake = MIN_CHAR(tty); + ldata->minimum_to_wake = MIN_CHAR(tty); else - tty->minimum_to_wake = 1; + ldata->minimum_to_wake = 1; } if (tty->ops->write && !tty_is_writelocked(tty) && tty_chars_in_buffer(tty) < WAKEUP_CHARS && @@ -2175,6 +2176,18 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file, } } +static void n_tty_fasync(struct tty_struct *tty, int on) +{ + struct n_tty_data *ldata = tty->disc_data; + + if (!waitqueue_active(&tty->read_wait)) { + if (on) + ldata->minimum_to_wake = 1; + else if (!tty->fasync) + ldata->minimum_to_wake = N_TTY_BUF_SIZE; + } +} + struct tty_ldisc_ops tty_ldisc_N_TTY = { .magic = TTY_LDISC_MAGIC, .name = "n_tty", @@ -2188,7 +2201,8 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = { .set_termios = n_tty_set_termios, .poll = n_tty_poll, .receive_buf = n_tty_receive_buf, - .write_wakeup = n_tty_write_wakeup + .write_wakeup = n_tty_write_wakeup, + .fasync = n_tty_fasync, }; /** diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 26e3a97..c52948b 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -4797,10 +4797,6 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_VENDOR_ID_IBM, 0x0299, 0, 0, pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, - 0x1000, 0x0012, - 0, 0, pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901, 0xA000, 0x1000, 0, 0, pbn_b0_1_115200 }, diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 80fe91e..a1ba94d 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -12,9 +12,8 @@ config SERIAL_8250 here are those that are setting up dedicated Ethernet WWW/FTP servers, or users that have one of the various bus mice instead of a serial mouse and don't intend to use their machine's standard serial - port for anything. (Note that the Cyclades and Stallion multi - serial port drivers do not need this driver built in for them to - work.) + port for anything. (Note that the Cyclades multi serial port driver + does not need this driver built in for it to work.) To compile this driver as a module, choose M here: the module will be called 8250. diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 7e7006f..46dd1c7 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -551,7 +551,7 @@ config BFIN_UART3_CTSRTS Enable hardware flow control in the driver. config SERIAL_IMX - bool "IMX serial port support" + tristate "IMX serial port support" depends on ARCH_MXC select SERIAL_CORE select RATIONAL @@ -561,22 +561,21 @@ config SERIAL_IMX config SERIAL_IMX_CONSOLE bool "Console on IMX serial port" - depends on SERIAL_IMX + depends on SERIAL_IMX=y select SERIAL_CORE_CONSOLE help - If you have enabled the serial port on the Motorola IMX + If you have enabled the serial port on the Freescale IMX CPU you can make it the console by answering Y to this option. Even if you say Y here, the currently visible virtual console (/dev/tty0) will still be used as the system console by default, but you can alter that using a kernel command line option such as - "console=ttySA0". (Try "man bootparam" or see the documentation of - your boot loader (lilo or loadlin) about how to pass options to the - kernel at boot time.) + "console=ttymxc0". (Try "man bootparam" or see the documentation of + your bootloader about how to pass options to the kernel at boot time.) config SERIAL_UARTLITE tristate "Xilinx uartlite serial port support" - depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE + depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE || ARCH_ZYNQ select SERIAL_CORE help Say Y here if you want to use the Xilinx uartlite serial controller. @@ -1484,6 +1483,20 @@ config SERIAL_RP2_NR_UARTS If multiple cards are present, the default limit of 32 ports may need to be increased. +config SERIAL_FSL_LPUART + tristate "Freescale lpuart serial port support" + select SERIAL_CORE + help + Support for the on-chip lpuart on some Freescale SOCs. + +config SERIAL_FSL_LPUART_CONSOLE + bool "Console on Freescale lpuart serial port" + depends on SERIAL_FSL_LPUART=y + select SERIAL_CORE_CONSOLE + help + If you have enabled the lpuart serial port on the Freescale SoCs, + you can make it the console by answering Y to this option. + endmenu endif # TTY diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index eedfec4..cf650f0 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -85,3 +85,4 @@ obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o obj-$(CONFIG_SERIAL_ARC) += arc_uart.o obj-$(CONFIG_SERIAL_RP2) += rp2.o +obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 13471dd..1d46966 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -604,7 +604,6 @@ static int altera_uart_remove(struct platform_device *pdev) if (port) { uart_remove_one_port(&altera_uart_driver, port); - platform_set_drvdata(pdev, NULL); port->mapbase = 0; } diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index e2774f9..ad41319 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -79,13 +79,12 @@ struct vendor_data { bool dma_threshold; bool cts_event_workaround; - unsigned int (*get_fifosize)(unsigned int periphid); + unsigned int (*get_fifosize)(struct amba_device *dev); }; -static unsigned int get_fifosize_arm(unsigned int periphid) +static unsigned int get_fifosize_arm(struct amba_device *dev) { - unsigned int rev = (periphid >> 20) & 0xf; - return rev < 3 ? 16 : 32; + return amba_rev(dev) < 3 ? 16 : 32; } static struct vendor_data vendor_arm = { @@ -98,7 +97,7 @@ static struct vendor_data vendor_arm = { .get_fifosize = get_fifosize_arm, }; -static unsigned int get_fifosize_st(unsigned int periphid) +static unsigned int get_fifosize_st(struct amba_device *dev) { return 64; } @@ -2157,7 +2156,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->lcrh_rx = vendor->lcrh_rx; uap->lcrh_tx = vendor->lcrh_tx; uap->old_cr = 0; - uap->fifosize = vendor->get_fifosize(dev->periphid); + uap->fifosize = vendor->get_fifosize(dev); uap->port.dev = &dev->dev; uap->port.mapbase = dev->res.start; uap->port.membase = base; diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 3467462..691265f 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1100,7 +1100,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state, * Enable the peripheral clock for this serial port. * This is called on uart_open() or a resume event. */ - clk_enable(atmel_port->clk); + clk_prepare_enable(atmel_port->clk); /* re-enable interrupts if we disabled some on suspend */ UART_PUT_IER(port, atmel_port->backup_imr); @@ -1114,7 +1114,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state, * Disable the peripheral clock for this serial port. * This is called on uart_close() or a suspend event. */ - clk_disable(atmel_port->clk); + clk_disable_unprepare(atmel_port->clk); break; default: printk(KERN_ERR "atmel_serial: unknown pm %d\n", state); @@ -1458,9 +1458,10 @@ static void atmel_of_init_port(struct atmel_uart_port *atmel_port, /* * Configure the port from the platform device resource info. */ -static void atmel_init_port(struct atmel_uart_port *atmel_port, +static int atmel_init_port(struct atmel_uart_port *atmel_port, struct platform_device *pdev) { + int ret; struct uart_port *port = &atmel_port->uart; struct atmel_uart_data *pdata = pdev->dev.platform_data; @@ -1496,9 +1497,19 @@ static void atmel_init_port(struct atmel_uart_port *atmel_port, /* for console, the clock could already be configured */ if (!atmel_port->clk) { atmel_port->clk = clk_get(&pdev->dev, "usart"); - clk_enable(atmel_port->clk); + if (IS_ERR(atmel_port->clk)) { + ret = PTR_ERR(atmel_port->clk); + atmel_port->clk = NULL; + return ret; + } + ret = clk_prepare_enable(atmel_port->clk); + if (ret) { + clk_put(atmel_port->clk); + atmel_port->clk = NULL; + return ret; + } port->uartclk = clk_get_rate(atmel_port->clk); - clk_disable(atmel_port->clk); + clk_disable_unprepare(atmel_port->clk); /* only enable clock when USART is in use */ } @@ -1511,6 +1522,8 @@ static void atmel_init_port(struct atmel_uart_port *atmel_port, } else { atmel_port->tx_done_mask = ATMEL_US_TXRDY; } + + return 0; } struct platform_device *atmel_default_console_device; /* the serial console device */ @@ -1601,6 +1614,7 @@ static void __init atmel_console_get_options(struct uart_port *port, int *baud, static int __init atmel_console_setup(struct console *co, char *options) { + int ret; struct uart_port *port = &atmel_ports[co->index].uart; int baud = 115200; int bits = 8; @@ -1612,7 +1626,9 @@ static int __init atmel_console_setup(struct console *co, char *options) return -ENODEV; } - clk_enable(atmel_ports[co->index].clk); + ret = clk_prepare_enable(atmel_ports[co->index].clk); + if (ret) + return ret; UART_PUT_IDR(port, -1); UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); @@ -1645,6 +1661,7 @@ static struct console atmel_console = { */ static int __init atmel_console_init(void) { + int ret; if (atmel_default_console_device) { struct atmel_uart_data *pdata = atmel_default_console_device->dev.platform_data; @@ -1655,7 +1672,9 @@ static int __init atmel_console_init(void) port->uart.line = id; add_preferred_console(ATMEL_DEVICENAME, id, NULL); - atmel_init_port(port, atmel_default_console_device); + ret = atmel_init_port(port, atmel_default_console_device); + if (ret) + return ret; register_console(&atmel_console); } @@ -1786,7 +1805,9 @@ static int atmel_serial_probe(struct platform_device *pdev) port->backup_imr = 0; port->uart.line = ret; - atmel_init_port(port, pdev); + ret = atmel_init_port(port, pdev); + if (ret) + goto err; pinctrl = devm_pinctrl_get_select_default(&pdev->dev); if (IS_ERR(pinctrl)) { @@ -1812,9 +1833,9 @@ static int atmel_serial_probe(struct platform_device *pdev) && ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) { /* * The serial core enabled the clock for us, so undo - * the clk_enable() in atmel_console_setup() + * the clk_prepare_enable() in atmel_console_setup() */ - clk_disable(port->clk); + clk_disable_unprepare(port->clk); } #endif diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index 97f4e18..f7672ca 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -1384,7 +1384,7 @@ static int cpm_uart_probe(struct platform_device *ofdev) if (index >= UART_NR) return -ENODEV; - dev_set_drvdata(&ofdev->dev, pinfo); + platform_set_drvdata(ofdev, pinfo); /* initialize the device pointer for the port */ pinfo->port.dev = &ofdev->dev; @@ -1398,7 +1398,7 @@ static int cpm_uart_probe(struct platform_device *ofdev) static int cpm_uart_remove(struct platform_device *ofdev) { - struct uart_cpm_port *pinfo = dev_get_drvdata(&ofdev->dev); + struct uart_cpm_port *pinfo = platform_get_drvdata(ofdev); return uart_remove_one_port(&cpm_reg, &pinfo->port); } diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c new file mode 100644 index 0000000..263cfaa --- /dev/null +++ b/drivers/tty/serial/fsl_lpuart.c @@ -0,0 +1,874 @@ +/* + * Freescale lpuart serial port driver + * + * Copyright 2012-2013 Freescale Semiconductor, Inc. + * + * 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. + */ + +#if defined(CONFIG_SERIAL_FSL_LPUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/module.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/console.h> +#include <linux/serial_core.h> +#include <linux/tty_flip.h> + +/* All registers are 8-bit width */ +#define UARTBDH 0x00 +#define UARTBDL 0x01 +#define UARTCR1 0x02 +#define UARTCR2 0x03 +#define UARTSR1 0x04 +#define UARTCR3 0x06 +#define UARTDR 0x07 +#define UARTCR4 0x0a +#define UARTCR5 0x0b +#define UARTMODEM 0x0d +#define UARTPFIFO 0x10 +#define UARTCFIFO 0x11 +#define UARTSFIFO 0x12 +#define UARTTWFIFO 0x13 +#define UARTTCFIFO 0x14 +#define UARTRWFIFO 0x15 + +#define UARTBDH_LBKDIE 0x80 +#define UARTBDH_RXEDGIE 0x40 +#define UARTBDH_SBR_MASK 0x1f + +#define UARTCR1_LOOPS 0x80 +#define UARTCR1_RSRC 0x20 +#define UARTCR1_M 0x10 +#define UARTCR1_WAKE 0x08 +#define UARTCR1_ILT 0x04 +#define UARTCR1_PE 0x02 +#define UARTCR1_PT 0x01 + +#define UARTCR2_TIE 0x80 +#define UARTCR2_TCIE 0x40 +#define UARTCR2_RIE 0x20 +#define UARTCR2_ILIE 0x10 +#define UARTCR2_TE 0x08 +#define UARTCR2_RE 0x04 +#define UARTCR2_RWU 0x02 +#define UARTCR2_SBK 0x01 + +#define UARTSR1_TDRE 0x80 +#define UARTSR1_TC 0x40 +#define UARTSR1_RDRF 0x20 +#define UARTSR1_IDLE 0x10 +#define UARTSR1_OR 0x08 +#define UARTSR1_NF 0x04 +#define UARTSR1_FE 0x02 +#define UARTSR1_PE 0x01 + +#define UARTCR3_R8 0x80 +#define UARTCR3_T8 0x40 +#define UARTCR3_TXDIR 0x20 +#define UARTCR3_TXINV 0x10 +#define UARTCR3_ORIE 0x08 +#define UARTCR3_NEIE 0x04 +#define UARTCR3_FEIE 0x02 +#define UARTCR3_PEIE 0x01 + +#define UARTCR4_MAEN1 0x80 +#define UARTCR4_MAEN2 0x40 +#define UARTCR4_M10 0x20 +#define UARTCR4_BRFA_MASK 0x1f +#define UARTCR4_BRFA_OFF 0 + +#define UARTCR5_TDMAS 0x80 +#define UARTCR5_RDMAS 0x20 + +#define UARTMODEM_RXRTSE 0x08 +#define UARTMODEM_TXRTSPOL 0x04 +#define UARTMODEM_TXRTSE 0x02 +#define UARTMODEM_TXCTSE 0x01 + +#define UARTPFIFO_TXFE 0x80 +#define UARTPFIFO_FIFOSIZE_MASK 0x7 +#define UARTPFIFO_TXSIZE_OFF 4 +#define UARTPFIFO_RXFE 0x08 +#define UARTPFIFO_RXSIZE_OFF 0 + +#define UARTCFIFO_TXFLUSH 0x80 +#define UARTCFIFO_RXFLUSH 0x40 +#define UARTCFIFO_RXOFE 0x04 +#define UARTCFIFO_TXOFE 0x02 +#define UARTCFIFO_RXUFE 0x01 + +#define UARTSFIFO_TXEMPT 0x80 +#define UARTSFIFO_RXEMPT 0x40 +#define UARTSFIFO_RXOF 0x04 +#define UARTSFIFO_TXOF 0x02 +#define UARTSFIFO_RXUF 0x01 + +#define DRIVER_NAME "fsl-lpuart" +#define DEV_NAME "ttyLP" +#define UART_NR 6 + +struct lpuart_port { + struct uart_port port; + struct clk *clk; + unsigned int txfifo_size; + unsigned int rxfifo_size; +}; + +static struct of_device_id lpuart_dt_ids[] = { + { + .compatible = "fsl,vf610-lpuart", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, lpuart_dt_ids); + +static void lpuart_stop_tx(struct uart_port *port) +{ + unsigned char temp; + + temp = readb(port->membase + UARTCR2); + temp &= ~(UARTCR2_TIE | UARTCR2_TCIE); + writeb(temp, port->membase + UARTCR2); +} + +static void lpuart_stop_rx(struct uart_port *port) +{ + unsigned char temp; + + temp = readb(port->membase + UARTCR2); + writeb(temp & ~UARTCR2_RE, port->membase + UARTCR2); +} + +static void lpuart_enable_ms(struct uart_port *port) +{ +} + +static inline void lpuart_transmit_buffer(struct lpuart_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + + while (!uart_circ_empty(xmit) && + (readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size)) { + writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + lpuart_stop_tx(&sport->port); +} + +static void lpuart_start_tx(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + unsigned char temp; + + temp = readb(port->membase + UARTCR2); + writeb(temp | UARTCR2_TIE, port->membase + UARTCR2); + + if (readb(port->membase + UARTSR1) & UARTSR1_TDRE) + lpuart_transmit_buffer(sport); +} + +static irqreturn_t lpuart_txint(int irq, void *dev_id) +{ + struct lpuart_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) { + writeb(sport->port.x_char, sport->port.membase + UARTDR); + goto out; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + lpuart_stop_tx(&sport->port); + goto out; + } + + lpuart_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; +} + +static irqreturn_t lpuart_rxint(int irq, void *dev_id) +{ + struct lpuart_port *sport = dev_id; + unsigned int flg, ignored = 0; + struct tty_port *port = &sport->port.state->port; + unsigned long flags; + unsigned char rx, sr; + + spin_lock_irqsave(&sport->port.lock, flags); + + while (!(readb(sport->port.membase + UARTSFIFO) & UARTSFIFO_RXEMPT)) { + flg = TTY_NORMAL; + sport->port.icount.rx++; + /* + * to clear the FE, OR, NF, FE, PE flags, + * read SR1 then read DR + */ + sr = readb(sport->port.membase + UARTSR1); + rx = readb(sport->port.membase + UARTDR); + + if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) + continue; + + if (sr & (UARTSR1_PE | UARTSR1_OR | UARTSR1_FE)) { + if (sr & UARTSR1_PE) + sport->port.icount.parity++; + else if (sr & UARTSR1_FE) + sport->port.icount.frame++; + + if (sr & UARTSR1_OR) + sport->port.icount.overrun++; + + if (sr & sport->port.ignore_status_mask) { + if (++ignored > 100) + goto out; + continue; + } + + sr &= sport->port.read_status_mask; + + if (sr & UARTSR1_PE) + flg = TTY_PARITY; + else if (sr & UARTSR1_FE) + flg = TTY_FRAME; + + if (sr & UARTSR1_OR) + flg = TTY_OVERRUN; + +#ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; +#endif + } + + tty_insert_flip_char(port, rx, flg); + } + +out: + spin_unlock_irqrestore(&sport->port.lock, flags); + + tty_flip_buffer_push(port); + return IRQ_HANDLED; +} + +static irqreturn_t lpuart_int(int irq, void *dev_id) +{ + struct lpuart_port *sport = dev_id; + unsigned char sts; + + sts = readb(sport->port.membase + UARTSR1); + + if (sts & UARTSR1_RDRF) + lpuart_rxint(irq, dev_id); + + if (sts & UARTSR1_TDRE && + !(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS)) + lpuart_txint(irq, dev_id); + + return IRQ_HANDLED; +} + +/* return TIOCSER_TEMT when transmitter is not busy */ +static unsigned int lpuart_tx_empty(struct uart_port *port) +{ + return (readb(port->membase + UARTSR1) & UARTSR1_TC) ? + TIOCSER_TEMT : 0; +} + +static unsigned int lpuart_get_mctrl(struct uart_port *port) +{ + unsigned int temp = 0; + unsigned char reg; + + reg = readb(port->membase + UARTMODEM); + if (reg & UARTMODEM_TXCTSE) + temp |= TIOCM_CTS; + + if (reg & UARTMODEM_RXRTSE) + temp |= TIOCM_RTS; + + return temp; +} + +static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned char temp; + + temp = readb(port->membase + UARTMODEM) & + ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); + + if (mctrl & TIOCM_RTS) + temp |= UARTMODEM_RXRTSE; + + if (mctrl & TIOCM_CTS) + temp |= UARTMODEM_TXCTSE; + + writeb(temp, port->membase + UARTMODEM); +} + +static void lpuart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned char temp; + + temp = readb(port->membase + UARTCR2) & ~UARTCR2_SBK; + + if (break_state != 0) + temp |= UARTCR2_SBK; + + writeb(temp, port->membase + UARTCR2); +} + +static void lpuart_setup_watermark(struct lpuart_port *sport) +{ + unsigned char val, cr2; + + cr2 = readb(sport->port.membase + UARTCR2); + cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_TE | + UARTCR2_RIE | UARTCR2_RE); + writeb(cr2, sport->port.membase + UARTCR2); + + /* determine FIFO size and enable FIFO mode */ + val = readb(sport->port.membase + UARTPFIFO); + + sport->txfifo_size = 0x1 << (((val >> UARTPFIFO_TXSIZE_OFF) & + UARTPFIFO_FIFOSIZE_MASK) + 1); + + sport->rxfifo_size = 0x1 << (((val >> UARTPFIFO_RXSIZE_OFF) & + UARTPFIFO_FIFOSIZE_MASK) + 1); + + writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE, + sport->port.membase + UARTPFIFO); + + /* flush Tx and Rx FIFO */ + writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH, + sport->port.membase + UARTCFIFO); + + writeb(2, sport->port.membase + UARTTWFIFO); + writeb(1, sport->port.membase + UARTRWFIFO); +} + +static int lpuart_startup(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + int ret; + unsigned long flags; + unsigned char temp; + + ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0, + DRIVER_NAME, sport); + if (ret) + return ret; + + spin_lock_irqsave(&sport->port.lock, flags); + + lpuart_setup_watermark(sport); + + temp = readb(sport->port.membase + UARTCR2); + temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE); + writeb(temp, sport->port.membase + UARTCR2); + + spin_unlock_irqrestore(&sport->port.lock, flags); + return 0; +} + +static void lpuart_shutdown(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + unsigned char temp; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* disable Rx/Tx and interrupts */ + temp = readb(port->membase + UARTCR2); + temp &= ~(UARTCR2_TE | UARTCR2_RE | + UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE); + writeb(temp, port->membase + UARTCR2); + + spin_unlock_irqrestore(&port->lock, flags); + + devm_free_irq(port->dev, port->irq, sport); +} + +static void +lpuart_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + unsigned long flags; + unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem; + unsigned int baud; + unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; + unsigned int sbr, brfa; + + cr1 = old_cr1 = readb(sport->port.membase + UARTCR1); + old_cr2 = readb(sport->port.membase + UARTCR2); + cr4 = readb(sport->port.membase + UARTCR4); + bdh = readb(sport->port.membase + UARTBDH); + modem = readb(sport->port.membase + UARTMODEM); + /* + * only support CS8 and CS7, and for CS7 must enable PE. + * supported mode: + * - (7,e/o,1) + * - (8,n,1) + * - (8,m/s,1) + * - (8,e/o,1) + */ + while ((termios->c_cflag & CSIZE) != CS8 && + (termios->c_cflag & CSIZE) != CS7) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + old_csize = CS8; + } + + if ((termios->c_cflag & CSIZE) == CS8 || + (termios->c_cflag & CSIZE) == CS7) + cr1 = old_cr1 & ~UARTCR1_M; + + if (termios->c_cflag & CMSPAR) { + if ((termios->c_cflag & CSIZE) != CS8) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + } + cr1 |= UARTCR1_M; + } + + if (termios->c_cflag & CRTSCTS) { + modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); + } else { + termios->c_cflag &= ~CRTSCTS; + modem &= ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); + } + + if (termios->c_cflag & CSTOPB) + termios->c_cflag &= ~CSTOPB; + + /* parity must be enabled when CS7 to match 8-bits format */ + if ((termios->c_cflag & CSIZE) == CS7) + termios->c_cflag |= PARENB; + + if ((termios->c_cflag & PARENB)) { + if (termios->c_cflag & CMSPAR) { + cr1 &= ~UARTCR1_PE; + cr1 |= UARTCR1_M; + } else { + cr1 |= UARTCR1_PE; + if ((termios->c_cflag & CSIZE) == CS8) + cr1 |= UARTCR1_M; + if (termios->c_cflag & PARODD) + cr1 |= UARTCR1_PT; + else + cr1 &= ~UARTCR1_PT; + } + } + + /* ask the core to calculate the divisor */ + baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); + + spin_lock_irqsave(&sport->port.lock, flags); + + sport->port.read_status_mask = 0; + if (termios->c_iflag & INPCK) + sport->port.read_status_mask |= (UARTSR1_FE | UARTSR1_PE); + if (termios->c_iflag & (BRKINT | PARMRK)) + sport->port.read_status_mask |= UARTSR1_FE; + + /* characters to ignore */ + sport->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= UARTSR1_PE; + if (termios->c_iflag & IGNBRK) { + sport->port.ignore_status_mask |= UARTSR1_FE; + /* + * if we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= UARTSR1_OR; + } + + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* wait transmit engin complete */ + while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC)) + barrier(); + + /* disable transmit and receive */ + writeb(old_cr2 & ~(UARTCR2_TE | UARTCR2_RE), + sport->port.membase + UARTCR2); + + sbr = sport->port.uartclk / (16 * baud); + brfa = ((sport->port.uartclk - (16 * sbr * baud)) * 2) / baud; + bdh &= ~UARTBDH_SBR_MASK; + bdh |= (sbr >> 8) & 0x1F; + cr4 &= ~UARTCR4_BRFA_MASK; + brfa &= UARTCR4_BRFA_MASK; + writeb(cr4 | brfa, sport->port.membase + UARTCR4); + writeb(bdh, sport->port.membase + UARTBDH); + writeb(sbr & 0xFF, sport->port.membase + UARTBDL); + writeb(cr1, sport->port.membase + UARTCR1); + writeb(modem, sport->port.membase + UARTMODEM); + + /* restore control register */ + writeb(old_cr2, sport->port.membase + UARTCR2); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static const char *lpuart_type(struct uart_port *port) +{ + return "FSL_LPUART"; +} + +static void lpuart_release_port(struct uart_port *port) +{ + /* nothing to do */ +} + +static int lpuart_request_port(struct uart_port *port) +{ + return 0; +} + +/* configure/autoconfigure the port */ +static void lpuart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_LPUART; +} + +static int lpuart_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_LPUART) + ret = -EINVAL; + if (port->irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != UPIO_MEM) + ret = -EINVAL; + if (port->uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if (port->iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; +} + +static struct uart_ops lpuart_pops = { + .tx_empty = lpuart_tx_empty, + .set_mctrl = lpuart_set_mctrl, + .get_mctrl = lpuart_get_mctrl, + .stop_tx = lpuart_stop_tx, + .start_tx = lpuart_start_tx, + .stop_rx = lpuart_stop_rx, + .enable_ms = lpuart_enable_ms, + .break_ctl = lpuart_break_ctl, + .startup = lpuart_startup, + .shutdown = lpuart_shutdown, + .set_termios = lpuart_set_termios, + .type = lpuart_type, + .request_port = lpuart_request_port, + .release_port = lpuart_release_port, + .config_port = lpuart_config_port, + .verify_port = lpuart_verify_port, +}; + +static struct lpuart_port *lpuart_ports[UART_NR]; + +#ifdef CONFIG_SERIAL_FSL_LPUART_CONSOLE +static void lpuart_console_putchar(struct uart_port *port, int ch) +{ + while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE)) + barrier(); + + writeb(ch, port->membase + UARTDR); +} + +static void +lpuart_console_write(struct console *co, const char *s, unsigned int count) +{ + struct lpuart_port *sport = lpuart_ports[co->index]; + unsigned char old_cr2, cr2; + + /* first save CR2 and then disable interrupts */ + cr2 = old_cr2 = readb(sport->port.membase + UARTCR2); + cr2 |= (UARTCR2_TE | UARTCR2_RE); + cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE); + writeb(cr2, sport->port.membase + UARTCR2); + + uart_console_write(&sport->port, s, count, lpuart_console_putchar); + + /* wait for transmitter finish complete and restore CR2 */ + while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC)) + barrier(); + + writeb(old_cr2, sport->port.membase + UARTCR2); +} + +/* + * if the port was already initialised (eg, by a boot loader), + * try to determine the current setup. + */ +static void __init +lpuart_console_get_options(struct lpuart_port *sport, int *baud, + int *parity, int *bits) +{ + unsigned char cr, bdh, bdl, brfa; + unsigned int sbr, uartclk, baud_raw; + + cr = readb(sport->port.membase + UARTCR2); + cr &= UARTCR2_TE | UARTCR2_RE; + if (!cr) + return; + + /* ok, the port was enabled */ + + cr = readb(sport->port.membase + UARTCR1); + + *parity = 'n'; + if (cr & UARTCR1_PE) { + if (cr & UARTCR1_PT) + *parity = 'o'; + else + *parity = 'e'; + } + + if (cr & UARTCR1_M) + *bits = 9; + else + *bits = 8; + + bdh = readb(sport->port.membase + UARTBDH); + bdh &= UARTBDH_SBR_MASK; + bdl = readb(sport->port.membase + UARTBDL); + sbr = bdh; + sbr <<= 8; + sbr |= bdl; + brfa = readb(sport->port.membase + UARTCR4); + brfa &= UARTCR4_BRFA_MASK; + + uartclk = clk_get_rate(sport->clk); + /* + * baud = mod_clk/(16*(sbr[13]+(brfa)/32) + */ + baud_raw = uartclk / (16 * (sbr + brfa / 32)); + + if (*baud != baud_raw) + printk(KERN_INFO "Serial: Console lpuart rounded baud rate" + "from %d to %d\n", baud_raw, *baud); +} + +static int __init lpuart_console_setup(struct console *co, char *options) +{ + struct lpuart_port *sport; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index == -1 || co->index >= ARRAY_SIZE(lpuart_ports)) + co->index = 0; + + sport = lpuart_ports[co->index]; + if (sport == NULL) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + lpuart_console_get_options(sport, &baud, &parity, &bits); + + lpuart_setup_watermark(sport); + + return uart_set_options(&sport->port, co, baud, parity, bits, flow); +} + +static struct uart_driver lpuart_reg; +static struct console lpuart_console = { + .name = DEV_NAME, + .write = lpuart_console_write, + .device = uart_console_device, + .setup = lpuart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &lpuart_reg, +}; + +#define LPUART_CONSOLE (&lpuart_console) +#else +#define LPUART_CONSOLE NULL +#endif + +static struct uart_driver lpuart_reg = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = DEV_NAME, + .nr = ARRAY_SIZE(lpuart_ports), + .cons = LPUART_CONSOLE, +}; + +static int lpuart_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct lpuart_port *sport; + struct resource *res; + int ret; + + sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); + if (!sport) + return -ENOMEM; + + pdev->dev.coherent_dma_mask = 0; + + ret = of_alias_get_id(np, "serial"); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); + return ret; + } + sport->port.line = ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + sport->port.mapbase = res->start; + sport->port.membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sport->port.membase)) + return PTR_ERR(sport->port.membase); + + sport->port.dev = &pdev->dev; + sport->port.type = PORT_LPUART; + sport->port.iotype = UPIO_MEM; + sport->port.irq = platform_get_irq(pdev, 0); + sport->port.ops = &lpuart_pops; + sport->port.flags = UPF_BOOT_AUTOCONF; + + sport->clk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(sport->clk)) { + ret = PTR_ERR(sport->clk); + dev_err(&pdev->dev, "failed to get uart clk: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(sport->clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable uart clk: %d\n", ret); + return ret; + } + + sport->port.uartclk = clk_get_rate(sport->clk); + + lpuart_ports[sport->port.line] = sport; + + platform_set_drvdata(pdev, &sport->port); + + ret = uart_add_one_port(&lpuart_reg, &sport->port); + if (ret) { + clk_disable_unprepare(sport->clk); + return ret; + } + + return 0; +} + +static int lpuart_remove(struct platform_device *pdev) +{ + struct lpuart_port *sport = platform_get_drvdata(pdev); + + uart_remove_one_port(&lpuart_reg, &sport->port); + + clk_disable_unprepare(sport->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int lpuart_suspend(struct device *dev) +{ + struct lpuart_port *sport = dev_get_drvdata(dev); + + uart_suspend_port(&lpuart_reg, &sport->port); + + return 0; +} + +static int lpuart_resume(struct device *dev) +{ + struct lpuart_port *sport = dev_get_drvdata(dev); + + uart_resume_port(&lpuart_reg, &sport->port); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(lpuart_pm_ops, lpuart_suspend, lpuart_resume); + +static struct platform_driver lpuart_driver = { + .probe = lpuart_probe, + .remove = lpuart_remove, + .driver = { + .name = "fsl-lpuart", + .owner = THIS_MODULE, + .of_match_table = lpuart_dt_ids, + .pm = &lpuart_pm_ops, + }, +}; + +static int __init lpuart_serial_init(void) +{ + int ret; + + pr_info("serial: Freescale lpuart driver\n"); + + ret = uart_register_driver(&lpuart_reg); + if (ret) + return ret; + + ret = platform_driver_register(&lpuart_driver); + if (ret) + uart_unregister_driver(&lpuart_reg); + + return 0; +} + +static void __exit lpuart_serial_exit(void) +{ + platform_driver_unregister(&lpuart_driver); + uart_unregister_driver(&lpuart_reg); +} + +module_init(lpuart_serial_init); +module_exit(lpuart_serial_exit); + +MODULE_DESCRIPTION("Freescale lpuart serial port driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 8cdfbd3..415cec6 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -201,6 +201,7 @@ struct imx_port { unsigned int old_status; int txirq, rxirq, rtsirq; unsigned int have_rtscts:1; + unsigned int dte_mode:1; unsigned int use_irda:1; unsigned int irda_inv_rx:1; unsigned int irda_inv_tx:1; @@ -271,6 +272,7 @@ static inline int is_imx21_uart(struct imx_port *sport) /* * Save and restore functions for UCR1, UCR2 and UCR3 registers */ +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_IMX_CONSOLE) static void imx_port_ucrs_save(struct uart_port *port, struct imx_port_ucrs *ucr) { @@ -288,6 +290,7 @@ static void imx_port_ucrs_restore(struct uart_port *port, writel(ucr->ucr2, port->membase + UCR2); writel(ucr->ucr3, port->membase + UCR3); } +#endif /* * Handle any change of modem status signal since we were last called. @@ -449,6 +452,13 @@ static void imx_start_tx(struct uart_port *port) temp &= ~(UCR1_RRDYEN); writel(temp, sport->port.membase + UCR1); } + /* Clear any pending ORE flag before enabling interrupt */ + temp = readl(sport->port.membase + USR2); + writel(temp | USR2_ORE, sport->port.membase + USR2); + + temp = readl(sport->port.membase + UCR4); + temp |= UCR4_OREN; + writel(temp, sport->port.membase + UCR4); temp = readl(sport->port.membase + UCR1); writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1); @@ -582,6 +592,7 @@ static irqreturn_t imx_int(int irq, void *dev_id) { struct imx_port *sport = dev_id; unsigned int sts; + unsigned int sts2; sts = readl(sport->port.membase + USR1); @@ -598,6 +609,13 @@ static irqreturn_t imx_int(int irq, void *dev_id) if (sts & USR1_AWAKE) writel(USR1_AWAKE, sport->port.membase + USR1); + sts2 = readl(sport->port.membase + USR2); + if (sts2 & USR2_ORE) { + dev_err(sport->port.dev, "Rx FIFO overrun\n"); + sport->port.icount.overrun++; + writel(sts2 | USR2_ORE, sport->port.membase + USR2); + } + return IRQ_HANDLED; } @@ -684,6 +702,17 @@ static int imx_startup(struct uart_port *port) int retval; unsigned long flags, temp; + if (!uart_console(port)) { + retval = clk_prepare_enable(sport->clk_per); + if (retval) + goto error_out1; + retval = clk_prepare_enable(sport->clk_ipg); + if (retval) { + clk_disable_unprepare(sport->clk_per); + goto error_out1; + } + } + imx_setup_ufcr(sport, 0); /* disable the DREN bit (Data Ready interrupt enable) before @@ -871,6 +900,11 @@ static void imx_shutdown(struct uart_port *port) writel(temp, sport->port.membase + UCR1); spin_unlock_irqrestore(&sport->port.lock, flags); + + if (!uart_console(&sport->port)) { + clk_disable_unprepare(sport->clk_per); + clk_disable_unprepare(sport->clk_ipg); + } } static void @@ -1007,6 +1041,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, ufcr = readl(sport->port.membase + UFCR); ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div); + if (sport->dte_mode) + ufcr |= UFCR_DCEDTE; writel(ufcr, sport->port.membase + UFCR); writel(num, sport->port.membase + UBIR); @@ -1431,6 +1467,9 @@ static int serial_imx_probe_dt(struct imx_port *sport, if (of_get_property(np, "fsl,irda-mode", NULL)) sport->use_irda = 1; + if (of_get_property(np, "fsl,dte-mode", NULL)) + sport->dte_mode = 1; + sport->devdata = of_id->data; return 0; @@ -1544,6 +1583,11 @@ static int serial_imx_probe(struct platform_device *pdev) goto deinit; platform_set_drvdata(pdev, sport); + if (!uart_console(&sport->port)) { + clk_disable_unprepare(sport->clk_per); + clk_disable_unprepare(sport->clk_ipg); + } + return 0; deinit: if (pdata && pdata->exit) @@ -1565,9 +1609,6 @@ static int serial_imx_remove(struct platform_device *pdev) uart_remove_one_port(&imx_reg, &sport->port); - clk_disable_unprepare(sport->clk_per); - clk_disable_unprepare(sport->clk_ipg); - if (pdata && pdata->exit) pdata->exit(pdev); diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index 5f4765a..e266eca 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -21,6 +21,10 @@ * be triggered */ +#if defined(CONFIG_SERIAL_MFD_HSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + #include <linux/module.h> #include <linux/init.h> #include <linux/console.h> diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index f51b280..e1280a2 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -84,16 +84,6 @@ static void mpc52xx_uart_of_enumerate(void); static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id); static irqreturn_t mpc5xxx_uart_process_int(struct uart_port *port); - -/* Simple macro to test if a port is console or not. This one is taken - * for serial_core.c and maybe should be moved to serial_core.h ? */ -#ifdef CONFIG_SERIAL_CORE_CONSOLE -#define uart_console(port) \ - ((port)->cons && (port)->cons->index == (port)->line) -#else -#define uart_console(port) (0) -#endif - /* ======================================================================== */ /* PSC fifo operations for isolating differences between 52xx and 512x */ /* ======================================================================== */ @@ -122,6 +112,15 @@ struct psc_ops { void (*fifoc_uninit)(void); void (*get_irq)(struct uart_port *, struct device_node *); irqreturn_t (*handle_irq)(struct uart_port *port); + u16 (*get_status)(struct uart_port *port); + u8 (*get_ipcr)(struct uart_port *port); + void (*command)(struct uart_port *port, u8 cmd); + void (*set_mode)(struct uart_port *port, u8 mr1, u8 mr2); + void (*set_rts)(struct uart_port *port, int state); + void (*enable_ms)(struct uart_port *port); + void (*set_sicr)(struct uart_port *port, u32 val); + void (*set_imr)(struct uart_port *port, u16 val); + u8 (*get_mr1)(struct uart_port *port); }; /* setting the prescaler and divisor reg is common for all chips */ @@ -134,6 +133,65 @@ static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc, out_8(&psc->ctlr, divisor & 0xff); } +static u16 mpc52xx_psc_get_status(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status); +} + +static u8 mpc52xx_psc_get_ipcr(struct uart_port *port) +{ + return in_8(&PSC(port)->mpc52xx_psc_ipcr); +} + +static void mpc52xx_psc_command(struct uart_port *port, u8 cmd) +{ + out_8(&PSC(port)->command, cmd); +} + +static void mpc52xx_psc_set_mode(struct uart_port *port, u8 mr1, u8 mr2) +{ + out_8(&PSC(port)->command, MPC52xx_PSC_SEL_MODE_REG_1); + out_8(&PSC(port)->mode, mr1); + out_8(&PSC(port)->mode, mr2); +} + +static void mpc52xx_psc_set_rts(struct uart_port *port, int state) +{ + if (state) + out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS); + else + out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS); +} + +static void mpc52xx_psc_enable_ms(struct uart_port *port) +{ + struct mpc52xx_psc __iomem *psc = PSC(port); + + /* clear D_*-bits by reading them */ + in_8(&psc->mpc52xx_psc_ipcr); + /* enable CTS and DCD as IPC interrupts */ + out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD); + + port->read_status_mask |= MPC52xx_PSC_IMR_IPC; + out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_set_sicr(struct uart_port *port, u32 val) +{ + out_be32(&PSC(port)->sicr, val); +} + +static void mpc52xx_psc_set_imr(struct uart_port *port, u16 val) +{ + out_be16(&PSC(port)->mpc52xx_psc_imr, val); +} + +static u8 mpc52xx_psc_get_mr1(struct uart_port *port) +{ + out_8(&PSC(port)->command, MPC52xx_PSC_SEL_MODE_REG_1); + return in_8(&PSC(port)->mode); +} + #ifdef CONFIG_PPC_MPC52xx #define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) static void mpc52xx_psc_fifo_init(struct uart_port *port) @@ -304,6 +362,15 @@ static struct psc_ops mpc52xx_psc_ops = { .set_baudrate = mpc5200_psc_set_baudrate, .get_irq = mpc52xx_psc_get_irq, .handle_irq = mpc52xx_psc_handle_irq, + .get_status = mpc52xx_psc_get_status, + .get_ipcr = mpc52xx_psc_get_ipcr, + .command = mpc52xx_psc_command, + .set_mode = mpc52xx_psc_set_mode, + .set_rts = mpc52xx_psc_set_rts, + .enable_ms = mpc52xx_psc_enable_ms, + .set_sicr = mpc52xx_psc_set_sicr, + .set_imr = mpc52xx_psc_set_imr, + .get_mr1 = mpc52xx_psc_get_mr1, }; static struct psc_ops mpc5200b_psc_ops = { @@ -325,6 +392,15 @@ static struct psc_ops mpc5200b_psc_ops = { .set_baudrate = mpc5200b_psc_set_baudrate, .get_irq = mpc52xx_psc_get_irq, .handle_irq = mpc52xx_psc_handle_irq, + .get_status = mpc52xx_psc_get_status, + .get_ipcr = mpc52xx_psc_get_ipcr, + .command = mpc52xx_psc_command, + .set_mode = mpc52xx_psc_set_mode, + .set_rts = mpc52xx_psc_set_rts, + .enable_ms = mpc52xx_psc_enable_ms, + .set_sicr = mpc52xx_psc_set_sicr, + .set_imr = mpc52xx_psc_set_imr, + .get_mr1 = mpc52xx_psc_get_mr1, }; #endif /* CONFIG_MPC52xx */ @@ -572,6 +648,246 @@ static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np) port->irqflags = IRQF_SHARED; port->irq = psc_fifoc_irq; } +#endif + +#ifdef CONFIG_PPC_MPC512x + +#define PSC_5125(port) ((struct mpc5125_psc __iomem *)((port)->membase)) +#define FIFO_5125(port) ((struct mpc512x_psc_fifo __iomem *)(PSC_5125(port)+1)) + +static void mpc5125_psc_fifo_init(struct uart_port *port) +{ + /* /32 prescaler */ + out_8(&PSC_5125(port)->mpc52xx_psc_clock_select, 0xdd); + + out_be32(&FIFO_5125(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_5125(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_5125(port)->txalarm, 1); + out_be32(&FIFO_5125(port)->tximr, 0); + + out_be32(&FIFO_5125(port)->rxcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_5125(port)->rxcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_5125(port)->rxalarm, 1); + out_be32(&FIFO_5125(port)->rximr, 0); + + out_be32(&FIFO_5125(port)->tximr, MPC512x_PSC_FIFO_ALARM); + out_be32(&FIFO_5125(port)->rximr, MPC512x_PSC_FIFO_ALARM); +} + +static int mpc5125_psc_raw_rx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_5125(port)->rxsr) & MPC512x_PSC_FIFO_EMPTY); +} + +static int mpc5125_psc_raw_tx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_5125(port)->txsr) & MPC512x_PSC_FIFO_FULL); +} + +static int mpc5125_psc_rx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_5125(port)->rxsr) & + in_be32(&FIFO_5125(port)->rximr) & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc5125_psc_tx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_5125(port)->txsr) & + in_be32(&FIFO_5125(port)->tximr) & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc5125_psc_tx_empty(struct uart_port *port) +{ + return in_be32(&FIFO_5125(port)->txsr) & MPC512x_PSC_FIFO_EMPTY; +} + +static void mpc5125_psc_stop_rx(struct uart_port *port) +{ + unsigned long rx_fifo_imr; + + rx_fifo_imr = in_be32(&FIFO_5125(port)->rximr); + rx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_5125(port)->rximr, rx_fifo_imr); +} + +static void mpc5125_psc_start_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_5125(port)->tximr); + tx_fifo_imr |= MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_5125(port)->tximr, tx_fifo_imr); +} + +static void mpc5125_psc_stop_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_5125(port)->tximr); + tx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_5125(port)->tximr, tx_fifo_imr); +} + +static void mpc5125_psc_rx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_5125(port)->rxisr, in_be32(&FIFO_5125(port)->rxisr)); +} + +static void mpc5125_psc_tx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_5125(port)->txisr, in_be32(&FIFO_5125(port)->txisr)); +} + +static void mpc5125_psc_write_char(struct uart_port *port, unsigned char c) +{ + out_8(&FIFO_5125(port)->txdata_8, c); +} + +static unsigned char mpc5125_psc_read_char(struct uart_port *port) +{ + return in_8(&FIFO_5125(port)->rxdata_8); +} + +static void mpc5125_psc_cw_disable_ints(struct uart_port *port) +{ + port->read_status_mask = + in_be32(&FIFO_5125(port)->tximr) << 16 | + in_be32(&FIFO_5125(port)->rximr); + out_be32(&FIFO_5125(port)->tximr, 0); + out_be32(&FIFO_5125(port)->rximr, 0); +} + +static void mpc5125_psc_cw_restore_ints(struct uart_port *port) +{ + out_be32(&FIFO_5125(port)->tximr, + (port->read_status_mask >> 16) & 0x7f); + out_be32(&FIFO_5125(port)->rximr, port->read_status_mask & 0x7f); +} + +static inline void mpc5125_set_divisor(struct mpc5125_psc __iomem *psc, + u8 prescaler, unsigned int divisor) +{ + /* select prescaler */ + out_8(&psc->mpc52xx_psc_clock_select, prescaler); + out_8(&psc->ctur, divisor >> 8); + out_8(&psc->ctlr, divisor & 0xff); +} + +static unsigned int mpc5125_psc_set_baudrate(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) +{ + unsigned int baud; + unsigned int divisor; + + /* + * Calculate with a /16 prescaler here. + */ + + /* uartclk contains the ips freq */ + baud = uart_get_baud_rate(port, new, old, + port->uartclk / (16 * 0xffff) + 1, + port->uartclk / 16); + divisor = (port->uartclk + 8 * baud) / (16 * baud); + + /* enable the /16 prescaler and set the divisor */ + mpc5125_set_divisor(PSC_5125(port), 0xdd, divisor); + return baud; +} + +/* + * MPC5125 have compatible PSC FIFO Controller. + * Special init not needed. + */ +static u16 mpc5125_psc_get_status(struct uart_port *port) +{ + return in_be16(&PSC_5125(port)->mpc52xx_psc_status); +} + +static u8 mpc5125_psc_get_ipcr(struct uart_port *port) +{ + return in_8(&PSC_5125(port)->mpc52xx_psc_ipcr); +} + +static void mpc5125_psc_command(struct uart_port *port, u8 cmd) +{ + out_8(&PSC_5125(port)->command, cmd); +} + +static void mpc5125_psc_set_mode(struct uart_port *port, u8 mr1, u8 mr2) +{ + out_8(&PSC_5125(port)->mr1, mr1); + out_8(&PSC_5125(port)->mr2, mr2); +} + +static void mpc5125_psc_set_rts(struct uart_port *port, int state) +{ + if (state & TIOCM_RTS) + out_8(&PSC_5125(port)->op1, MPC52xx_PSC_OP_RTS); + else + out_8(&PSC_5125(port)->op0, MPC52xx_PSC_OP_RTS); +} + +static void mpc5125_psc_enable_ms(struct uart_port *port) +{ + struct mpc5125_psc __iomem *psc = PSC_5125(port); + + /* clear D_*-bits by reading them */ + in_8(&psc->mpc52xx_psc_ipcr); + /* enable CTS and DCD as IPC interrupts */ + out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD); + + port->read_status_mask |= MPC52xx_PSC_IMR_IPC; + out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc5125_psc_set_sicr(struct uart_port *port, u32 val) +{ + out_be32(&PSC_5125(port)->sicr, val); +} + +static void mpc5125_psc_set_imr(struct uart_port *port, u16 val) +{ + out_be16(&PSC_5125(port)->mpc52xx_psc_imr, val); +} + +static u8 mpc5125_psc_get_mr1(struct uart_port *port) +{ + return in_8(&PSC_5125(port)->mr1); +} + +static struct psc_ops mpc5125_psc_ops = { + .fifo_init = mpc5125_psc_fifo_init, + .raw_rx_rdy = mpc5125_psc_raw_rx_rdy, + .raw_tx_rdy = mpc5125_psc_raw_tx_rdy, + .rx_rdy = mpc5125_psc_rx_rdy, + .tx_rdy = mpc5125_psc_tx_rdy, + .tx_empty = mpc5125_psc_tx_empty, + .stop_rx = mpc5125_psc_stop_rx, + .start_tx = mpc5125_psc_start_tx, + .stop_tx = mpc5125_psc_stop_tx, + .rx_clr_irq = mpc5125_psc_rx_clr_irq, + .tx_clr_irq = mpc5125_psc_tx_clr_irq, + .write_char = mpc5125_psc_write_char, + .read_char = mpc5125_psc_read_char, + .cw_disable_ints = mpc5125_psc_cw_disable_ints, + .cw_restore_ints = mpc5125_psc_cw_restore_ints, + .set_baudrate = mpc5125_psc_set_baudrate, + .clock = mpc512x_psc_clock, + .fifoc_init = mpc512x_psc_fifoc_init, + .fifoc_uninit = mpc512x_psc_fifoc_uninit, + .get_irq = mpc512x_psc_get_irq, + .handle_irq = mpc512x_psc_handle_irq, + .get_status = mpc5125_psc_get_status, + .get_ipcr = mpc5125_psc_get_ipcr, + .command = mpc5125_psc_command, + .set_mode = mpc5125_psc_set_mode, + .set_rts = mpc5125_psc_set_rts, + .enable_ms = mpc5125_psc_enable_ms, + .set_sicr = mpc5125_psc_set_sicr, + .set_imr = mpc5125_psc_set_imr, + .get_mr1 = mpc5125_psc_get_mr1, +}; static struct psc_ops mpc512x_psc_ops = { .fifo_init = mpc512x_psc_fifo_init, @@ -595,8 +911,18 @@ static struct psc_ops mpc512x_psc_ops = { .fifoc_uninit = mpc512x_psc_fifoc_uninit, .get_irq = mpc512x_psc_get_irq, .handle_irq = mpc512x_psc_handle_irq, + .get_status = mpc52xx_psc_get_status, + .get_ipcr = mpc52xx_psc_get_ipcr, + .command = mpc52xx_psc_command, + .set_mode = mpc52xx_psc_set_mode, + .set_rts = mpc52xx_psc_set_rts, + .enable_ms = mpc52xx_psc_enable_ms, + .set_sicr = mpc52xx_psc_set_sicr, + .set_imr = mpc52xx_psc_set_imr, + .get_mr1 = mpc52xx_psc_get_mr1, }; -#endif +#endif /* CONFIG_PPC_MPC512x */ + static const struct psc_ops *psc_ops; @@ -613,17 +939,14 @@ mpc52xx_uart_tx_empty(struct uart_port *port) static void mpc52xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { - if (mctrl & TIOCM_RTS) - out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS); - else - out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS); + psc_ops->set_rts(port, mctrl & TIOCM_RTS); } static unsigned int mpc52xx_uart_get_mctrl(struct uart_port *port) { unsigned int ret = TIOCM_DSR; - u8 status = in_8(&PSC(port)->mpc52xx_psc_ipcr); + u8 status = psc_ops->get_ipcr(port); if (!(status & MPC52xx_PSC_CTS)) ret |= TIOCM_CTS; @@ -673,15 +996,7 @@ mpc52xx_uart_stop_rx(struct uart_port *port) static void mpc52xx_uart_enable_ms(struct uart_port *port) { - struct mpc52xx_psc __iomem *psc = PSC(port); - - /* clear D_*-bits by reading them */ - in_8(&psc->mpc52xx_psc_ipcr); - /* enable CTS and DCD as IPC interrupts */ - out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD); - - port->read_status_mask |= MPC52xx_PSC_IMR_IPC; - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->enable_ms(port); } static void @@ -691,9 +1006,9 @@ mpc52xx_uart_break_ctl(struct uart_port *port, int ctl) spin_lock_irqsave(&port->lock, flags); if (ctl == -1) - out_8(&PSC(port)->command, MPC52xx_PSC_START_BRK); + psc_ops->command(port, MPC52xx_PSC_START_BRK); else - out_8(&PSC(port)->command, MPC52xx_PSC_STOP_BRK); + psc_ops->command(port, MPC52xx_PSC_STOP_BRK); spin_unlock_irqrestore(&port->lock, flags); } @@ -701,7 +1016,6 @@ mpc52xx_uart_break_ctl(struct uart_port *port, int ctl) static int mpc52xx_uart_startup(struct uart_port *port) { - struct mpc52xx_psc __iomem *psc = PSC(port); int ret; if (psc_ops->clock) { @@ -717,15 +1031,15 @@ mpc52xx_uart_startup(struct uart_port *port) return ret; /* Reset/activate the port, clear and enable interrupts */ - out_8(&psc->command, MPC52xx_PSC_RST_RX); - out_8(&psc->command, MPC52xx_PSC_RST_TX); + psc_ops->command(port, MPC52xx_PSC_RST_RX); + psc_ops->command(port, MPC52xx_PSC_RST_TX); - out_be32(&psc->sicr, 0); /* UART mode DCD ignored */ + psc_ops->set_sicr(port, 0); /* UART mode DCD ignored */ psc_ops->fifo_init(port); - out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); - out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); + psc_ops->command(port, MPC52xx_PSC_TX_ENABLE); + psc_ops->command(port, MPC52xx_PSC_RX_ENABLE); return 0; } @@ -733,19 +1047,20 @@ mpc52xx_uart_startup(struct uart_port *port) static void mpc52xx_uart_shutdown(struct uart_port *port) { - struct mpc52xx_psc __iomem *psc = PSC(port); - /* Shut down the port. Leave TX active if on a console port */ - out_8(&psc->command, MPC52xx_PSC_RST_RX); + psc_ops->command(port, MPC52xx_PSC_RST_RX); if (!uart_console(port)) - out_8(&psc->command, MPC52xx_PSC_RST_TX); + psc_ops->command(port, MPC52xx_PSC_RST_TX); port->read_status_mask = 0; - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->set_imr(port, port->read_status_mask); if (psc_ops->clock) psc_ops->clock(port, 0); + /* Disable interrupt */ + psc_ops->cw_disable_ints(port); + /* Release interrupt */ free_irq(port->irq, port); } @@ -754,7 +1069,6 @@ static void mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, struct ktermios *old) { - struct mpc52xx_psc __iomem *psc = PSC(port); unsigned long flags; unsigned char mr1, mr2; unsigned int j; @@ -818,13 +1132,11 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, "Some chars may have been lost.\n"); /* Reset the TX & RX */ - out_8(&psc->command, MPC52xx_PSC_RST_RX); - out_8(&psc->command, MPC52xx_PSC_RST_TX); + psc_ops->command(port, MPC52xx_PSC_RST_RX); + psc_ops->command(port, MPC52xx_PSC_RST_TX); /* Send new mode settings */ - out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); - out_8(&psc->mode, mr1); - out_8(&psc->mode, mr2); + psc_ops->set_mode(port, mr1, mr2); baud = psc_ops->set_baudrate(port, new, old); /* Update the per-port timeout */ @@ -834,8 +1146,8 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, mpc52xx_uart_enable_ms(port); /* Reenable TX & RX */ - out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); - out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); + psc_ops->command(port, MPC52xx_PSC_TX_ENABLE); + psc_ops->command(port, MPC52xx_PSC_RX_ENABLE); /* We're all set, release the lock */ spin_unlock_irqrestore(&port->lock, flags); @@ -963,7 +1275,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) flag = TTY_NORMAL; port->icount.rx++; - status = in_be16(&PSC(port)->mpc52xx_psc_status); + status = psc_ops->get_status(port); if (status & (MPC52xx_PSC_SR_PE | MPC52xx_PSC_SR_FE | @@ -983,7 +1295,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) } /* Clear error condition */ - out_8(&PSC(port)->command, MPC52xx_PSC_RST_ERR_STAT); + psc_ops->command(port, MPC52xx_PSC_RST_ERR_STAT); } tty_insert_flip_char(tport, ch, flag); @@ -1066,7 +1378,7 @@ mpc5xxx_uart_process_int(struct uart_port *port) if (psc_ops->tx_rdy(port)) keepgoing |= mpc52xx_uart_int_tx_chars(port); - status = in_8(&PSC(port)->mpc52xx_psc_ipcr); + status = psc_ops->get_ipcr(port); if (status & MPC52xx_PSC_D_DCD) uart_handle_dcd_change(port, !(status & MPC52xx_PSC_DCD)); @@ -1107,14 +1419,12 @@ static void __init mpc52xx_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits, int *flow) { - struct mpc52xx_psc __iomem *psc = PSC(port); unsigned char mr1; pr_debug("mpc52xx_console_get_options(port=%p)\n", port); /* Read the mode registers */ - out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); - mr1 = in_8(&psc->mode); + mr1 = psc_ops->get_mr1(port); /* CT{U,L}R are write-only ! */ *baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD; @@ -1304,6 +1614,7 @@ static struct of_device_id mpc52xx_uart_of_match[] = { #endif #ifdef CONFIG_PPC_MPC512x { .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, }, + { .compatible = "fsl,mpc5125-psc-uart", .data = &mpc5125_psc_ops, }, #endif {}, }; @@ -1372,15 +1683,14 @@ static int mpc52xx_uart_of_probe(struct platform_device *op) if (ret) return ret; - dev_set_drvdata(&op->dev, (void *)port); + platform_set_drvdata(op, (void *)port); return 0; } static int mpc52xx_uart_of_remove(struct platform_device *op) { - struct uart_port *port = dev_get_drvdata(&op->dev); - dev_set_drvdata(&op->dev, NULL); + struct uart_port *port = platform_get_drvdata(op); if (port) uart_remove_one_port(&mpc52xx_uart_driver, port); @@ -1392,7 +1702,7 @@ mpc52xx_uart_of_remove(struct platform_device *op) static int mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state) { - struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); + struct uart_port *port = (struct uart_port *) platform_get_drvdata(op); if (port) uart_suspend_port(&mpc52xx_uart_driver, port); @@ -1403,7 +1713,7 @@ mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state) static int mpc52xx_uart_of_resume(struct platform_device *op) { - struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); + struct uart_port *port = (struct uart_port *) platform_get_drvdata(op); if (port) uart_resume_port(&mpc52xx_uart_driver, port); diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 39c7ea4..2caf9c6 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -204,7 +204,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev) info->type = port_type; info->line = ret; - dev_set_drvdata(&ofdev->dev, info); + platform_set_drvdata(ofdev, info); return 0; out: kfree(info); @@ -217,7 +217,7 @@ out: */ static int of_platform_serial_remove(struct platform_device *ofdev) { - struct of_serial_info *info = dev_get_drvdata(&ofdev->dev); + struct of_serial_info *info = platform_get_drvdata(ofdev); switch (info->type) { #ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index f0b9f6b..b6d1728 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -161,6 +161,7 @@ struct uart_omap_port { u32 calc_latency; struct work_struct qos_work; struct pinctrl *pins; + bool is_suspending; }; #define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port))) @@ -197,7 +198,7 @@ static int serial_omap_get_context_loss_count(struct uart_omap_port *up) struct omap_uart_port_info *pdata = up->dev->platform_data; if (!pdata || !pdata->get_context_loss_count) - return 0; + return -EINVAL; return pdata->get_context_loss_count(up->dev); } @@ -1289,6 +1290,22 @@ static struct uart_driver serial_omap_reg = { }; #ifdef CONFIG_PM_SLEEP +static int serial_omap_prepare(struct device *dev) +{ + struct uart_omap_port *up = dev_get_drvdata(dev); + + up->is_suspending = true; + + return 0; +} + +static void serial_omap_complete(struct device *dev) +{ + struct uart_omap_port *up = dev_get_drvdata(dev); + + up->is_suspending = false; +} + static int serial_omap_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); @@ -1307,7 +1324,10 @@ static int serial_omap_resume(struct device *dev) return 0; } -#endif +#else +#define serial_omap_prepare NULL +#define serial_omap_complete NULL +#endif /* CONFIG_PM_SLEEP */ static void omap_serial_fill_features_erratas(struct uart_omap_port *up) { @@ -1482,6 +1502,9 @@ static int serial_omap_probe(struct platform_device *pdev) platform_set_drvdata(pdev, up); pm_runtime_enable(&pdev->dev); + if (omap_up_info->autosuspend_timeout == 0) + omap_up_info->autosuspend_timeout = -1; + device_init_wakeup(up->dev, true); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, omap_up_info->autosuspend_timeout); @@ -1591,13 +1614,19 @@ static void serial_omap_restore_context(struct uart_omap_port *up) static int serial_omap_runtime_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); - struct omap_uart_port_info *pdata = dev->platform_data; if (!up) return -EINVAL; - if (!pdata) - return 0; + /* + * When using 'no_console_suspend', the console UART must not be + * suspended. Since driver suspend is managed by runtime suspend, + * preventing runtime suspend (by returning error) will keep device + * active during suspend. + */ + if (up->is_suspending && !console_suspend_enabled && + uart_console(&up->port)) + return -EBUSY; up->context_loss_cnt = serial_omap_get_context_loss_count(up); @@ -1626,7 +1655,7 @@ static int serial_omap_runtime_resume(struct device *dev) int loss_cnt = serial_omap_get_context_loss_count(up); if (loss_cnt < 0) { - dev_err(dev, "serial_omap_get_context_loss_count failed : %d\n", + dev_dbg(dev, "serial_omap_get_context_loss_count failed : %d\n", loss_cnt); serial_omap_restore_context(up); } else if (up->context_loss_cnt != loss_cnt) { @@ -1643,6 +1672,8 @@ static const struct dev_pm_ops serial_omap_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(serial_omap_suspend, serial_omap_resume) SET_RUNTIME_PM_OPS(serial_omap_runtime_suspend, serial_omap_runtime_resume, NULL) + .prepare = serial_omap_prepare, + .complete = serial_omap_complete, }; #if defined(CONFIG_OF) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 21a7e17..572d481 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -217,6 +217,7 @@ enum { #define FRI2_64_UARTCLK 64000000 /* 64.0000 MHz */ #define FRI2_48_UARTCLK 48000000 /* 48.0000 MHz */ #define NTC1_UARTCLK 64000000 /* 64.0000 MHz */ +#define MINNOW_UARTCLK 50000000 /* 50.0000 MHz */ struct pch_uart_buffer { unsigned char *buf; @@ -398,6 +399,10 @@ static int pch_uart_get_uartclk(void) strstr(cmp, "nanoETXexpress-TT"))) return NTC1_UARTCLK; + cmp = dmi_get_system_info(DMI_BOARD_NAME); + if (cmp && strstr(cmp, "MinnowBoard")) + return MINNOW_UARTCLK; + return DEFAULT_UARTCLK; } diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 0c8a9fa..81ebc86 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1811,7 +1811,13 @@ static int __init s3c24xx_serial_modinit(void) return ret; } - return platform_driver_register(&samsung_serial_driver); + ret = platform_driver_register(&samsung_serial_driver); + if (ret < 0) { + pr_err("Failed to register platform driver\n"); + uart_unregister_driver(&s3c24xx_uart_drv); + } + + return ret; } static void __exit s3c24xx_serial_modexit(void) diff --git a/drivers/tty/serial/sc26xx.c b/drivers/tty/serial/sc26xx.c index c973568..4b1434d 100644 --- a/drivers/tty/serial/sc26xx.c +++ b/drivers/tty/serial/sc26xx.c @@ -696,7 +696,7 @@ static int sc26xx_probe(struct platform_device *dev) if (err) goto out_remove_ports; - dev_set_drvdata(&dev->dev, up); + platform_set_drvdata(dev, up); return 0; out_remove_ports: @@ -716,7 +716,7 @@ out_free_port: static int __exit sc26xx_driver_remove(struct platform_device *dev) { - struct uart_sc26xx_port *up = dev_get_drvdata(&dev->dev); + struct uart_sc26xx_port *up = platform_get_drvdata(dev); free_irq(up->port[0].irq, up); @@ -728,7 +728,6 @@ static int __exit sc26xx_driver_remove(struct platform_device *dev) kfree(up); sc26xx_port = NULL; - dev_set_drvdata(&dev->dev, NULL); return 0; } diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index f87dbfd..28cdd28 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -50,12 +50,6 @@ static struct lock_class_key port_lock_key; #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) -#ifdef CONFIG_SERIAL_CORE_CONSOLE -#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) -#else -#define uart_console(port) (0) -#endif - static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, struct ktermios *old_termios); static void uart_wait_until_sent(struct tty_struct *tty, int timeout); diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 03465b6..1fd564b 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -687,9 +687,10 @@ int sirfsoc_uart_probe(struct platform_device *pdev) if (sirfport->hw_flow_ctrl) { sirfport->p = pinctrl_get_select_default(&pdev->dev); - ret = IS_ERR(sirfport->p); - if (ret) + if (IS_ERR(sirfport->p)) { + ret = PTR_ERR(sirfport->p); goto err; + } } sirfport->clk = clk_get(&pdev->dev, NULL); diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index ba60708..cf86e72 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -577,7 +577,7 @@ static int hv_probe(struct platform_device *op) if (err) goto out_remove_port; - dev_set_drvdata(&op->dev, port); + platform_set_drvdata(op, port); return 0; @@ -601,7 +601,7 @@ out_free_port: static int hv_remove(struct platform_device *dev) { - struct uart_port *port = dev_get_drvdata(&dev->dev); + struct uart_port *port = platform_get_drvdata(dev); free_irq(port->irq, port); @@ -612,8 +612,6 @@ static int hv_remove(struct platform_device *dev) kfree(port); sunhv_port = NULL; - dev_set_drvdata(&dev->dev, NULL); - return 0; } diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index a422c8b..5d6136b 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -1037,7 +1037,7 @@ static int sab_probe(struct platform_device *op) if (err) goto out3; - dev_set_drvdata(&op->dev, &up[0]); + platform_set_drvdata(op, &up[0]); inst++; @@ -1059,7 +1059,7 @@ out: static int sab_remove(struct platform_device *op) { - struct uart_sunsab_port *up = dev_get_drvdata(&op->dev); + struct uart_sunsab_port *up = platform_get_drvdata(op); uart_remove_one_port(&sunsab_reg, &up[1].port); uart_remove_one_port(&sunsab_reg, &up[0].port); @@ -1070,8 +1070,6 @@ static int sab_remove(struct platform_device *op) up[0].port.membase, sizeof(union sab82532_async_regs)); - dev_set_drvdata(&op->dev, NULL); - return 0; } diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 0d84657..699cc1b 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1454,7 +1454,7 @@ static int su_probe(struct platform_device *op) kfree(up); return err; } - dev_set_drvdata(&op->dev, up); + platform_set_drvdata(op, up); nr_inst++; @@ -1483,7 +1483,7 @@ static int su_probe(struct platform_device *op) if (err) goto out_unmap; - dev_set_drvdata(&op->dev, up); + platform_set_drvdata(op, up); nr_inst++; @@ -1496,7 +1496,7 @@ out_unmap: static int su_remove(struct platform_device *op) { - struct uart_sunsu_port *up = dev_get_drvdata(&op->dev); + struct uart_sunsu_port *up = platform_get_drvdata(op); bool kbdms = false; if (up->su_type == SU_PORT_MS || @@ -1516,8 +1516,6 @@ static int su_remove(struct platform_device *op) if (kbdms) kfree(up); - dev_set_drvdata(&op->dev, NULL); - return 0; } diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index 813ef8e..135a152 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -1495,7 +1495,7 @@ static int zs_probe(struct platform_device *op) kbm_inst++; } - dev_set_drvdata(&op->dev, &up[0]); + platform_set_drvdata(op, &up[0]); return 0; } @@ -1512,7 +1512,7 @@ static void zs_remove_one(struct uart_sunzilog_port *up) static int zs_remove(struct platform_device *op) { - struct uart_sunzilog_port *up = dev_get_drvdata(&op->dev); + struct uart_sunzilog_port *up = platform_get_drvdata(op); struct zilog_layout __iomem *regs; zs_remove_one(&up[0]); @@ -1521,8 +1521,6 @@ static int zs_remove(struct platform_device *op) regs = sunzilog_chip_regs[up[0].port.line / 2]; of_iounmap(&op->resource[0], regs, sizeof(struct zilog_layout)); - dev_set_drvdata(&op->dev, NULL); - return 0; } diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index 7355303..8831748 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -1451,7 +1451,7 @@ static int ucc_uart_probe(struct platform_device *ofdev) goto out_np; } - dev_set_drvdata(&ofdev->dev, qe_port); + platform_set_drvdata(ofdev, qe_port); dev_info(&ofdev->dev, "UCC%u assigned to /dev/ttyQE%u\n", qe_port->ucc_num + 1, qe_port->port.line); @@ -1471,13 +1471,12 @@ out_free: static int ucc_uart_remove(struct platform_device *ofdev) { - struct uart_qe_port *qe_port = dev_get_drvdata(&ofdev->dev); + struct uart_qe_port *qe_port = platform_get_drvdata(ofdev); dev_info(&ofdev->dev, "removing /dev/ttyQE%u\n", qe_port->port.line); uart_remove_one_port(&ucc_uart_driver, &qe_port->port); - dev_set_drvdata(&ofdev->dev, NULL); kfree(qe_port); return 0; @@ -1518,9 +1517,11 @@ static int __init ucc_uart_init(void) } ret = platform_driver_register(&ucc_uart_of_driver); - if (ret) + if (ret) { printk(KERN_ERR "ucc-uart: could not register platform driver\n"); + uart_unregister_driver(&ucc_uart_driver); + } return ret; } diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 1a8bc22..48af43d 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -648,7 +648,7 @@ static struct platform_driver vt8500_platform_driver = { .driver = { .name = "vt8500_serial", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(wmt_dt_ids), + .of_match_table = wmt_dt_ids, }, }; diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 4e5c778..6c91745 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -974,12 +974,11 @@ static int xuartps_probe(struct platform_device *pdev) port->dev = &pdev->dev; port->uartclk = clk_get_rate(clk); port->private_data = clk; - dev_set_drvdata(&pdev->dev, port); + platform_set_drvdata(pdev, port); rc = uart_add_one_port(&xuartps_uart_driver, port); if (rc) { dev_err(&pdev->dev, "uart_add_one_port() failed; err=%i\n", rc); - dev_set_drvdata(&pdev->dev, NULL); return rc; } return 0; @@ -994,46 +993,17 @@ static int xuartps_probe(struct platform_device *pdev) **/ static int xuartps_remove(struct platform_device *pdev) { - struct uart_port *port = dev_get_drvdata(&pdev->dev); + struct uart_port *port = platform_get_drvdata(pdev); struct clk *clk = port->private_data; int rc; /* Remove the xuartps port from the serial core */ rc = uart_remove_one_port(&xuartps_uart_driver, port); - dev_set_drvdata(&pdev->dev, NULL); port->mapbase = 0; clk_disable_unprepare(clk); return rc; } -/** - * xuartps_suspend - suspend event - * @pdev: Pointer to the platform device structure - * @state: State of the device - * - * Returns 0 - **/ -static int xuartps_suspend(struct platform_device *pdev, pm_message_t state) -{ - /* Call the API provided in serial_core.c file which handles - * the suspend. - */ - uart_suspend_port(&xuartps_uart_driver, &xuartps_port[pdev->id]); - return 0; -} - -/** - * xuartps_resume - Resume after a previous suspend - * @pdev: Pointer to the platform device structure - * - * Returns 0 - **/ -static int xuartps_resume(struct platform_device *pdev) -{ - uart_resume_port(&xuartps_uart_driver, &xuartps_port[pdev->id]); - return 0; -} - /* Match table for of_platform binding */ static struct of_device_id xuartps_of_match[] = { { .compatible = "xlnx,xuartps", }, @@ -1044,8 +1014,6 @@ MODULE_DEVICE_TABLE(of, xuartps_of_match); static struct platform_driver xuartps_platform_driver = { .probe = xuartps_probe, /* Probe method */ .remove = xuartps_remove, /* Detach method */ - .suspend = xuartps_suspend, /* Suspend */ - .resume = xuartps_resume, /* Resume after a suspend */ .driver = { .owner = THIS_MODULE, .name = XUARTPS_NAME, /* Driver name */ diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index b51c154..5f68f2c 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -932,7 +932,7 @@ static int sysrq_reset_seq_param_set(const char *buffer, unsigned long val; int error; - error = strict_strtoul(buffer, 0, &val); + error = kstrtoul(buffer, 0, &val); if (error < 0) return error; diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 6464029..366af83 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1618,6 +1618,8 @@ static void release_tty(struct tty_struct *tty, int idx) tty_free_termios(tty); tty_driver_remove_tty(tty->driver, tty); tty->port->itty = NULL; + if (tty->link) + tty->link->port->itty = NULL; cancel_work_sync(&tty->port->buf.work); if (tty->link) @@ -2138,6 +2140,7 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait) static int __tty_fasync(int fd, struct file *filp, int on) { struct tty_struct *tty = file_tty(filp); + struct tty_ldisc *ldisc; unsigned long flags; int retval = 0; @@ -2148,11 +2151,17 @@ static int __tty_fasync(int fd, struct file *filp, int on) if (retval <= 0) goto out; + ldisc = tty_ldisc_ref(tty); + if (ldisc) { + if (ldisc->ops->fasync) + ldisc->ops->fasync(tty, on); + tty_ldisc_deref(ldisc); + } + if (on) { enum pid_type type; struct pid *pid; - if (!waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = 1; + spin_lock_irqsave(&tty->ctrl_lock, flags); if (tty->pgrp) { pid = tty->pgrp; @@ -2165,13 +2174,7 @@ static int __tty_fasync(int fd, struct file *filp, int on) spin_unlock_irqrestore(&tty->ctrl_lock, flags); retval = __f_setown(filp, pid, type, 0); put_pid(pid); - if (retval) - goto out; - } else { - if (!tty->fasync && !waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = N_TTY_BUF_SIZE; } - retval = 0; out: return retval; } diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c new file mode 100644 index 0000000..22fad8a --- /dev/null +++ b/drivers/tty/tty_ldsem.c @@ -0,0 +1,453 @@ +/* + * Ldisc rw semaphore + * + * The ldisc semaphore is semantically a rw_semaphore but which enforces + * an alternate policy, namely: + * 1) Supports lock wait timeouts + * 2) Write waiter has priority + * 3) Downgrading is not supported + * + * Implementation notes: + * 1) Upper half of semaphore count is a wait count (differs from rwsem + * in that rwsem normalizes the upper half to the wait bias) + * 2) Lacks overflow checking + * + * The generic counting was copied and modified from include/asm-generic/rwsem.h + * by Paul Mackerras <paulus@samba.org>. + * + * The scheduling policy was copied and modified from lib/rwsem.c + * Written by David Howells (dhowells@redhat.com). + * + * This implementation incorporates the write lock stealing work of + * Michel Lespinasse <walken@google.com>. + * + * Copyright (C) 2013 Peter Hurley <peter@hurleysoftware.com> + * + * This file may be redistributed under the terms of the GNU General Public + * License v2. + */ + +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/atomic.h> +#include <linux/tty.h> +#include <linux/sched.h> + + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# define __acq(l, s, t, r, c, n, i) \ + lock_acquire(&(l)->dep_map, s, t, r, c, n, i) +# define __rel(l, n, i) \ + lock_release(&(l)->dep_map, n, i) +# ifdef CONFIG_PROVE_LOCKING +# define lockdep_acquire(l, s, t, i) __acq(l, s, t, 0, 2, NULL, i) +# define lockdep_acquire_nest(l, s, t, n, i) __acq(l, s, t, 0, 2, n, i) +# define lockdep_acquire_read(l, s, t, i) __acq(l, s, t, 1, 2, NULL, i) +# define lockdep_release(l, n, i) __rel(l, n, i) +# else +# define lockdep_acquire(l, s, t, i) __acq(l, s, t, 0, 1, NULL, i) +# define lockdep_acquire_nest(l, s, t, n, i) __acq(l, s, t, 0, 1, n, i) +# define lockdep_acquire_read(l, s, t, i) __acq(l, s, t, 1, 1, NULL, i) +# define lockdep_release(l, n, i) __rel(l, n, i) +# endif +#else +# define lockdep_acquire(l, s, t, i) do { } while (0) +# define lockdep_acquire_nest(l, s, t, n, i) do { } while (0) +# define lockdep_acquire_read(l, s, t, i) do { } while (0) +# define lockdep_release(l, n, i) do { } while (0) +#endif + +#ifdef CONFIG_LOCK_STAT +# define lock_stat(_lock, stat) lock_##stat(&(_lock)->dep_map, _RET_IP_) +#else +# define lock_stat(_lock, stat) do { } while (0) +#endif + + +#if BITS_PER_LONG == 64 +# define LDSEM_ACTIVE_MASK 0xffffffffL +#else +# define LDSEM_ACTIVE_MASK 0x0000ffffL +#endif + +#define LDSEM_UNLOCKED 0L +#define LDSEM_ACTIVE_BIAS 1L +#define LDSEM_WAIT_BIAS (-LDSEM_ACTIVE_MASK-1) +#define LDSEM_READ_BIAS LDSEM_ACTIVE_BIAS +#define LDSEM_WRITE_BIAS (LDSEM_WAIT_BIAS + LDSEM_ACTIVE_BIAS) + +struct ldsem_waiter { + struct list_head list; + struct task_struct *task; +}; + +static inline long ldsem_atomic_update(long delta, struct ld_semaphore *sem) +{ + return atomic_long_add_return(delta, (atomic_long_t *)&sem->count); +} + +static inline int ldsem_cmpxchg(long *old, long new, struct ld_semaphore *sem) +{ + long tmp = *old; + *old = atomic_long_cmpxchg(&sem->count, *old, new); + return *old == tmp; +} + +/* + * Initialize an ldsem: + */ +void __init_ldsem(struct ld_semaphore *sem, const char *name, + struct lock_class_key *key) +{ +#ifdef CONFIG_DEBUG_LOCK_ALLOC + /* + * Make sure we are not reinitializing a held semaphore: + */ + debug_check_no_locks_freed((void *)sem, sizeof(*sem)); + lockdep_init_map(&sem->dep_map, name, key, 0); +#endif + sem->count = LDSEM_UNLOCKED; + sem->wait_readers = 0; + raw_spin_lock_init(&sem->wait_lock); + INIT_LIST_HEAD(&sem->read_wait); + INIT_LIST_HEAD(&sem->write_wait); +} + +static void __ldsem_wake_readers(struct ld_semaphore *sem) +{ + struct ldsem_waiter *waiter, *next; + struct task_struct *tsk; + long adjust, count; + + /* Try to grant read locks to all readers on the read wait list. + * Note the 'active part' of the count is incremented by + * the number of readers before waking any processes up. + */ + adjust = sem->wait_readers * (LDSEM_ACTIVE_BIAS - LDSEM_WAIT_BIAS); + count = ldsem_atomic_update(adjust, sem); + do { + if (count > 0) + break; + if (ldsem_cmpxchg(&count, count - adjust, sem)) + return; + } while (1); + + list_for_each_entry_safe(waiter, next, &sem->read_wait, list) { + tsk = waiter->task; + smp_mb(); + waiter->task = NULL; + wake_up_process(tsk); + put_task_struct(tsk); + } + INIT_LIST_HEAD(&sem->read_wait); + sem->wait_readers = 0; +} + +static inline int writer_trylock(struct ld_semaphore *sem) +{ + /* only wake this writer if the active part of the count can be + * transitioned from 0 -> 1 + */ + long count = ldsem_atomic_update(LDSEM_ACTIVE_BIAS, sem); + do { + if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS) + return 1; + if (ldsem_cmpxchg(&count, count - LDSEM_ACTIVE_BIAS, sem)) + return 0; + } while (1); +} + +static void __ldsem_wake_writer(struct ld_semaphore *sem) +{ + struct ldsem_waiter *waiter; + + waiter = list_entry(sem->write_wait.next, struct ldsem_waiter, list); + wake_up_process(waiter->task); +} + +/* + * handle the lock release when processes blocked on it that can now run + * - if we come here from up_xxxx(), then: + * - the 'active part' of count (&0x0000ffff) reached 0 (but may have changed) + * - the 'waiting part' of count (&0xffff0000) is -ve (and will still be so) + * - the spinlock must be held by the caller + * - woken process blocks are discarded from the list after having task zeroed + */ +static void __ldsem_wake(struct ld_semaphore *sem) +{ + if (!list_empty(&sem->write_wait)) + __ldsem_wake_writer(sem); + else if (!list_empty(&sem->read_wait)) + __ldsem_wake_readers(sem); +} + +static void ldsem_wake(struct ld_semaphore *sem) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&sem->wait_lock, flags); + __ldsem_wake(sem); + raw_spin_unlock_irqrestore(&sem->wait_lock, flags); +} + +/* + * wait for the read lock to be granted + */ +static struct ld_semaphore __sched * +down_read_failed(struct ld_semaphore *sem, long count, long timeout) +{ + struct ldsem_waiter waiter; + struct task_struct *tsk = current; + long adjust = -LDSEM_ACTIVE_BIAS + LDSEM_WAIT_BIAS; + + /* set up my own style of waitqueue */ + raw_spin_lock_irq(&sem->wait_lock); + + /* Try to reverse the lock attempt but if the count has changed + * so that reversing fails, check if there are are no waiters, + * and early-out if not */ + do { + if (ldsem_cmpxchg(&count, count + adjust, sem)) + break; + if (count > 0) { + raw_spin_unlock_irq(&sem->wait_lock); + return sem; + } + } while (1); + + list_add_tail(&waiter.list, &sem->read_wait); + sem->wait_readers++; + + waiter.task = tsk; + get_task_struct(tsk); + + /* if there are no active locks, wake the new lock owner(s) */ + if ((count & LDSEM_ACTIVE_MASK) == 0) + __ldsem_wake(sem); + + raw_spin_unlock_irq(&sem->wait_lock); + + /* wait to be given the lock */ + for (;;) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + + if (!waiter.task) + break; + if (!timeout) + break; + timeout = schedule_timeout(timeout); + } + + __set_task_state(tsk, TASK_RUNNING); + + if (!timeout) { + /* lock timed out but check if this task was just + * granted lock ownership - if so, pretend there + * was no timeout; otherwise, cleanup lock wait */ + raw_spin_lock_irq(&sem->wait_lock); + if (waiter.task) { + ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem); + list_del(&waiter.list); + raw_spin_unlock_irq(&sem->wait_lock); + put_task_struct(waiter.task); + return NULL; + } + raw_spin_unlock_irq(&sem->wait_lock); + } + + return sem; +} + +/* + * wait for the write lock to be granted + */ +static struct ld_semaphore __sched * +down_write_failed(struct ld_semaphore *sem, long count, long timeout) +{ + struct ldsem_waiter waiter; + struct task_struct *tsk = current; + long adjust = -LDSEM_ACTIVE_BIAS; + int locked = 0; + + /* set up my own style of waitqueue */ + raw_spin_lock_irq(&sem->wait_lock); + + /* Try to reverse the lock attempt but if the count has changed + * so that reversing fails, check if the lock is now owned, + * and early-out if so */ + do { + if (ldsem_cmpxchg(&count, count + adjust, sem)) + break; + if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS) { + raw_spin_unlock_irq(&sem->wait_lock); + return sem; + } + } while (1); + + list_add_tail(&waiter.list, &sem->write_wait); + + waiter.task = tsk; + + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + for (;;) { + if (!timeout) + break; + raw_spin_unlock_irq(&sem->wait_lock); + timeout = schedule_timeout(timeout); + raw_spin_lock_irq(&sem->wait_lock); + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if ((locked = writer_trylock(sem))) + break; + } + + if (!locked) + ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem); + list_del(&waiter.list); + raw_spin_unlock_irq(&sem->wait_lock); + + __set_task_state(tsk, TASK_RUNNING); + + /* lock wait may have timed out */ + if (!locked) + return NULL; + return sem; +} + + + +static inline int __ldsem_down_read_nested(struct ld_semaphore *sem, + int subclass, long timeout) +{ + long count; + + lockdep_acquire_read(sem, subclass, 0, _RET_IP_); + + count = ldsem_atomic_update(LDSEM_READ_BIAS, sem); + if (count <= 0) { + lock_stat(sem, contended); + if (!down_read_failed(sem, count, timeout)) { + lockdep_release(sem, 1, _RET_IP_); + return 0; + } + } + lock_stat(sem, acquired); + return 1; +} + +static inline int __ldsem_down_write_nested(struct ld_semaphore *sem, + int subclass, long timeout) +{ + long count; + + lockdep_acquire(sem, subclass, 0, _RET_IP_); + + count = ldsem_atomic_update(LDSEM_WRITE_BIAS, sem); + if ((count & LDSEM_ACTIVE_MASK) != LDSEM_ACTIVE_BIAS) { + lock_stat(sem, contended); + if (!down_write_failed(sem, count, timeout)) { + lockdep_release(sem, 1, _RET_IP_); + return 0; + } + } + lock_stat(sem, acquired); + return 1; +} + + +/* + * lock for reading -- returns 1 if successful, 0 if timed out + */ +int __sched ldsem_down_read(struct ld_semaphore *sem, long timeout) +{ + might_sleep(); + return __ldsem_down_read_nested(sem, 0, timeout); +} + +/* + * trylock for reading -- returns 1 if successful, 0 if contention + */ +int ldsem_down_read_trylock(struct ld_semaphore *sem) +{ + long count = sem->count; + + while (count >= 0) { + if (ldsem_cmpxchg(&count, count + LDSEM_READ_BIAS, sem)) { + lockdep_acquire_read(sem, 0, 1, _RET_IP_); + lock_stat(sem, acquired); + return 1; + } + } + return 0; +} + +/* + * lock for writing -- returns 1 if successful, 0 if timed out + */ +int __sched ldsem_down_write(struct ld_semaphore *sem, long timeout) +{ + might_sleep(); + return __ldsem_down_write_nested(sem, 0, timeout); +} + +/* + * trylock for writing -- returns 1 if successful, 0 if contention + */ +int ldsem_down_write_trylock(struct ld_semaphore *sem) +{ + long count = sem->count; + + while ((count & LDSEM_ACTIVE_MASK) == 0) { + if (ldsem_cmpxchg(&count, count + LDSEM_WRITE_BIAS, sem)) { + lockdep_acquire(sem, 0, 1, _RET_IP_); + lock_stat(sem, acquired); + return 1; + } + } + return 0; +} + +/* + * release a read lock + */ +void ldsem_up_read(struct ld_semaphore *sem) +{ + long count; + + lockdep_release(sem, 1, _RET_IP_); + + count = ldsem_atomic_update(-LDSEM_READ_BIAS, sem); + if (count < 0 && (count & LDSEM_ACTIVE_MASK) == 0) + ldsem_wake(sem); +} + +/* + * release a write lock + */ +void ldsem_up_write(struct ld_semaphore *sem) +{ + long count; + + lockdep_release(sem, 1, _RET_IP_); + + count = ldsem_atomic_update(-LDSEM_WRITE_BIAS, sem); + if (count < 0) + ldsem_wake(sem); +} + + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + +int ldsem_down_read_nested(struct ld_semaphore *sem, int subclass, long timeout) +{ + might_sleep(); + return __ldsem_down_read_nested(sem, subclass, timeout); +} + +int ldsem_down_write_nested(struct ld_semaphore *sem, int subclass, + long timeout) +{ + might_sleep(); + return __ldsem_down_write_nested(sem, subclass, timeout); +} + +#endif diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 740202d..c677829 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3086,17 +3086,6 @@ err: }; -static int bind_con_driver(const struct consw *csw, int first, int last, - int deflt) -{ - int ret; - - console_lock(); - ret = do_bind_con_driver(csw, first, last, deflt); - console_unlock(); - return ret; -} - #ifdef CONFIG_VT_HW_CONSOLE_BINDING static int con_is_graphics(const struct consw *csw, int first, int last) { @@ -3114,34 +3103,6 @@ static int con_is_graphics(const struct consw *csw, int first, int last) return retval; } -/** - * unbind_con_driver - unbind a console driver - * @csw: pointer to console driver to unregister - * @first: first in range of consoles that @csw should be unbound from - * @last: last in range of consoles that @csw should be unbound from - * @deflt: should next bound console driver be default after @csw is unbound? - * - * To unbind a driver from all possible consoles, pass 0 as @first and - * %MAX_NR_CONSOLES as @last. - * - * @deflt controls whether the console that ends up replacing @csw should be - * the default console. - * - * RETURNS: - * -ENODEV if @csw isn't a registered console driver or can't be unregistered - * or 0 on success. - */ -int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) -{ - int retval; - - console_lock(); - retval = do_unbind_con_driver(csw, first, last, deflt); - console_unlock(); - return retval; -} -EXPORT_SYMBOL(unbind_con_driver); - /* unlocked version of unbind_con_driver() */ int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt) { @@ -3262,8 +3223,11 @@ static int vt_bind(struct con_driver *con) if (first == 0 && last == MAX_NR_CONSOLES -1) deflt = 1; - if (first != -1) - bind_con_driver(csw, first, last, deflt); + if (first != -1) { + console_lock(); + do_bind_con_driver(csw, first, last, deflt); + console_unlock(); + } first = -1; last = -1; @@ -3301,8 +3265,11 @@ static int vt_unbind(struct con_driver *con) if (first == 0 && last == MAX_NR_CONSOLES -1) deflt = 1; - if (first != -1) - unbind_con_driver(csw, first, last, deflt); + if (first != -1) { + console_lock(); + do_unbind_con_driver(csw, first, last, deflt); + console_unlock(); + } first = -1; last = -1; @@ -3574,29 +3541,9 @@ err: return retval; } -/** - * register_con_driver - register console driver to console layer - * @csw: console driver - * @first: the first console to take over, minimum value is 0 - * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 - * - * DESCRIPTION: This function registers a console driver which can later - * bind to a range of consoles specified by @first and @last. It will - * also initialize the console driver by calling con_startup(). - */ -int register_con_driver(const struct consw *csw, int first, int last) -{ - int retval; - - console_lock(); - retval = do_register_con_driver(csw, first, last); - console_unlock(); - return retval; -} -EXPORT_SYMBOL(register_con_driver); /** - * unregister_con_driver - unregister console driver from console layer + * do_unregister_con_driver - unregister console driver from console layer * @csw: console driver * * DESCRIPTION: All drivers that registers to the console layer must @@ -3606,17 +3553,6 @@ EXPORT_SYMBOL(register_con_driver); * * The driver must unbind first prior to unregistration. */ -int unregister_con_driver(const struct consw *csw) -{ - int retval; - - console_lock(); - retval = do_unregister_con_driver(csw); - console_unlock(); - return retval; -} -EXPORT_SYMBOL(unregister_con_driver); - int do_unregister_con_driver(const struct consw *csw) { int i, retval = -ENODEV; @@ -3654,7 +3590,7 @@ EXPORT_SYMBOL_GPL(do_unregister_con_driver); * when a driver wants to take over some existing consoles * and become default driver for newly opened ones. * - * take_over_console is basically a register followed by unbind + * do_take_over_console is basically a register followed by unbind */ int do_take_over_console(const struct consw *csw, int first, int last, int deflt) { @@ -3675,30 +3611,6 @@ int do_take_over_console(const struct consw *csw, int first, int last, int deflt } EXPORT_SYMBOL_GPL(do_take_over_console); -/* - * If we support more console drivers, this function is used - * when a driver wants to take over some existing consoles - * and become default driver for newly opened ones. - * - * take_over_console is basically a register followed by unbind - */ -int take_over_console(const struct consw *csw, int first, int last, int deflt) -{ - int err; - - err = register_con_driver(csw, first, last); - /* - * If we get an busy error we still want to bind the console driver - * and return success, as we may have unbound the console driver - * but not unregistered it. - */ - if (err == -EBUSY) - err = 0; - if (!err) - bind_con_driver(csw, first, last, deflt); - - return err; -} /* * give_up_console is a wrapper to unregister_con_driver. It will only @@ -3706,7 +3618,9 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt) */ void give_up_console(const struct consw *csw) { - unregister_con_driver(csw); + console_lock(); + do_unregister_con_driver(csw); + console_unlock(); } static int __init vtconsole_class_init(void) @@ -4262,6 +4176,5 @@ EXPORT_SYMBOL(console_blanked); EXPORT_SYMBOL(vc_cons); EXPORT_SYMBOL(global_cursor_default); #ifndef VT_SINGLE_DRIVER -EXPORT_SYMBOL(take_over_console); EXPORT_SYMBOL(give_up_console); #endif |