From cf0ebee0d0374c6d75494ac96f86b4aea482dd09 Mon Sep 17 00:00:00 2001 From: Sourav Poddar Date: Wed, 15 May 2013 21:05:37 +0530 Subject: [PATCH 01/51] serial: Move "uart_console" def to core header file. Move "uart_console" definition to serial core header file, so that it can be used by serial drivers. Get rid of the uart_console defintion from mpc52xx_uart driver. Cc: Santosh Shilimkar Cc: Rajendra nayak Reviewed-by: Felipe Balbi Reviewed-by: Kevin Hilman Tested-by: Kevin Hilman Signed-off-by: Sourav Poddar Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mpc52xx_uart.c | 10 ---------- drivers/tty/serial/serial_core.c | 6 ------ include/linux/serial_core.h | 7 +++++++ 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 018bad922554..d74ac060c06f 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 */ /* ======================================================================== */ diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index f87dbfd32770..28cdd2829139 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/include/linux/serial_core.h b/include/linux/serial_core.h index 87d4bbc773fc..b98291ac7f14 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -31,6 +31,13 @@ #include #include +#ifdef CONFIG_SERIAL_CORE_CONSOLE +#define uart_console(port) \ + ((port)->cons && (port)->cons->index == (port)->line) +#else +#define uart_console(port) (0) +#endif + struct uart_port; struct serial_struct; struct device; From ddd85e225c8885b5e4419b0499ab27100e7c366a Mon Sep 17 00:00:00 2001 From: Sourav Poddar Date: Wed, 15 May 2013 21:05:38 +0530 Subject: [PATCH 02/51] serial: omap: prevent runtime PM for "no_console_suspend" The driver manages "no_console_suspend" by preventing runtime PM during the suspend path, which forces the console UART to stay awake. Reviewed-by: Felipe Balbi Reviewed-by: Kevin Hilman Tested-by: Kevin Hilman Signed-off-by: Sourav Poddar Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/omap-serial.c | 34 +++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 30d4f7a783cd..9457fe331ab5 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))) @@ -1312,6 +1313,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); @@ -1330,7 +1347,10 @@ static int serial_omap_resume(struct device *dev) return 0; } -#endif +#else +#define serial_omap_prepare NULL +#define serial_omap_prepare NULL +#endif /* CONFIG_PM_SLEEP */ static void omap_serial_fill_features_erratas(struct uart_omap_port *up) { @@ -1616,6 +1636,16 @@ 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; + /* + * 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; + if (!up) return -EINVAL; @@ -1666,6 +1696,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) From cd97721a1b6bbf6eaa9ad050eb472c7f180bd0e0 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 16:15:28 +0900 Subject: [PATCH 03/51] serial: altera: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Tobias Klauser Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/altera_uart.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 13471dd95793..1d46966e2a65 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; } From ea33640a933e208301fea4fe7cbc6cc288e32cc6 Mon Sep 17 00:00:00 2001 From: Jongsung Kim Date: Fri, 10 May 2013 18:05:35 +0900 Subject: [PATCH 04/51] tty: serial: amba-pl011: revise to use amba_rev macro This patch replaces the ugly bit-operations with the convenient amba_rev macro from the get_fifosize_arm function. The type of get_fifosize member function is also slightly changed to take a pointer to the amba_device. Signed-off-by: Jongsung Kim Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 8ab70a620919..85cfe116f423 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; From f1f836e4209eb904008efeb5faa69839b24fab5b Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Tue, 14 May 2013 17:06:07 +0200 Subject: [PATCH 05/51] serial: imx: Add Rx Fifo overrun error message This patch enables the overrun error (ORE) interrupt and increases the counter in case of overrun. Signed-off-by: Alexander Stein Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 147c9e193595..72bc1dbcd055 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -449,6 +449,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 +589,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 +606,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; } From e424259e2e27290c457f65161ae62f7c89215b88 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Mon, 13 May 2013 10:46:35 -0700 Subject: [PATCH 06/51] tty: xuartps: Remove suspend/resume functions Currently Zynq does not support suspend/resume. The driver callbacks are never used or tested, broken and using the old PM interface. Signed-off-by: Soren Brinkmann Cc: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 4e5c77834c50..b5f655d10098 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1006,34 +1006,6 @@ static int xuartps_remove(struct platform_device *pdev) 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 +1016,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 */ From 582f55907d3f6cb762af20b56478bf25ef96430e Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 17 May 2013 12:49:48 -0400 Subject: [PATCH 07/51] tty: Remove TTY_HW_COOK_IN/OUT No in-tree tty driver supports cooked mode in hardware; remove. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 12 +++--------- include/linux/tty.h | 2 -- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index d655416087b7..905a6fa5250e 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -647,8 +647,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; @@ -1516,12 +1515,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) || @@ -2037,7 +2031,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) { diff --git a/include/linux/tty.h b/include/linux/tty.h index 8780bd2a272a..82ab69bc9b79 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -309,8 +309,6 @@ struct tty_file_private { #define TTY_LDISC 9 /* Line discipline attached */ #define TTY_LDISC_CHANGING 10 /* Line discipline changing */ #define TTY_LDISC_OPEN 11 /* Line discipline is open */ -#define TTY_HW_COOK_OUT 14 /* Hardware can do output cooking */ -#define TTY_HW_COOK_IN 15 /* Hardware can do input cooking */ #define TTY_PTY_LOCK 16 /* pty private */ #define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */ #define TTY_HUPPED 18 /* Post driver->hangup() */ From e57f35d45f37a380ebf4006b560b68f3eb6af3d7 Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Thu, 9 May 2013 02:13:30 +0800 Subject: [PATCH 08/51] fbcon: convert last fbcon_takeover call to do_fbcon_takeover After commit 054430e773c9a1e26f38e30156eff02dedfffc17 (fbcon: fix locking harder), there is only one place use do_fbcon_takeover now, this patch convert it to do_fbcon_takeover too, then we can delete fbcon_takeover whos function can be achieved with do_fbcon_takeover easily to reduce code size and duplication. Signed-off-by: Wang YanQing Signed-off-by: Greg Kroah-Hartman --- drivers/video/console/fbcon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index a92783e480e6..84121da38474 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -3543,8 +3543,9 @@ static void fbcon_start(void) } } + do_fbcon_takeover(0); console_unlock(); - fbcon_takeover(0); + } } From 7c80591c6032fc92ae16be2d6876a1b1ad20485e Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Thu, 9 May 2013 02:13:51 +0800 Subject: [PATCH 09/51] fbcon: delete unneeded function fbcon_takeover Now there is no place use fbcon_takeover, and fbcon_takeover has huge duplication code with do_fbcon_takeover, we can achieve fbcon_takeover's function with do_fbcon_takeover easily, so we can just delete it. Signed-off-by: Wang YanQing Signed-off-by: Greg Kroah-Hartman --- drivers/video/console/fbcon.c | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 84121da38474..e05fa8356068 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -556,34 +556,6 @@ static int do_fbcon_takeover(int show_logo) return err; } -static int fbcon_takeover(int show_logo) -{ - int err, i; - - if (!num_registered_fb) - return -ENODEV; - - if (!show_logo) - logo_shown = FBCON_LOGO_DONTSHOW; - - for (i = first_fb_vc; i <= last_fb_vc; i++) - con2fb_map[i] = info_idx; - - err = take_over_console(&fb_con, first_fb_vc, last_fb_vc, - fbcon_is_default); - - if (err) { - for (i = first_fb_vc; i <= last_fb_vc; i++) { - con2fb_map[i] = -1; - } - info_idx = -1; - } else { - fbcon_has_console_bind = 1; - } - - return err; -} - #ifdef MODULE static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, int cols, int rows, int new_cols, int new_rows) From dc9641895abb30aa456918a433a52e13fa0f56b1 Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Thu, 9 May 2013 02:13:59 +0800 Subject: [PATCH 10/51] vt: delete unneeded functions register_con_driver|take_over_console Now there is no place use register_con_driver|take_over_console, and we can achieve their function with do_register_con_driver| do_take_over_console easily, so just delete them to reduce code duplication. Signed-off-by: Wang YanQing Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 45 ----------------------------------------- include/linux/console.h | 2 -- 2 files changed, 47 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index fbd447b390f7..852d470d674b 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3576,26 +3576,6 @@ 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 @@ -3677,30 +3657,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 @@ -4264,6 +4220,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 diff --git a/include/linux/console.h b/include/linux/console.h index 73bab0f58af5..8f9bd966dbc6 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -75,10 +75,8 @@ extern const struct consw newport_con; /* SGI Newport console */ extern const struct consw prom_con; /* SPARC PROM console */ int con_is_bound(const struct consw *csw); -int register_con_driver(const struct consw *csw, int first, int last); int unregister_con_driver(const struct consw *csw); int do_unregister_con_driver(const struct consw *csw); -int take_over_console(const struct consw *sw, int first, int last, int deflt); int do_take_over_console(const struct consw *sw, int first, int last, int deflt); void give_up_console(const struct consw *sw); #ifdef CONFIG_HW_CONSOLE From 618f2b90077ef22e5c7025fca4e9027bf75d918f Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Thu, 9 May 2013 02:14:07 +0800 Subject: [PATCH 11/51] vt: convert last unbind_con_driver call to do_unbind_con_driver There is only one place use unbind_con_driver, this patch convert it to do_unbind_con_driver too, then we can delete unbind_con_driver to reduce code size and duplication. Signed-off-by: Wang YanQing Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 852d470d674b..a422322cf8b9 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3303,8 +3303,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; From c1f5e38a5d3583b877619f95a922b607966dfc2e Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Thu, 9 May 2013 02:14:15 +0800 Subject: [PATCH 12/51] vt: delete unneeded function unbind_con_driver Now there is no place use unbind_con_driver, and we can achieve unbind_con_driver's function with do_unbind_con_driver easily, so just delete it to reduce code size and duplication. Signed-off-by: Wang YanQing Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 28 ---------------------------- include/linux/vt_kern.h | 2 -- 2 files changed, 30 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index a422322cf8b9..164cbeff5676 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3116,34 +3116,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) { diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index e8d65718560b..adc7ff58befc 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -133,8 +133,6 @@ void change_console(struct vc_data *new_vc); void reset_vc(struct vc_data *vc); extern int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt); -extern int unbind_con_driver(const struct consw *csw, int first, int last, - int deflt); int vty_init(const struct file_operations *console_fops); static inline bool vt_force_oops_output(struct vc_data *vc) From c62a1e57e49d956fe45ee49197ba3bd5763c87ab Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Thu, 9 May 2013 02:14:21 +0800 Subject: [PATCH 13/51] vt: convert last bind_con_driver call to do_bind_con_driver There is only one place use bind_con_driver now, this patch convert it to do_bind_con_driver too, then we can delete bind_con_driver whos function can be replaced by do_bind_con_driver easily to reduce code size and duplication. Signed-off-by: Wang YanQing Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 164cbeff5676..7e0953cd3f3a 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3236,8 +3236,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; From 77d6c98454f0443f03ec690fb0ea25a26c01ddcb Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Thu, 9 May 2013 02:14:29 +0800 Subject: [PATCH 14/51] vt: delete unneeded function bind_con_driver Now there is no place use bind_con_driver, and do_bind_con_driver can achieve bind_con_driver's function easily, so just delete it to reduce code size and duplication. Signed-off-by: Wang YanQing Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 7e0953cd3f3a..d5bfa869675b 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3088,17 +3088,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) { From 70125e76b0a7aec5035ca45076b96e5f9e230ef7 Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Thu, 9 May 2013 02:14:39 +0800 Subject: [PATCH 15/51] fbcon: convert last two unregister_con_driver call to do_unregister_con_driver There are only two place use unregister_con_driver now, this patch convert them to do_unregister_con_driver too, then we can delete unregister_con_driver whos function can be achieved with do_unregister_con_driver easily to reduce code size and duplication. Signed-off-by: Wang YanQing Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 4 +++- drivers/video/console/fbcon.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index d5bfa869675b..8f4a71aa971e 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3631,7 +3631,9 @@ EXPORT_SYMBOL_GPL(do_take_over_console); */ 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) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index e05fa8356068..a09c667c0c3d 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -3621,8 +3621,8 @@ static void __exit fb_console_exit(void) fbcon_deinit_device(); device_destroy(fb_class, MKDEV(0, 0)); fbcon_exit(); + do_unregister_con_driver(&fb_con); console_unlock(); - unregister_con_driver(&fb_con); } module_exit(fb_console_exit); From 50539dd4f88e8a689a38c94337768fd7ff3fd326 Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Thu, 9 May 2013 02:14:44 +0800 Subject: [PATCH 16/51] vt: delete unneeded function unregister_con_driver Now there is no place use unregister_con_driver, and we can achieve unregister_con_driver's function with unregister_con_driver easily, so just delete it to reduce code size and duplication. Signed-off-by: Wang YanQing Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 13 +------------ include/linux/console.h | 1 - 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 8f4a71aa971e..405e1c90ada2 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3545,7 +3545,7 @@ err: /** - * 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 @@ -3555,17 +3555,6 @@ err: * * 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; diff --git a/include/linux/console.h b/include/linux/console.h index 8f9bd966dbc6..7571a16bd653 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -75,7 +75,6 @@ extern const struct consw newport_con; /* SGI Newport console */ extern const struct consw prom_con; /* SPARC PROM console */ int con_is_bound(const struct consw *csw); -int unregister_con_driver(const struct consw *csw); int do_unregister_con_driver(const struct consw *csw); int do_take_over_console(const struct consw *sw, int first, int last, int deflt); void give_up_console(const struct consw *sw); From 4898e640caf03fdbaf2122d5a33949bf3e4a5b34 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 16 Apr 2013 06:15:50 -0400 Subject: [PATCH 17/51] tty: Add timed, writer-prioritized rw semaphore The semantics of a rw semaphore are almost ideally suited for tty line discipline lifetime management; multiple active threads obtain "references" (read locks) while performing i/o to prevent the loss or change of the current line discipline (write lock). Unfortunately, the existing rw_semaphore is ill-suited in other ways; 1) TIOCSETD ioctl (change line discipline) expects to return an error if the line discipline cannot be exclusively locked within 5 secs. Lock wait timeouts are not supported by rwsem. 2) A tty hangup is expected to halt and scrap pending i/o, so exclusive locking must be prioritized. Writer priority is not supported by rwsem. Add ld_semaphore which implements these requirements in a semantically similar way to rw_semaphore. Writer priority is handled by separate wait lists for readers and writers. Pending write waits are priortized before existing read waits and prevent further read locks. Wait timeouts are trivially added, but obviously change the lock semantics as lock attempts can fail (but only due to timeout). This implementation incorporates the write-lock stealing work of Michel Lespinasse . Cc: Michel Lespinasse Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/Makefile | 2 +- drivers/tty/tty_ldsem.c | 453 ++++++++++++++++++++++++++++++++++++++ include/linux/tty_ldisc.h | 46 ++++ 3 files changed, 500 insertions(+), 1 deletion(-) create mode 100644 drivers/tty/tty_ldsem.c diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 6b78399bc7c9..58ad1c05b7f8 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/tty_ldsem.c b/drivers/tty/tty_ldsem.c new file mode 100644 index 000000000000..22fad8ad5ac2 --- /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 . + * + * 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 . + * + * Copyright (C) 2013 Peter Hurley + * + * This file may be redistributed under the terms of the GNU General Public + * License v2. + */ + +#include +#include +#include +#include +#include + + +#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/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 58390c73df8b..7b24bbd85ea8 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -110,6 +110,52 @@ #include #include + +/* + * the semaphore definition + */ +struct ld_semaphore { + long count; + raw_spinlock_t wait_lock; + unsigned int wait_readers; + struct list_head read_wait; + struct list_head write_wait; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif +}; + +extern void __init_ldsem(struct ld_semaphore *sem, const char *name, + struct lock_class_key *key); + +#define init_ldsem(sem) \ +do { \ + static struct lock_class_key __key; \ + \ + __init_ldsem((sem), #sem, &__key); \ +} while (0) + + +extern int ldsem_down_read(struct ld_semaphore *sem, long timeout); +extern int ldsem_down_read_trylock(struct ld_semaphore *sem); +extern int ldsem_down_write(struct ld_semaphore *sem, long timeout); +extern int ldsem_down_write_trylock(struct ld_semaphore *sem); +extern void ldsem_up_read(struct ld_semaphore *sem); +extern void ldsem_up_write(struct ld_semaphore *sem); + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +extern int ldsem_down_read_nested(struct ld_semaphore *sem, int subclass, + long timeout); +extern int ldsem_down_write_nested(struct ld_semaphore *sem, int subclass, + long timeout); +#else +# define ldsem_down_read_nested(sem, subclass, timeout) \ + ldsem_down_read(sem, timeout) +# define ldsem_down_write_nested(sem, subclass, timeout) \ + ldsem_down_write(sem, timeout) +#endif + + struct tty_ldisc_ops { int magic; char *name; From 155957f56c3537dbb63bbb63c39067987c061a6d Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Tue, 21 May 2013 13:15:12 +0800 Subject: [PATCH 18/51] TTY:vt: convert remain take_over_console's users to do_take_over_console Impact: 1:convert all remain take_over_console to do_take_over_console 2:update take_over_console to do_take_over_console in comment Commit dc9641895abb ("vt: delete unneeded functions register_con_driver|take_over_console") delete take_over_console, but forget to convert remain take_over_console's users to new API do_take_over_console, this patch fix it. Reported-by: Stephen Rothwell Signed-off-by: Wang YanQing Signed-off-by: Greg Kroah-Hartman --- arch/alpha/kernel/console.c | 4 +++- arch/alpha/kernel/process.c | 4 +++- arch/mips/pci/pci-bcm1480.c | 4 +++- arch/mips/pci/pci-sb1250.c | 4 +++- arch/parisc/kernel/setup.c | 2 +- drivers/tty/vt/vt.c | 2 +- drivers/usb/misc/sisusbvga/sisusb_con.c | 18 +++++++++++------- drivers/video/console/fbcon.c | 2 +- drivers/video/console/mdacon.c | 8 ++++++-- drivers/video/console/newport_con.c | 9 ++++++--- drivers/video/console/sticon.c | 6 +++++- 11 files changed, 43 insertions(+), 20 deletions(-) diff --git a/arch/alpha/kernel/console.c b/arch/alpha/kernel/console.c index da711e37fc97..6a61deed4a85 100644 --- a/arch/alpha/kernel/console.c +++ b/arch/alpha/kernel/console.c @@ -61,7 +61,9 @@ locate_and_init_vga(void *(*sel_func)(void *, void *)) /* Set the VGA hose and init the new console. */ pci_vga_hose = hose; - take_over_console(&vga_con, 0, MAX_NR_CONSOLES-1, 1); + console_lock(); + do_take_over_console(&vga_con, 0, MAX_NR_CONSOLES-1, 1); + console_unlock(); } void __init diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index ab80a80d38a2..f2360a74e5d5 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -117,7 +117,9 @@ common_shutdown_1(void *generic_ptr) if (in_interrupt()) irq_exit(); /* This has the effect of resetting the VGA video origin. */ - take_over_console(&dummy_con, 0, MAX_NR_CONSOLES-1, 1); + console_lock(); + do_take_over_console(&dummy_con, 0, MAX_NR_CONSOLES-1, 1); + console_unlock(); #endif pci_restore_srm_config(); set_hae(srm_hae); diff --git a/arch/mips/pci/pci-bcm1480.c b/arch/mips/pci/pci-bcm1480.c index e2e69e1e9fe1..44dd5aa2e36f 100644 --- a/arch/mips/pci/pci-bcm1480.c +++ b/arch/mips/pci/pci-bcm1480.c @@ -257,7 +257,9 @@ static int __init bcm1480_pcibios_init(void) register_pci_controller(&bcm1480_controller); #ifdef CONFIG_VGA_CONSOLE - take_over_console(&vga_con, 0, MAX_NR_CONSOLES-1, 1); + console_lock(); + do_take_over_console(&vga_con, 0, MAX_NR_CONSOLES-1, 1); + console_unlock(); #endif return 0; } diff --git a/arch/mips/pci/pci-sb1250.c b/arch/mips/pci/pci-sb1250.c index cdefcc4cb8d4..fc634aeda4a5 100644 --- a/arch/mips/pci/pci-sb1250.c +++ b/arch/mips/pci/pci-sb1250.c @@ -283,7 +283,9 @@ static int __init sb1250_pcibios_init(void) register_pci_controller(&sb1250_controller); #ifdef CONFIG_VGA_CONSOLE - take_over_console(&vga_con, 0, MAX_NR_CONSOLES - 1, 1); + console_lock(); + do_take_over_console(&vga_con, 0, MAX_NR_CONSOLES - 1, 1); + console_unlock(); #endif return 0; } diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c index 76b63e726a53..60c1ae604876 100644 --- a/arch/parisc/kernel/setup.c +++ b/arch/parisc/kernel/setup.c @@ -155,7 +155,7 @@ void __init setup_arch(char **cmdline_p) #endif #if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE) - conswitchp = &dummy_con; /* we use take_over_console() later ! */ + conswitchp = &dummy_con; /* we use do_take_over_console() later ! */ #endif } diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 405e1c90ada2..d4ba2ba78d13 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3592,7 +3592,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) { diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c index 411e605f448a..a638c4e9a947 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_con.c +++ b/drivers/usb/misc/sisusbvga/sisusb_con.c @@ -208,7 +208,7 @@ sisusbcon_init(struct vc_data *c, int init) struct sisusb_usb_data *sisusb; int cols, rows; - /* This is called by take_over_console(), + /* This is called by do_take_over_console(), * ie by us/under our control. It is * only called after text mode and fonts * are set up/restored. @@ -273,7 +273,7 @@ sisusbcon_deinit(struct vc_data *c) struct sisusb_usb_data *sisusb; int i; - /* This is called by take_over_console() + /* This is called by do_take_over_console() * and others, ie not under our control. */ @@ -1490,8 +1490,9 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last) mutex_unlock(&sisusb->lock); /* Now grab the desired console(s) */ - ret = take_over_console(&sisusb_con, first - 1, last - 1, 0); - + console_lock(); + ret = do_take_over_console(&sisusb_con, first - 1, last - 1, 0); + console_unlock(); if (!ret) sisusb->haveconsole = 1; else { @@ -1535,11 +1536,14 @@ sisusb_console_exit(struct sisusb_usb_data *sisusb) if (sisusb->haveconsole) { for (i = 0; i < MAX_NR_CONSOLES; i++) - if (sisusb->havethisconsole[i]) - take_over_console(&sisusb_dummy_con, i, i, 0); + if (sisusb->havethisconsole[i]) { + console_lock(); + do_take_over_console(&sisusb_dummy_con, i, i, 0); + console_unlock(); /* At this point, con_deinit for all our - * consoles is executed by take_over_console(). + * consoles is executed by do_take_over_console(). */ + } sisusb->haveconsole = 0; } diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index a09c667c0c3d..d55b33757465 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -873,7 +873,7 @@ static int set_con2fb_map(int unit, int newidx, int user) /* * Low Level Operations */ -/* NOTE: fbcon cannot be __init: it may be called from take_over_console later */ +/* NOTE: fbcon cannot be __init: it may be called from do_take_over_console later */ static int var_to_display(struct display *disp, struct fb_var_screeninfo *var, struct fb_info *info) diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c index 0b67866cae10..296e94561556 100644 --- a/drivers/video/console/mdacon.c +++ b/drivers/video/console/mdacon.c @@ -585,10 +585,14 @@ static const struct consw mda_con = { int __init mda_console_init(void) { + int err; + if (mda_first_vc > mda_last_vc) return 1; - - return take_over_console(&mda_con, mda_first_vc-1, mda_last_vc-1, 0); + console_lock(); + err = do_take_over_console(&mda_con, mda_first_vc-1, mda_last_vc-1, 0); + console_unlock(); + return err; } static void __exit mda_console_exit(void) diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index b05afd03729e..a6ab9299813c 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -297,7 +297,7 @@ static void newport_exit(void) newport_set_def_font(i, NULL); } -/* Can't be __init, take_over_console may call it later */ +/* Can't be __init, do_take_over_console may call it later */ static const char *newport_startup(void) { int i; @@ -746,6 +746,7 @@ static int newport_probe(struct gio_device *dev, const struct gio_device_id *id) { unsigned long newport_addr; + int err; if (!dev->resource.start) return -EINVAL; @@ -759,8 +760,10 @@ static int newport_probe(struct gio_device *dev, npregs = (struct newport_regs *)/* ioremap cannot fail */ ioremap(newport_addr, sizeof(struct newport_regs)); - - return take_over_console(&newport_con, 0, MAX_NR_CONSOLES - 1, 1); + console_lock(); + err = do_take_over_console(&newport_con, 0, MAX_NR_CONSOLES - 1, 1); + console_unlock(); + return err; } static void newport_remove(struct gio_device *dev) diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index 491c1c1baf4c..5f65ca3d8564 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -372,6 +372,7 @@ static const struct consw sti_con = { static int __init sticonsole_init(void) { + int err; /* already initialized ? */ if (sticon_sti) return 0; @@ -382,7 +383,10 @@ static int __init sticonsole_init(void) if (conswitchp == &dummy_con) { printk(KERN_INFO "sticon: Initializing STI text console.\n"); - return take_over_console(&sti_con, 0, MAX_NR_CONSOLES - 1, 1); + console_lock(); + err = do_take_over_console(&sti_con, 0, MAX_NR_CONSOLES - 1, 1); + console_unlock(); + return err; } return 0; } From 6b5f146535fe6969aeded9f00b0bc42b3783f7fd Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Tue, 21 May 2013 23:29:11 +0800 Subject: [PATCH 19/51] TTY:console: update document console.txt Update Documentatin/console/console.txt to reflect the following changes: take_over_console|register_con_driver|unregister_con_driver|bind_con_driver had gone, they had been replace by the new caller hold console lock version, do_take_over_console|do_register_con_driver|do_unregister_con_driver|do_bind_con_driver. This patch also fix a place include wrong description about give_up_console. Signed-off-by: Wang YanQing Signed-off-by: Greg Kroah-Hartman --- Documentation/console/console.txt | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Documentation/console/console.txt b/Documentation/console/console.txt index 926cf1b5e63e..f93810d599ad 100644 --- a/Documentation/console/console.txt +++ b/Documentation/console/console.txt @@ -12,20 +12,20 @@ The second type has to be explicitly loaded and unloaded. This will be called any time with each driver sharing the console with other drivers including the system driver. However, modular drivers cannot take over the console that is currently occupied by another modular driver. (Exception: Drivers that -call take_over_console() will succeed in the takeover regardless of the type +call do_take_over_console() will succeed in the takeover regardless of the type of driver occupying the consoles.) They can only take over the console that is occupied by the system driver. In the same token, if the modular driver is released by the console, the system driver will take over. Modular drivers, from the programmer's point of view, has to call: - take_over_console() - load and bind driver to console layer - give_up_console() - unbind and unload driver + do_take_over_console() - load and bind driver to console layer + give_up_console() - unload driver, it will only work if driver is fully unbond In newer kernels, the following are also available: - register_con_driver() - unregister_con_driver() + do_register_con_driver() + do_unregister_con_driver() If sysfs is enabled, the contents of /sys/class/vtconsole can be examined. This shows the console backends currently registered by the @@ -94,12 +94,12 @@ for more details). Notes for developers: ===================== -take_over_console() is now broken up into: +do_take_over_console() is now broken up into: - register_con_driver() - bind_con_driver() - private function + do_register_con_driver() + do_bind_con_driver() - private function -give_up_console() is a wrapper to unregister_con_driver(), and a driver must +give_up_console() is a wrapper to do_unregister_con_driver(), and a driver must be fully unbound for this call to succeed. con_is_bound() will check if the driver is bound or not. @@ -109,10 +109,10 @@ Guidelines for console driver writers: In order for binding to and unbinding from the console to properly work, console drivers must follow these guidelines: -1. All drivers, except system drivers, must call either register_con_driver() - or take_over_console(). register_con_driver() will just add the driver to +1. All drivers, except system drivers, must call either do_register_con_driver() + or do_take_over_console(). do_register_con_driver() will just add the driver to the console's internal list. It won't take over the - console. take_over_console(), as it name implies, will also take over (or + console. do_take_over_console(), as it name implies, will also take over (or bind to) the console. 2. All resources allocated during con->con_init() must be released in @@ -128,10 +128,10 @@ console drivers must follow these guidelines: rebind the driver to the console arrives. 4. Upon exit of the driver, ensure that the driver is totally unbound. If the - condition is satisfied, then the driver must call unregister_con_driver() + condition is satisfied, then the driver must call do_unregister_con_driver() or give_up_console(). -5. unregister_con_driver() can also be called on conditions which make it +5. do_unregister_con_driver() can also be called on conditions which make it impossible for the driver to service console requests. This can happen with the framebuffer console that suddenly lost all of its drivers. From 86c346de5db0b82087d88ec04d0a404d293c420d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 22 May 2013 17:06:29 +0530 Subject: [PATCH 20/51] serial: vt8500: Remove redundant use of of_match_ptr macro 'wmt_dt_ids' is always compiled in. Hence of_match_ptr is not necessary. Signed-off-by: Sachin Kamat Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/vt8500_serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 1a8bc2275ea4..48af43de3467 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, }, }; From 9d141cb97703439255e21bbc95903eda32324a3d Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Wed, 22 May 2013 15:15:24 -0300 Subject: [PATCH 21/51] serial/JSM: change maintainer to myself Lucas has moved on. Since his email address does not work any more, it's not expected to get his ack. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index fd3a495a0005..3d87ac33f503 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4566,7 +4566,7 @@ F: fs/jbd2/ F: include/linux/jbd2.h JSM Neo PCI based serial card -M: Lucas Tavares +M: Thadeu Lima de Souza Cascardo L: linux-serial@vger.kernel.org S: Maintained F: drivers/tty/serial/jsm/ From 696faedd616e202f5c510cd03dcc8853c11ca6db Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 23 May 2013 19:39:36 +0900 Subject: [PATCH 22/51] serial: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Also, unnecessary dev_set_drvdata() is removed, because the driver core clears the driver data to NULL after device_release or on probe failure. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/cpm_uart/cpm_uart_core.c | 4 ++-- drivers/tty/serial/mpc52xx_uart.c | 9 ++++----- drivers/tty/serial/of_serial.c | 4 ++-- drivers/tty/serial/sc26xx.c | 5 ++--- drivers/tty/serial/sunhv.c | 6 ++---- drivers/tty/serial/sunsab.c | 6 ++---- drivers/tty/serial/sunsu.c | 8 +++----- drivers/tty/serial/sunzilog.c | 6 ++---- drivers/tty/serial/ucc_uart.c | 5 ++--- drivers/tty/serial/xilinx_uartps.c | 6 ++---- 10 files changed, 23 insertions(+), 36 deletions(-) diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index 97f4e1858649..f7672cae5321 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/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 510fa986cf9e..2c03904359b9 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -1362,15 +1362,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); @@ -1382,7 +1381,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); @@ -1393,7 +1392,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 39c7ea4cb14f..2caf9c6f6149 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/sc26xx.c b/drivers/tty/serial/sc26xx.c index c9735680762d..4b1434d53e9d 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/sunhv.c b/drivers/tty/serial/sunhv.c index ba60708053e0..cf86e729532b 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 a422c8b55a47..5d6136b2a04a 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 0d8465728473..699cc1b5f6aa 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 813ef8eb8eff..135a15203532 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 7355303dad99..c8ab8e45e1b9 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; diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index b5f655d10098..6c9174530422 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,13 +993,12 @@ 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; From 2574b27eee4f00f2773d1c209218b3f4cf34a3ca Mon Sep 17 00:00:00 2001 From: Matteo Facchinetti Date: Fri, 24 May 2013 20:24:58 +0200 Subject: [PATCH 23/51] serial/mpc52xx_uart: prepare for adding MPC5125 PSC UART support MPC5125 PSC controller has different register layout than MPC5121. To support MPC5125 PSC in this driver we have to provide further psc_ops functions for SoC specific register accesses. Add new register access functions to the psc_ops structure and provide MPC52xx and MPC512x specific implementation for them. Then replace remaining direct register accesses in the driver by appropriate psc_ops function calls. The subsequent patch can now add MPC5125 specific set of psc_ops functions. Signed-off-by: Vladimir Ermakov Signed-off-by: Matteo Facchinetti Signed-off-by: Anatolij Gustschin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mpc52xx_uart.c | 161 ++++++++++++++++++++++-------- 1 file changed, 119 insertions(+), 42 deletions(-) diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 2c03904359b9..019ecb1c6847 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -112,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 */ @@ -124,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) @@ -294,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 = { @@ -315,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 */ @@ -585,8 +671,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; @@ -603,17 +699,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; @@ -663,15 +756,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 @@ -681,9 +766,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); } @@ -691,7 +776,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) { @@ -707,15 +791,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; } @@ -723,15 +807,13 @@ 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); @@ -744,7 +826,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; @@ -808,13 +889,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 */ @@ -824,8 +903,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); @@ -953,7 +1032,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 | @@ -973,7 +1052,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); @@ -1056,7 +1135,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)); @@ -1097,14 +1176,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; From 1f48c499df414a50e28a4a2b1332ff0e8e1af9a8 Mon Sep 17 00:00:00 2001 From: Matteo Facchinetti Date: Fri, 24 May 2013 20:24:59 +0200 Subject: [PATCH 24/51] serial/mpc52xx_uart: add MPC5125 PSC support Add MPC5125 PSC register layout structure, MPC5125 specific psc_ops function set and the compatible string. Signed-off-by: Vladimir Ermakov Signed-off-by: Matteo Facchinetti Signed-off-by: Anatolij Gustschin Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/include/asm/mpc52xx_psc.h | 49 +++++ drivers/tty/serial/mpc52xx_uart.c | 241 +++++++++++++++++++++++++ 2 files changed, 290 insertions(+) diff --git a/arch/powerpc/include/asm/mpc52xx_psc.h b/arch/powerpc/include/asm/mpc52xx_psc.h index 2966df604221..d0ece257d310 100644 --- a/arch/powerpc/include/asm/mpc52xx_psc.h +++ b/arch/powerpc/include/asm/mpc52xx_psc.h @@ -299,4 +299,53 @@ struct mpc512x_psc_fifo { #define rxdata_32 rxdata.rxdata_32 }; +struct mpc5125_psc { + u8 mr1; /* PSC + 0x00 */ + u8 reserved0[3]; + u8 mr2; /* PSC + 0x04 */ + u8 reserved1[3]; + struct { + u16 status; /* PSC + 0x08 */ + u8 reserved2[2]; + u8 clock_select; /* PSC + 0x0c */ + u8 reserved3[3]; + } sr_csr; + u8 command; /* PSC + 0x10 */ + u8 reserved4[3]; + union { /* PSC + 0x14 */ + u8 buffer_8; + u16 buffer_16; + u32 buffer_32; + } buffer; + struct { + u8 ipcr; /* PSC + 0x18 */ + u8 reserved5[3]; + u8 acr; /* PSC + 0x1c */ + u8 reserved6[3]; + } ipcr_acr; + struct { + u16 isr; /* PSC + 0x20 */ + u8 reserved7[2]; + u16 imr; /* PSC + 0x24 */ + u8 reserved8[2]; + } isr_imr; + u8 ctur; /* PSC + 0x28 */ + u8 reserved9[3]; + u8 ctlr; /* PSC + 0x2c */ + u8 reserved10[3]; + u32 ccr; /* PSC + 0x30 */ + u32 ac97slots; /* PSC + 0x34 */ + u32 ac97cmd; /* PSC + 0x38 */ + u32 ac97data; /* PSC + 0x3c */ + u8 reserved11[4]; + u8 ip; /* PSC + 0x44 */ + u8 reserved12[3]; + u8 op1; /* PSC + 0x48 */ + u8 reserved13[3]; + u8 op0; /* PSC + 0x4c */ + u8 reserved14[3]; + u32 sicr; /* PSC + 0x50 */ + u8 reserved15[4]; /* make eq. sizeof(mpc52xx_psc) */ +}; + #endif /* __ASM_MPC52xx_PSC_H__ */ diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 019ecb1c6847..9ba194590a80 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -648,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, @@ -1371,6 +1611,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 {}, }; From 86b40567b9178d2de8bbc08b04c98c8373ddf194 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Sat, 1 Jun 2013 16:30:06 +0900 Subject: [PATCH 25/51] tty: replace strict_strtoul() with kstrtoul() The usage of strict_strtoul() is not preferred, because strict_strtoul() is obsolete. Thus, kstrtoul() should be used. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/tty/hvc/hvc_iucv.c | 2 +- drivers/tty/sysrq.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index b6f7d52f7c35..9d47f50c2755 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/sysrq.c b/drivers/tty/sysrq.c index b51c15408ff3..5f68f2cfdfd0 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; From 147275983e42e07f4c9f3764a91ff65be026aea9 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 29 May 2013 13:47:09 +0800 Subject: [PATCH 26/51] serial: samsung: missing uart_unregister_driver() on error in s3c24xx_serial_modinit() Add the missing uart_unregister_driver() before return from s3c24xx_serial_modinit() in the error handling case. Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 89429410a245..1c2b8c31fde1 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1798,7 +1798,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) From 1b615beec7e8a1c40158c69a142e9e9832a6f7ad Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Wed, 29 May 2013 10:33:16 +0800 Subject: [PATCH 27/51] driver: tty: add missing unregister in err case when platform_driver_register broken, we should unregister ucc_uart_driver Signed-off-by: Libo chen Acked-by: Timur Tabi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ucc_uart.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index c8ab8e45e1b9..88317482b81f 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -1517,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; } From 2cb5a2fa219897e5d4ccd8831e8d0f3d6e6f0f23 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 1 Jun 2013 11:18:13 +0200 Subject: [PATCH 28/51] serial: omap: repair building without PM_SLEEP A recent bug fix in 3.10, ddd85e225c "serial: omap: prevent runtime PM for "no_console_suspend"", introduced a regression from an obvious typo: drivers/tty/serial/omap-serial.c:1677:14: error: 'serial_omap_complete' undeclared here (not in a function) This changes the incorrectly added macro to the one that we need instead. Signed-off-by: Arnd Bergmann Acked-by: Kevin Hilman Signed-off-by: Bin Liu Acked-by: Sourav Poddar Tested-by: Sourav Poddar Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/omap-serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 393a8eb98d72..1aaeca8727d4 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1326,7 +1326,7 @@ static int serial_omap_resume(struct device *dev) } #else #define serial_omap_prepare NULL -#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) From 20ff2fe60aa86683a68cd369c919ae6a98059c80 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Thu, 30 May 2013 14:07:12 +0800 Subject: [PATCH 29/51] serial: imx: add support for DTE mode The uart works in the DCE mode by default, but sometime we need it works at the DTE mode. This patch adds the support for the DTE mode. Signed-off-by: Huang Shijie Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/tty/serial/fsl-imx-uart.txt | 3 +++ drivers/tty/serial/imx.c | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt b/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt index b462d0c54823..c662eb36be29 100644 --- a/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt +++ b/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt @@ -8,6 +8,8 @@ Required properties: Optional properties: - fsl,uart-has-rtscts : Indicate the uart has rts and cts - fsl,irda-mode : Indicate the uart supports irda mode +- fsl,dte-mode : Indicate the uart works in DTE mode. The uart works + is DCE mode by default. Example: @@ -16,4 +18,5 @@ serial@73fbc000 { reg = <0x73fbc000 0x4000>; interrupts = <31>; fsl,uart-has-rtscts; + fsl,dte-mode; }; diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 72bc1dbcd055..381a2d79593c 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; @@ -1020,6 +1021,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); @@ -1444,6 +1447,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; From e943f58ea84a80cc88dfceb6a5ed788d0ba24a1e Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Tue, 28 May 2013 09:29:36 +0200 Subject: [PATCH 30/51] stallion: final cleanup Support for the Stallion multiport serial drivers was removed in v3.1. Clean up their last references in the tree: mainly an outdated Kconfig entry and unneeded documentation. Signed-off-by: Paul Bolle Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- Documentation/serial/00-INDEX | 2 - Documentation/serial/stallion.txt | 392 ------------------------------ drivers/char/Kconfig | 12 - drivers/tty/serial/8250/Kconfig | 5 +- 4 files changed, 2 insertions(+), 409 deletions(-) delete mode 100644 Documentation/serial/stallion.txt diff --git a/Documentation/serial/00-INDEX b/Documentation/serial/00-INDEX index f7b0c7dc25ef..1f1b22fbd739 100644 --- a/Documentation/serial/00-INDEX +++ b/Documentation/serial/00-INDEX @@ -16,8 +16,6 @@ serial-rs485.txt - info about RS485 structures and support in the kernel. specialix.txt - info on hardware/driver for specialix IO8+ multiport serial card. -stallion.txt - - info on using the Stallion multiport serial driver. sx.txt - info on the Specialix SX/SI multiport serial driver. tty.txt diff --git a/Documentation/serial/stallion.txt b/Documentation/serial/stallion.txt deleted file mode 100644 index 4d798c0cb5cb..000000000000 --- a/Documentation/serial/stallion.txt +++ /dev/null @@ -1,392 +0,0 @@ -* NOTE - This is an unmaintained driver. Lantronix, which bought Stallion -technologies, is not active in driver maintenance, and they have no information -on when or if they will have a 2.6 driver. - -James Nelson - 12-12-2004 - -Stallion Multiport Serial Driver Readme ---------------------------------------- - -Copyright (C) 1994-1999, Stallion Technologies. - -Version: 5.5.1 -Date: 28MAR99 - - - -1. INTRODUCTION - -There are two drivers that work with the different families of Stallion -multiport serial boards. One is for the Stallion smart boards - that is -EasyIO, EasyConnection 8/32 and EasyConnection 8/64-PCI, the other for -the true Stallion intelligent multiport boards - EasyConnection 8/64 -(ISA, EISA), EasyConnection/RA-PCI, ONboard and Brumby. - -If you are using any of the Stallion intelligent multiport boards (Brumby, -ONboard, EasyConnection 8/64 (ISA, EISA), EasyConnection/RA-PCI) with -Linux you will need to get the driver utility package. This contains a -firmware loader and the firmware images necessary to make the devices operate. - -The Stallion Technologies ftp site, ftp.stallion.com, will always have -the latest version of the driver utility package. - -ftp://ftp.stallion.com/drivers/ata5/Linux/ata-linux-550.tar.gz - -As of the printing of this document the latest version of the driver -utility package is 5.5.0. If a later version is now available then you -should use the latest version. - -If you are using the EasyIO, EasyConnection 8/32 or EasyConnection 8/64-PCI -boards then you don't need this package, although it does have a serial stats -display program. - -If you require DIP switch settings, or EISA configuration files, or any -other information related to Stallion boards then have a look at Stallion's -web pages at http://www.stallion.com. - - - -2. INSTALLATION - -The drivers can be used as loadable modules or compiled into the kernel. -You can choose which when doing a "config" on the kernel. - -All ISA, and EISA boards that you want to use need to be configured into -the driver(s). All PCI boards will be automatically detected when you load -the driver - so they do not need to be entered into the driver(s) -configuration structure. Note that kernel PCI support is required to use PCI -boards. - -There are two methods of configuring ISA and EISA boards into the drivers. -If using the driver as a loadable module then the simplest method is to pass -the driver configuration as module arguments. The other method is to modify -the driver source to add configuration lines for each board in use. - -If you have pre-built Stallion driver modules then the module argument -configuration method should be used. A lot of Linux distributions come with -pre-built driver modules in /lib/modules/X.Y.Z/misc for the kernel in use. -That makes things pretty simple to get going. - - -2.1 MODULE DRIVER CONFIGURATION: - -The simplest configuration for modules is to use the module load arguments -to configure any ISA or EISA boards. PCI boards are automatically -detected, so do not need any additional configuration at all. - -If using EasyIO, EasyConnection 8/32 ISA, or EasyConnection 8/63-PCI -boards then use the "stallion" driver module, Otherwise if you are using -an EasyConnection 8/64 ISA or EISA, EasyConnection/RA-PCI, ONboard, -Brumby or original Stallion board then use the "istallion" driver module. - -Typically to load up the smart board driver use: - - modprobe stallion - -This will load the EasyIO and EasyConnection 8/32 driver. It will output a -message to say that it loaded and print the driver version number. It will -also print out whether it found the configured boards or not. These messages -may not appear on the console, but typically are always logged to -/var/adm/messages or /var/log/syslog files - depending on how the klogd and -syslogd daemons are setup on your system. - -To load the intelligent board driver use: - - modprobe istallion - -It will output similar messages to the smart board driver. - -If not using an auto-detectable board type (that is a PCI board) then you -will also need to supply command line arguments to the modprobe command -when loading the driver. The general form of the configuration argument is - - board?=[,[,][,]] - -where: - - board? -- specifies the arbitrary board number of this board, - can be in the range 0 to 3. - - name -- textual name of this board. The board name is the common - board name, or any "shortened" version of that. The board - type number may also be used here. - - ioaddr -- specifies the I/O address of this board. This argument is - optional, but should generally be specified. - - addr -- optional second address argument. Some board types require - a second I/O address, some require a memory address. The - exact meaning of this argument depends on the board type. - - irq -- optional IRQ line used by this board. - -Up to 4 board configuration arguments can be specified on the load line. -Here is some examples: - - modprobe stallion board0=easyio,0x2a0,5 - -This configures an EasyIO board as board 0 at I/O address 0x2a0 and IRQ 5. - - modprobe istallion board3=ec8/64,0x2c0,0xcc000 - -This configures an EasyConnection 8/64 ISA as board 3 at I/O address 0x2c0 at -memory address 0xcc000. - - modprobe stallion board1=ec8/32-at,0x2a0,0x280,10 - -This configures an EasyConnection 8/32 ISA board at primary I/O address 0x2a0, -secondary address 0x280 and IRQ 10. - -You will probably want to enter this module load and configuration information -into your system startup scripts so that the drivers are loaded and configured -on each system boot. Typically configuration files are put in the -/etc/modprobe.d/ directory. - - -2.2 STATIC DRIVER CONFIGURATION: - -For static driver configuration you need to modify the driver source code. -Entering ISA and EISA boards into the driver(s) configuration structure -involves editing the driver(s) source file. It's pretty easy if you follow -the instructions below. Both drivers can support up to 4 boards. The smart -card driver (the stallion.c driver) supports any combination of EasyIO and -EasyConnection 8/32 boards (up to a total of 4). The intelligent driver -supports any combination of ONboards, Brumbys, Stallions and EasyConnection -8/64 (ISA and EISA) boards (up to a total of 4). - -To set up the driver(s) for the boards that you want to use you need to -edit the appropriate driver file and add configuration entries. - -If using EasyIO or EasyConnection 8/32 ISA boards, - In drivers/char/stallion.c: - - find the definition of the stl_brdconf array (of structures) - near the top of the file - - modify this to match the boards you are going to install - (the comments before this structure should help) - - save and exit - -If using ONboard, Brumby, Stallion or EasyConnection 8/64 (ISA or EISA) -boards, - In drivers/char/istallion.c: - - find the definition of the stli_brdconf array (of structures) - near the top of the file - - modify this to match the boards you are going to install - (the comments before this structure should help) - - save and exit - -Once you have set up the board configurations then you are ready to build -the kernel or modules. - -When the new kernel is booted, or the loadable module loaded then the -driver will emit some kernel trace messages about whether the configured -boards were detected or not. Depending on how your system logger is set -up these may come out on the console, or just be logged to -/var/adm/messages or /var/log/syslog. You should check the messages to -confirm that all is well. - - -2.3 SHARING INTERRUPTS - -It is possible to share interrupts between multiple EasyIO and -EasyConnection 8/32 boards in an EISA system. To do this you must be using -static driver configuration, modifying the driver source code to add driver -configuration. Then a couple of extra things are required: - -1. When entering the board resources into the stallion.c file you need to - mark the boards as using level triggered interrupts. Do this by replacing - the "0" entry at field position 6 (the last field) in the board - configuration structure with a "1". (This is the structure that defines - the board type, I/O locations, etc. for each board). All boards that are - sharing an interrupt must be set this way, and each board should have the - same interrupt number specified here as well. Now build the module or - kernel as you would normally. - -2. When physically installing the boards into the system you must enter - the system EISA configuration utility. You will need to install the EISA - configuration files for *all* the EasyIO and EasyConnection 8/32 boards - that are sharing interrupts. The Stallion EasyIO and EasyConnection 8/32 - EISA configuration files required are supplied by Stallion Technologies - on the EASY Utilities floppy diskette (usually supplied in the box with - the board when purchased. If not, you can pick it up from Stallion's FTP - site, ftp.stallion.com). You will need to edit the board resources to - choose level triggered interrupts, and make sure to set each board's - interrupt to the same IRQ number. - -You must complete both the above steps for this to work. When you reboot -or load the driver your EasyIO and EasyConnection 8/32 boards will be -sharing interrupts. - - -2.4 USING HIGH SHARED MEMORY - -The EasyConnection 8/64-EI, ONboard and Stallion boards are capable of -using shared memory addresses above the usual 640K - 1Mb range. The ONboard -ISA and the Stallion boards can be programmed to use memory addresses up to -16Mb (the ISA bus addressing limit), and the EasyConnection 8/64-EI and -ONboard/E can be programmed for memory addresses up to 4Gb (the EISA bus -addressing limit). - -The higher than 1Mb memory addresses are fully supported by this driver. -Just enter the address as you normally would for a lower than 1Mb address -(in the driver's board configuration structure). - - - -2.5 TROUBLE SHOOTING - -If a board is not found by the driver but is actually in the system then the -most likely problem is that the I/O address is wrong. Change the module load -argument for the loadable module form. Or change it in the driver stallion.c -or istallion.c configuration structure and rebuild the kernel or modules, or -change it on the board. - -On EasyIO and EasyConnection 8/32 boards the IRQ is software programmable, so -if there is a conflict you may need to change the IRQ used for a board. There -are no interrupts to worry about for ONboard, Brumby or EasyConnection 8/64 -(ISA and EISA) boards. The memory region on EasyConnection 8/64 and -ONboard boards is software programmable, but not on the Brumby boards. - - - -3. USING THE DRIVERS - -3.1 INTELLIGENT DRIVER OPERATION - -The intelligent boards also need to have their "firmware" code downloaded -to them. This is done via a user level application supplied in the driver -utility package called "stlload". Compile this program wherever you dropped -the package files, by typing "make". In its simplest form you can then type - - ./stlload -i cdk.sys - -in this directory and that will download board 0 (assuming board 0 is an -EasyConnection 8/64 or EasyConnection/RA board). To download to an -ONboard, Brumby or Stallion do: - - ./stlload -i 2681.sys - -Normally you would want all boards to be downloaded as part of the standard -system startup. To achieve this, add one of the lines above into the -/etc/rc.d/rc.S or /etc/rc.d/rc.serial file. To download each board just add -the "-b " option to the line. You will need to download code for -every board. You should probably move the stlload program into a system -directory, such as /usr/sbin. Also, the default location of the cdk.sys image -file in the stlload down-loader is /usr/lib/stallion. Create that directory -and put the cdk.sys and 2681.sys files in it. (It's a convenient place to put -them anyway). As an example your /etc/rc.d/rc.S file might have the -following lines added to it (if you had 3 boards): - - /usr/sbin/stlload -b 0 -i /usr/lib/stallion/cdk.sys - /usr/sbin/stlload -b 1 -i /usr/lib/stallion/2681.sys - /usr/sbin/stlload -b 2 -i /usr/lib/stallion/2681.sys - -The image files cdk.sys and 2681.sys are specific to the board types. The -cdk.sys will only function correctly on an EasyConnection 8/64 board. Similarly -the 2681.sys image fill only operate on ONboard, Brumby and Stallion boards. -If you load the wrong image file into a board it will fail to start up, and -of course the ports will not be operational! - -If you are using the modularized version of the driver you might want to put -the modprobe calls in the startup script as well (before the download lines -obviously). - - -3.2 USING THE SERIAL PORTS - -Once the driver is installed you will need to setup some device nodes to -access the serial ports. The simplest method is to use the /dev/MAKEDEV program. -It will automatically create device entries for Stallion boards. This will -create the normal serial port devices as /dev/ttyE# where# is the port number -starting from 0. A bank of 64 minor device numbers is allocated to each board, -so the first port on the second board is port 64,etc. A set of callout type -devices may also be created. They are created as the devices /dev/cue# where # -is the same as for the ttyE devices. - -For the most part the Stallion driver tries to emulate the standard PC system -COM ports and the standard Linux serial driver. The idea is that you should -be able to use Stallion board ports and COM ports interchangeably without -modifying anything but the device name. Anything that doesn't work like that -should be considered a bug in this driver! - -If you look at the driver code you will notice that it is fairly closely -based on the Linux serial driver (linux/drivers/char/serial.c). This is -intentional, obviously this is the easiest way to emulate its behavior! - -Since this driver tries to emulate the standard serial ports as much as -possible, most system utilities should work as they do for the standard -COM ports. Most importantly "stty" works as expected and "setserial" can -also be used (excepting the ability to auto-configure the I/O and IRQ -addresses of boards). Higher baud rates are supported in the usual fashion -through setserial or using the CBAUDEX extensions. Note that the EasyIO and -EasyConnection (all types) support at least 57600 and 115200 baud. The newer -EasyConnection XP modules and new EasyIO boards support 230400 and 460800 -baud as well. The older boards including ONboard and Brumby support a -maximum baud rate of 38400. - -If you are unfamiliar with how to use serial ports, then get the Serial-HOWTO -by Greg Hankins. It will explain everything you need to know! - - - -4. NOTES - -You can use both drivers at once if you have a mix of board types installed -in a system. However to do this you will need to change the major numbers -used by one of the drivers. Currently both drivers use major numbers 24, 25 -and 28 for their devices. Change one driver to use some other major numbers, -and then modify the mkdevnods script to make device nodes based on those new -major numbers. For example, you could change the istallion.c driver to use -major numbers 60, 61 and 62. You will also need to create device nodes with -different names for the ports, for example ttyF# and cuf#. - -The original Stallion board is no longer supported by Stallion Technologies. -Although it is known to work with the istallion driver. - -Finding a free physical memory address range can be a problem. The older -boards like the Stallion and ONboard need large areas (64K or even 128K), so -they can be very difficult to get into a system. If you have 16 Mb of RAM -then you have no choice but to put them somewhere in the 640K -> 1Mb range. -ONboards require 64K, so typically 0xd0000 is good, or 0xe0000 on some -systems. If you have an original Stallion board, "V4.0" or Rev.O, then you -need a 64K memory address space, so again 0xd0000 and 0xe0000 are good. -Older Stallion boards are a much bigger problem. They need 128K of address -space and must be on a 128K boundary. If you don't have a VGA card then -0xc0000 might be usable - there is really no other place you can put them -below 1Mb. - -Both the ONboard and old Stallion boards can use higher memory addresses as -well, but you must have less than 16Mb of RAM to be able to use them. Usual -high memory addresses used include 0xec0000 and 0xf00000. - -The Brumby boards only require 16Kb of address space, so you can usually -squeeze them in somewhere. Common addresses are 0xc8000, 0xcc000, or in -the 0xd0000 range. EasyConnection 8/64 boards are even better, they only -require 4Kb of address space, again usually 0xc8000, 0xcc000 or 0xd0000 -are good. - -If you are using an EasyConnection 8/64-EI or ONboard/E then usually the -0xd0000 or 0xe0000 ranges are the best options below 1Mb. If neither of -them can be used then the high memory support to use the really high address -ranges is the best option. Typically the 2Gb range is convenient for them, -and gets them well out of the way. - -The ports of the EasyIO-8M board do not have DCD or DTR signals. So these -ports cannot be used as real modem devices. Generally, when using these -ports you should only use the cueX devices. - -The driver utility package contains a couple of very useful programs. One -is a serial port statistics collection and display program - very handy -for solving serial port problems. The other is an extended option setting -program that works with the intelligent boards. - - - -5. DISCLAIMER - -The information contained in this document is believed to be accurate and -reliable. However, no responsibility is assumed by Stallion Technologies -Pty. Ltd. for its use, nor any infringements of patents or other rights -of third parties resulting from its use. Stallion Technologies reserves -the right to modify the design of its products and will endeavour to change -the information in manuals and accompanying documentation accordingly. - diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 3bb6fa3930be..14219972c745 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -15,18 +15,6 @@ config DEVKMEM kind of kernel debugging operations. When in doubt, say "N". -config STALDRV - bool "Stallion multiport serial support" - depends on SERIAL_NONSTANDARD - help - Stallion cards give you many serial ports. You would need something - like this to connect more than two modems to your Linux box, for - instance in order to become a dial-in server. If you say Y here, - you will be asked for your specific card model in the next - questions. Make sure to read - in this case. If you have never heard about all this, it's safe to - say N. - config SGI_SNSC bool "SGI Altix system controller communication support" depends on (IA64_SGI_SN2 || IA64_GENERIC) diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 80fe91e64a52..a1ba94d64885 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. From 28eb4274e7ec3ddba6711e3d13e374046b3c2230 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Tue, 4 Jun 2013 09:59:33 +0800 Subject: [PATCH 31/51] serial: imx: enable the clocks only when the uart is used Current code opens the clocks when the uart driver is probed. This will wastes some power if several uarts are enabled, but not really used. So close these clocks for uart, and enable the clocks only when the uart is used. Signed-off-by: Huang Shijie Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 381a2d79593c..831324a9745b 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -700,6 +700,14 @@ static int imx_startup(struct uart_port *port) int retval; unsigned long flags, temp; + retval = clk_prepare_enable(sport->clk_per); + if (retval) + goto error_out1; + + retval = clk_prepare_enable(sport->clk_ipg); + if (retval) + goto error_out1; + imx_setup_ufcr(sport, 0); /* disable the DREN bit (Data Ready interrupt enable) before @@ -885,6 +893,9 @@ static void imx_shutdown(struct uart_port *port) writel(temp, sport->port.membase + UCR1); spin_unlock_irqrestore(&sport->port.lock, flags); + + clk_disable_unprepare(sport->clk_per); + clk_disable_unprepare(sport->clk_ipg); } static void @@ -1563,6 +1574,9 @@ static int serial_imx_probe(struct platform_device *pdev) goto deinit; platform_set_drvdata(pdev, sport); + clk_disable_unprepare(sport->clk_per); + clk_disable_unprepare(sport->clk_ipg); + return 0; deinit: if (pdata && pdata->exit) @@ -1584,9 +1598,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); From c36d6006d8a33ae3a0befb120c9878a634775786 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Tue, 4 Jun 2013 14:20:43 +0200 Subject: [PATCH 32/51] tty: serial: Enable uartlite for ARM zynq Enable it in Kconfig. Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 7e7006fd404e..64250b19cd8d 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -576,7 +576,7 @@ config SERIAL_IMX_CONSOLE 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. From 7f25301d83432efb0e7bb3fba288001a7bb03fdb Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 5 Jun 2013 10:04:49 +0800 Subject: [PATCH 33/51] serial: omap: fix potential NULL pointer dereference in serial_omap_runtime_suspend() The dereference to 'up' should be moved below the NULL test. Introduced by commit ddd85e225c8885b5e4419b0499ab27100e7c366a (serial: omap: prevent runtime PM for "no_console_suspend") Signed-off-by: Wei Yongjun Acked-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/omap-serial.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 1aaeca8727d4..156b5aaed95b 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1613,6 +1613,9 @@ 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; + /* * When using 'no_console_suspend', the console UART must not be * suspended. Since driver suspend is managed by runtime suspend, @@ -1623,9 +1626,6 @@ static int serial_omap_runtime_suspend(struct device *dev) uart_console(&up->port)) return -EBUSY; - if (!up) - return -EINVAL; - if (!pdata) return 0; From 9f6d20ff848c23506c2ec55431479cb350179886 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Thu, 6 Jun 2013 01:28:12 +0400 Subject: [PATCH 34/51] tty/serial/sirf: fix error propagation in sirfsoc_uart_probe() If pinctrl_get_select_default() fails, sirfsoc_uart_probe() returns IS_ERR(result) instead of PTR_ERR(result). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 03465b673945..1fd564b8194b 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); From e8bfa7607342a21af06a0d365bf2f1d3062643bd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 5 Jun 2013 00:58:46 -0300 Subject: [PATCH 35/51] serial: imx: Fix warning when !CONFIG_SERIAL_IMX_CONSOLE When CONFIG_SERIAL_IMX_CONSOLE is not selected the following build warnings appear: drivers/tty/serial/imx.c:274:13: warning: 'imx_port_ucrs_save' defined but not used [-Wunused-function] drivers/tty/serial/imx.c:283:13: warning: 'imx_port_ucrs_restore' defined but not used [-Wunused-function] imx_port_ucrs_save() and imx_port_ucrs_restore() are only used when CONFIG_CONSOLE_POLL or CONFIG_SERIAL_IMX_CONSOLE are selected, so protect these functions declaration with a proper ifdef. Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 831324a9745b..0478083069c0 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -272,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) { @@ -289,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. From 37fb5b5604c5852daaa10110a29db1bb21d4f769 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 5 Jun 2013 00:58:47 -0300 Subject: [PATCH 36/51] serial: imx: Allow module build There is no need to only allow the serial driver to be built-in. Allow the imx serial driver to be built as a module. Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 64250b19cd8d..28ebf9097951 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,7 +561,7 @@ 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 From f0f6b80368133411414762d1ce42d983ff618c95 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 5 Jun 2013 00:58:48 -0300 Subject: [PATCH 37/51] serial: imx: Improve Kconfig text Some improvements in the Kconfig text: - Vendor is Freescale now - Provide a better example for the console parameter, which is something like console=ttymxc0 - Do not pass the names of ancient bootloaders Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 28ebf9097951..95e66acca165 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -564,15 +564,14 @@ config SERIAL_IMX_CONSOLE 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" From c9e2e946fb0ba5d2398feb89558f98c5c28e23e3 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Fri, 7 Jun 2013 09:20:40 +0800 Subject: [PATCH 38/51] tty: serial: add Freescale lpuart driver support Add Freescale lpuart driver support. The lpuart device can be found on Vybrid VF610 and Layerscape LS-1 SoCs. Signed-off-by: Jingchang Lu Signed-off-by: Greg Kroah-Hartman --- .../bindings/tty/serial/fsl-lpuart.txt | 14 + drivers/tty/serial/Kconfig | 14 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/fsl_lpuart.c | 874 ++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 5 files changed, 906 insertions(+) create mode 100644 Documentation/devicetree/bindings/tty/serial/fsl-lpuart.txt create mode 100644 drivers/tty/serial/fsl_lpuart.c diff --git a/Documentation/devicetree/bindings/tty/serial/fsl-lpuart.txt b/Documentation/devicetree/bindings/tty/serial/fsl-lpuart.txt new file mode 100644 index 000000000000..6fd1dd1638dd --- /dev/null +++ b/Documentation/devicetree/bindings/tty/serial/fsl-lpuart.txt @@ -0,0 +1,14 @@ +* Freescale low power universal asynchronous receiver/transmitter (lpuart) + +Required properties: +- compatible : Should be "fsl,-lpuart" +- reg : Address and length of the register set for the device +- interrupts : Should contain uart interrupt + +Example: + +uart0: serial@40027000 { + compatible = "fsl,vf610-lpuart"; + reg = <0x40027000 0x1000>; + interrupts = <0 61 0x00>; + }; diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 95e66acca165..46dd1c72feda 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1483,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 eedfec40e3dd..cf650f0cd6e4 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/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c new file mode 100644 index 000000000000..263cfaabe9e2 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 74c2bf7211f8..c8eaeb5465ef 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -226,4 +226,7 @@ /* Rocketport EXPRESS/INFINITY */ #define PORT_RP2 102 +/* Freescale lpuart */ +#define PORT_LPUART 103 + #endif /* _UAPILINUX_SERIAL_CORE_H */ From 0c375501be6e6dc23c11ebfa394434517444e62d Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Sun, 9 Jun 2013 10:01:19 +0800 Subject: [PATCH 39/51] serial: imx: enable the clocks for console The console's clocks are disabled after the uart driver is probed. It makes that we can see less log from the console now (though we still can get all the log by the `dmesg`). So enable the clocks for console, and we can see all the log again. This patch also disables the sport->clk_per when we fail to enable the sport->clk_ipg; Signed-off-by: Huang Shijie Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 7a761f7c9781..78f809759ed7 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -702,13 +702,16 @@ static int imx_startup(struct uart_port *port) int retval; unsigned long flags, temp; - retval = clk_prepare_enable(sport->clk_per); - if (retval) - goto error_out1; - - retval = clk_prepare_enable(sport->clk_ipg); - if (retval) - goto error_out1; + 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); @@ -1578,8 +1581,10 @@ static int serial_imx_probe(struct platform_device *pdev) goto deinit; platform_set_drvdata(pdev, sport); - clk_disable_unprepare(sport->clk_per); - clk_disable_unprepare(sport->clk_ipg); + if (!uart_console(&sport->port)) { + clk_disable_unprepare(sport->clk_per); + clk_disable_unprepare(sport->clk_ipg); + } return 0; deinit: From fcd2bb9b5d64d115b4433ddff9fb3289dd9597a2 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Fri, 14 Jun 2013 18:11:17 +0800 Subject: [PATCH 40/51] serial: mfd: Add sysrq support When using MFD HSU based console, sometime we need the sysrq function to help debugging kernel. The sysrq code is basically there, this patch just simply enable it. Signed-off-by: Feng Tang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mfd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index 5f4765a7a5c5..e266eca0ec76 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 #include #include From 8a29dfb8b67fbea03e084370d93fc99cbf375535 Mon Sep 17 00:00:00 2001 From: Matteo Facchinetti Date: Wed, 12 Jun 2013 09:21:42 +0200 Subject: [PATCH 41/51] serial/mpc52xx_uart: fix kernel panic when system reboot This bug appear when a second PSC based driver appends an interrupt routine to the FIFO controller shared interrupt (like spi-mpc512x-psc). When reboot, uart_shutdown() remove the serial console interrupt handler while spi-mpc512x-psc isr is still activate and cause the following kernel panic: The system is going down for reboot NOW!rpc (ttyPSC0) (Mon Jun 10 12:26:07 20 INIT: Sending processirq 40: nobody cared (try booting with the "irqpoll" option) CPU: 0 PID: 0 Comm: swapper Not tainted 3.10.0-rc4-next-20130607-00001-ga0bceb3-dirty #5 Call Trace: [cfff9f00] [c0007910] show_stack+0x48/0x150 (unreliable) [cfff9f40] [c005ae60] __report_bad_irq.isra.6+0x34/0xe0 [cfff9f60] [c005b194] note_interrupt+0x214/0x26c [cfff9f90] [c00590fc] handle_irq_event_percpu+0xd0/0x1bc [cfff9fd0] [c005921c] handle_irq_event+0x34/0x54 [cfff9fe0] [c005b8f4] handle_level_irq+0x90/0xf4 [cfff9ff0] [c000cb98] call_handle_irq+0x18/0x28 [c050dd60] [c000575c] do_IRQ+0xcc/0x124 [c050dd90] [c000eb04] ret_from_except+0x0/0x14 Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mpc52xx_uart.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 9ba194590a80..e1280a20b7a2 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -1058,6 +1058,9 @@ mpc52xx_uart_shutdown(struct uart_port *port) if (psc_ops->clock) psc_ops->clock(port, 0); + /* Disable interrupt */ + psc_ops->cw_disable_ints(port); + /* Release interrupt */ free_irq(port->irq, port); } From 80c48497f279a7ca25bfc6d2f9cae335a018ae17 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sun, 9 Jun 2013 15:17:18 -0300 Subject: [PATCH 42/51] serial: imx: Fix serial clock unbalance Since commit 0c375501 (serial: imx: enable the clocks for console), the imx_startup() function calls clk_prepare_enable conditionally, so we need to call clk_disable_unprepare inside imx_shutdown() under the same condition to avoid unbalanced clock calls. This avoids the following warning: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 70 at drivers/clk/clk.c:780 __clk_disable+0x68/0x84() Modules linked in: CPU: 0 PID: 70 Comm: mountall Not tainted 3.10.0-rc4-next-20130607+ #435 Backtrace: [<800116a4>] (dump_backtrace+0x0/0x10c) from [<80011844>] (show_stack+0x18/0x1c) r6:8069f4e8 r5:0000030c r4:00000000 r3:00000000 [<8001182c>] (show_stack+0x0/0x1c) from [<8053bce0>] (dump_stack+0x78/0x94) [<8053bc68>] (dump_stack+0x0/0x94) from [<80023df8>] (warn_slowpath_common+0x6c/0x8c) r4:00000000 r3:00000000 [<80023d8c>] (warn_slowpath_common+0x0/0x8c) from [<80023e3c>] (warn_slowpath_null+0x24/0x2c) r8:bf2ed008 r7:bfaa9810 r6:000f0013 r5:bf824b80 r4:bf824b80 [<80023e18>] (warn_slowpath_null+0x0/0x2c) from [<8041af84>] (__clk_disable+0x68/0x84) [<8041af1c>] (__clk_disable+0x0/0x84) from [<8041b098>] (clk_disable+0x20/0x2c) r4:600f0013 r3:00000001 [<8041b078>] (clk_disable+0x0/0x2c) from [<802c93e8>] (imx_shutdown+0xbc/0xec) r5:bf824b80 r4:bfaa9810 [<802c932c>] (imx_shutdown+0x0/0xec) from [<802c63a0>] (uart_port_shutdown+0x34/0x40) r5:bf86f860 r4:bfaa9810 [<802c636c>] (uart_port_shutdown+0x0/0x40) from [<802c68c0>] (uart_shutdown+0x98/0xc4) r4:bf86f800 r3:00000000 [<802c6828>] (uart_shutdown+0x0/0xc4) from [<802c7514>] (uart_close+0x5c/0x198) r7:bfaa9810 r6:bf274400 r5:bf86f86c r4:bf86f800 [<802c74b8>] (uart_close+0x0/0x198) from [<802ac648>] (tty_release+0xf8/0x500) [<802ac550>] (tty_release+0x0/0x500) from [<800c5a30>] (__fput+0x9c/0x208) [<800c5994>] (__fput+0x0/0x208) from [<800c5bac>] (____fput+0x10/0x14) [<800c5b9c>] (____fput+0x0/0x14) from [<80040234>] (task_work_run+0xb4/0xec) [<80040180>] (task_work_run+0x0/0xec) from [<80029238>] (do_exit+0x2b0/0x920) r8:8000e144 r7:000000f8 r6:bf306300 r5:00000000 r4:bfac1180 [<80028f88>] (do_exit+0x0/0x920) from [<80029a4c>] (do_group_exit+0x50/0xd4) r7:000000f8 [<800299fc>] (do_group_exit+0x0/0xd4) from [<80029ae8>] (__wake_up_parent+0x0/0x28) r7:000000f8 r6:00000001 r5:0006f7ae r4:0006f79a [<80029ad0>] (SyS_exit_group+0x0/0x18) from [<8000dfc0>] (ret_fast_syscall+0x0/0x30) ---[ end trace 16d080eb7efea4e9 ]--- Reported-by: Shawn Guo Signed-off-by: Fabio Estevam Tested-by: Shawn Guo Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 78f809759ed7..415cec62073f 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -901,8 +901,10 @@ static void imx_shutdown(struct uart_port *port) writel(temp, sport->port.membase + UCR1); spin_unlock_irqrestore(&sport->port.lock, flags); - clk_disable_unprepare(sport->clk_per); - clk_disable_unprepare(sport->clk_ipg); + if (!uart_console(&sport->port)) { + clk_disable_unprepare(sport->clk_per); + clk_disable_unprepare(sport->clk_ipg); + } } static void From a630fbfbb1beeffc5bbe542a7986bf2068874633 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 10 Jun 2013 07:39:09 -0700 Subject: [PATCH 43/51] serial: omap: Fix device tree based PM runtime In the runtime_suspend function pdata is not being used, and also blocks the function in device tree based booting. Fix it by removing the unused pdata from the runtime_suspend function. Further, context loss count is not being passed in pdata, so let's just reinitialize the port every time for those case. This can be further optimized later on for the device tree case by adding detection for the hardware state and possibly by adding a driver specific autosuspend timeout. And doing this, we can then make the related dev_err into a dev_dbg message instead of an error. In order for the wake-up events to work, we also need to set autosuspend_timeout to -1 if 0, and also device_init_wakeup() as that's not being done by the platform init code for the device tree case. Note that this does not affect legacy booting, and in fact might make it work for the cases where the context loss info is not being passed in pdata. Thanks to Kevin Hilman for debugging and suggesting fixes for the autosuspend_timeout and device_init_wakeup() related initializiation. Reviewed-by: Kevin Hilman Tested-by: Kevin Hilman Signed-off-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/omap-serial.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 156b5aaed95b..b6d172873076 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -198,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); } @@ -1502,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); @@ -1611,7 +1614,6 @@ 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; @@ -1626,9 +1628,6 @@ static int serial_omap_runtime_suspend(struct device *dev) uart_console(&up->port)) return -EBUSY; - if (!pdata) - return 0; - up->context_loss_cnt = serial_omap_get_context_loss_count(up); if (device_may_wakeup(dev)) { @@ -1656,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) { From f6c8dbe6e50c6e5121d7b6644718207daa008221 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 07:28:28 -0400 Subject: [PATCH 44/51] n_tty: Encapsulate minimum_to_wake within N_TTY minimum_to_wake is unique to N_TTY processing, and belongs in per-ldisc data. Add the ldisc method, ldisc_ops::fasync(), to notify line disciplines when signal-driven I/O is enabled or disabled. When enabled for N_TTY (by fcntl(F_SETFL, O_ASYNC)), blocking reader/polls will be woken for any readable input. When disabled, blocking reader/polls are not woken until the read buffer is full. Canonical mode (L_ICANON(tty), n_tty_data::icanon) is not affected by the minimum_to_wake setting. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 39 +++++++++++++++++++++++++++------------ drivers/tty/tty_io.c | 17 +++++++++-------- include/linux/tty.h | 1 - include/linux/tty_ldisc.h | 6 ++++++ 4 files changed, 42 insertions(+), 21 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index cdcdb0ea061a..f1806de69b18 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; @@ -1455,7 +1456,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, n_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)) @@ -1636,7 +1637,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); @@ -1804,17 +1805,17 @@ do_it_again: minimum = MIN_CHAR(tty); if (minimum) { 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; + ldata->minimum_to_wake = minimum = 1; } } @@ -1854,9 +1855,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)) { @@ -1973,7 +1974,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; @@ -2105,6 +2106,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); @@ -2119,9 +2121,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 && @@ -2169,6 +2171,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", @@ -2182,7 +2196,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/tty_io.c b/drivers/tty/tty_io.c index 6464029e4860..bd88007fa6ea 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2138,6 +2138,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 +2149,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 +2172,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/include/linux/tty.h b/include/linux/tty.h index 82ab69bc9b79..01ac30efd6a6 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -272,7 +272,6 @@ struct tty_struct { #define N_TTY_BUF_SIZE 4096 unsigned char closing:1; - unsigned short minimum_to_wake; unsigned char *write_buf; int write_cnt; /* If the tty has a pending do_SAK, queue it here - akpm */ diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 7b24bbd85ea8..a1b048999821 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -100,6 +100,11 @@ * seek to perform this action quickly but should wait until * any pending driver I/O is completed. * + * void (*fasync)(struct tty_struct *, int on) + * + * Notify line discipline when signal-driven I/O is enabled or + * disabled. + * * void (*dcd_change)(struct tty_struct *tty, unsigned int status) * * Tells the discipline that the DCD pin has changed its status. @@ -189,6 +194,7 @@ struct tty_ldisc_ops { char *fp, int count); void (*write_wakeup)(struct tty_struct *); void (*dcd_change)(struct tty_struct *, unsigned int); + void (*fasync)(struct tty_struct *tty, int on); struct module *owner; From a6e54319a7499bf754efb3a2cb2f5d4901ccbcff Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 07:28:29 -0400 Subject: [PATCH 45/51] n_tty: Untangle read completion variables Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index f1806de69b18..fa5cb4654c4f 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1801,20 +1801,16 @@ 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) ldata->minimum_to_wake = 1; else if (!waitqueue_active(&tty->read_wait) || (ldata->minimum_to_wake > minimum)) ldata->minimum_to_wake = minimum; } else { - timeout = 0; - if (time) { - timeout = time; - time = 0; - } + timeout = (HZ / 10) * TIME_CHAR(tty); ldata->minimum_to_wake = minimum = 1; } } From b84830527645dfe7b7a5cc03518e3c791b4ee9e0 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 07:28:30 -0400 Subject: [PATCH 46/51] n_tty: Fix unsafe update of available buffer space receive_room is used to control the amount of data the flip buffer work can push to the read buffer. This update is unsafe: CPU 0 | CPU 1 | | n_tty_read() | n_tty_set_room() | left = n_tty_receive_buf() | | n_tty_set_room() | left = | tty->receive_room = left | | tty->receive_room = left receive_room is now updated with a stale calculation of the available buffer space, and the subsequent work loop will likely overwrite unread data in the input buffer. Update receive_room atomically with the calculation of the available buffer space. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index fa5cb4654c4f..064616542cb5 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -115,13 +115,14 @@ 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". + * Sets 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) @@ -129,8 +130,10 @@ static void n_tty_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 @@ -150,6 +153,8 @@ 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); + /* Did this open up the receive buffer? We may need to flip */ if (left && !old_left) { WARN_RATELIMIT(tty->port->itty == NULL, @@ -1872,7 +1877,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; From 7879a9f9fd4ff4a88b3c51f4ab6718dc7f86ed86 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 07:28:31 -0400 Subject: [PATCH 47/51] n_tty: Buffer work should not reschedule itself Although the driver-side input path must update the available buffer space, it should not reschedule itself. If space is still available and the flip buffers are not empty, flush_to_ldisc() will loop again. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 064616542cb5..4bf0fc0843d7 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -118,14 +118,14 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, * n_tty_set_room - receive space * @tty: terminal * - * Sets tty->receive_room to reflect the currently available space + * 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; @@ -155,8 +155,13 @@ static void n_tty_set_room(struct tty_struct *tty) 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 @@ -1459,7 +1464,7 @@ 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 >= ldata->minimum_to_wake)) || L_EXTPROC(tty)) { From 64e377dcd7d75c241d614458e9619d3445de44ef Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:01:00 -0400 Subject: [PATCH 48/51] tty: Reset itty for other pty Commit 19ffd68f816878aed456d5e87697f43bd9e3bd2b ('pty: Remove redundant itty reset') introduced a regression whereby the other pty's linkage is not cleared on teardown. This triggers a false positive diagnostic in testing. Properly reset the itty linkage. Signed-off-by: Peter Hurley Cc: stable # 3.10 Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index bd88007fa6ea..366af832794b 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) From 91f8c2d805f5c5fd82bf8655065148b2e16967ff Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 19 Jun 2013 13:17:30 +0200 Subject: [PATCH 49/51] tty: atmel_serial: prepare clk before calling enable Replace clk_enable/disable with clk_prepare_enable/disable_unprepare to avoid common clk framework warnings. Signed-off-by: Boris BREZILLON Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 41 +++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 3467462869ce..691265faebbe 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 From 29692d05647cb7ecea56242241f77291d5624b95 Mon Sep 17 00:00:00 2001 From: Darren Hart Date: Tue, 25 Jun 2013 18:53:22 -0700 Subject: [PATCH 50/51] pch_uart: Add uart_clk selection for the MinnowBoard Use DMI_BOARD_NAME to determine if we are running on a MinnowBoard and set the uart clock to 50MHz if so. This removes the need to pass the user_uartclk to the kernel at boot time. Signed-off-by: Darren Hart Cc: Jiri Slaby Cc: "H. Peter Anvin" Cc: Peter Waskiewicz Cc: Andy Shevchenko Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pch_uart.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 21a7e179edf3..572d48189de9 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; } From 828c6a102b1f2b8583fadc0e779c46b31d448f0b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 30 Jun 2013 09:03:06 -0700 Subject: [PATCH 51/51] Revert "serial: 8250_pci: add support for another kind of NetMos Technology PCI 9835 Multi-I/O Controller" This reverts commit 8d2f8cd424ca0b99001f3ff4f5db87c4e525f366. As reported by Stefan, this device already works with the parport_serial driver, so the 8250_pci driver should not also try to grab it as well. Reported-by: Stefan Seyfried Cc: Wang YanQing Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 26e3a97ab157..c52948b368d8 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 },