diff options
Diffstat (limited to 'drivers/tty')
67 files changed, 2408 insertions, 1272 deletions
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 5817e2397463..b95bed92da9f 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_R3964) += n_r3964.o obj-y += vt/ obj-$(CONFIG_HVC_DRIVER) += hvc/ obj-y += serial/ +obj-$(CONFIG_SERIAL_DEV_BUS) += serdev/ # tty drivers obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index 3fc912373adf..996bd473dd03 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -300,7 +300,7 @@ static int goldfish_tty_probe(struct platform_device *pdev) return 0; err_tty_register_device_failed: - free_irq(irq, pdev); + free_irq(irq, qtty); err_request_irq_failed: goldfish_tty_current_line_count--; if (goldfish_tty_current_line_count == 0) diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index 9b5c0fb216b5..b19ae36a05ec 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -29,7 +29,6 @@ #include <linux/kernel.h> #include <linux/kthread.h> #include <linux/list.h> -#include <linux/init.h> #include <linux/major.h> #include <linux/atomic.h> #include <linux/sysrq.h> diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig new file mode 100644 index 000000000000..cdc6b820cf93 --- /dev/null +++ b/drivers/tty/serdev/Kconfig @@ -0,0 +1,16 @@ +# +# Serial bus device driver configuration +# +menuconfig SERIAL_DEV_BUS + tristate "Serial device bus" + help + Core support for devices connected via a serial port. + +if SERIAL_DEV_BUS + +config SERIAL_DEV_CTRL_TTYPORT + bool "Serial device TTY port controller" + depends on TTY + depends on SERIAL_DEV_BUS != m + +endif diff --git a/drivers/tty/serdev/Makefile b/drivers/tty/serdev/Makefile new file mode 100644 index 000000000000..0cbdb9444d9d --- /dev/null +++ b/drivers/tty/serdev/Makefile @@ -0,0 +1,5 @@ +serdev-objs := core.o + +obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o + +obj-$(CONFIG_SERIAL_DEV_CTRL_TTYPORT) += serdev-ttyport.o diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c new file mode 100644 index 000000000000..f4c6c90add78 --- /dev/null +++ b/drivers/tty/serdev/core.c @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <robh@kernel.org> + * + * Based on drivers/spmi/spmi.c: + * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/errno.h> +#include <linux/idr.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/serdev.h> +#include <linux/slab.h> + +static bool is_registered; +static DEFINE_IDA(ctrl_ida); + +static void serdev_device_release(struct device *dev) +{ + struct serdev_device *serdev = to_serdev_device(dev); + kfree(serdev); +} + +static const struct device_type serdev_device_type = { + .release = serdev_device_release, +}; + +static void serdev_ctrl_release(struct device *dev) +{ + struct serdev_controller *ctrl = to_serdev_controller(dev); + ida_simple_remove(&ctrl_ida, ctrl->nr); + kfree(ctrl); +} + +static const struct device_type serdev_ctrl_type = { + .release = serdev_ctrl_release, +}; + +static int serdev_device_match(struct device *dev, struct device_driver *drv) +{ + /* TODO: ACPI and platform matching */ + return of_driver_match_device(dev, drv); +} + +static int serdev_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + /* TODO: ACPI and platform modalias */ + return of_device_uevent_modalias(dev, env); +} + +/** + * serdev_device_add() - add a device previously constructed via serdev_device_alloc() + * @serdev: serdev_device to be added + */ +int serdev_device_add(struct serdev_device *serdev) +{ + struct device *parent = serdev->dev.parent; + int err; + + dev_set_name(&serdev->dev, "%s-%d", dev_name(parent), serdev->nr); + + err = device_add(&serdev->dev); + if (err < 0) { + dev_err(&serdev->dev, "Can't add %s, status %d\n", + dev_name(&serdev->dev), err); + goto err_device_add; + } + + dev_dbg(&serdev->dev, "device %s registered\n", dev_name(&serdev->dev)); + +err_device_add: + return err; +} +EXPORT_SYMBOL_GPL(serdev_device_add); + +/** + * serdev_device_remove(): remove an serdev device + * @serdev: serdev_device to be removed + */ +void serdev_device_remove(struct serdev_device *serdev) +{ + device_unregister(&serdev->dev); +} +EXPORT_SYMBOL_GPL(serdev_device_remove); + +int serdev_device_open(struct serdev_device *serdev) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->open) + return -EINVAL; + + return ctrl->ops->open(ctrl); +} +EXPORT_SYMBOL_GPL(serdev_device_open); + +void serdev_device_close(struct serdev_device *serdev) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->close) + return; + + ctrl->ops->close(ctrl); +} +EXPORT_SYMBOL_GPL(serdev_device_close); + +int serdev_device_write_buf(struct serdev_device *serdev, + const unsigned char *buf, size_t count) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->write_buf) + return -EINVAL; + + return ctrl->ops->write_buf(ctrl, buf, count); +} +EXPORT_SYMBOL_GPL(serdev_device_write_buf); + +void serdev_device_write_flush(struct serdev_device *serdev) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->write_flush) + return; + + ctrl->ops->write_flush(ctrl); +} +EXPORT_SYMBOL_GPL(serdev_device_write_flush); + +int serdev_device_write_room(struct serdev_device *serdev) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->write_room) + return 0; + + return serdev->ctrl->ops->write_room(ctrl); +} +EXPORT_SYMBOL_GPL(serdev_device_write_room); + +unsigned int serdev_device_set_baudrate(struct serdev_device *serdev, unsigned int speed) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->set_baudrate) + return 0; + + return ctrl->ops->set_baudrate(ctrl, speed); + +} +EXPORT_SYMBOL_GPL(serdev_device_set_baudrate); + +void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->set_flow_control) + return; + + ctrl->ops->set_flow_control(ctrl, enable); +} +EXPORT_SYMBOL_GPL(serdev_device_set_flow_control); + +static int serdev_drv_probe(struct device *dev) +{ + const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver); + + return sdrv->probe(to_serdev_device(dev)); +} + +static int serdev_drv_remove(struct device *dev) +{ + const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver); + + sdrv->remove(to_serdev_device(dev)); + return 0; +} + +static ssize_t modalias_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = of_device_get_modalias(dev, buf, PAGE_SIZE - 2); + buf[len] = '\n'; + buf[len+1] = 0; + return len+1; +} + +static struct device_attribute serdev_device_attrs[] = { + __ATTR_RO(modalias), + __ATTR_NULL +}; + +static struct bus_type serdev_bus_type = { + .name = "serial", + .match = serdev_device_match, + .probe = serdev_drv_probe, + .remove = serdev_drv_remove, + .uevent = serdev_uevent, + .dev_attrs = serdev_device_attrs, +}; + +/** + * serdev_controller_alloc() - Allocate a new serdev device + * @ctrl: associated controller + * + * Caller is responsible for either calling serdev_device_add() to add the + * newly allocated controller, or calling serdev_device_put() to discard it. + */ +struct serdev_device *serdev_device_alloc(struct serdev_controller *ctrl) +{ + struct serdev_device *serdev; + + serdev = kzalloc(sizeof(*serdev), GFP_KERNEL); + if (!serdev) + return NULL; + + serdev->ctrl = ctrl; + ctrl->serdev = serdev; + device_initialize(&serdev->dev); + serdev->dev.parent = &ctrl->dev; + serdev->dev.bus = &serdev_bus_type; + serdev->dev.type = &serdev_device_type; + return serdev; +} +EXPORT_SYMBOL_GPL(serdev_device_alloc); + +/** + * serdev_controller_alloc() - Allocate a new serdev controller + * @parent: parent device + * @size: size of private data + * + * Caller is responsible for either calling serdev_controller_add() to add the + * newly allocated controller, or calling serdev_controller_put() to discard it. + * The allocated private data region may be accessed via + * serdev_controller_get_drvdata() + */ +struct serdev_controller *serdev_controller_alloc(struct device *parent, + size_t size) +{ + struct serdev_controller *ctrl; + int id; + + if (WARN_ON(!parent)) + return NULL; + + ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL); + if (!ctrl) + return NULL; + + device_initialize(&ctrl->dev); + ctrl->dev.type = &serdev_ctrl_type; + ctrl->dev.bus = &serdev_bus_type; + ctrl->dev.parent = parent; + ctrl->dev.of_node = parent->of_node; + serdev_controller_set_drvdata(ctrl, &ctrl[1]); + + id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + dev_err(parent, + "unable to allocate serdev controller identifier.\n"); + serdev_controller_put(ctrl); + return NULL; + } + + ctrl->nr = id; + dev_set_name(&ctrl->dev, "serial%d", id); + + dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id); + return ctrl; +} +EXPORT_SYMBOL_GPL(serdev_controller_alloc); + +static int of_serdev_register_devices(struct serdev_controller *ctrl) +{ + struct device_node *node; + struct serdev_device *serdev = NULL; + int err; + bool found = false; + + for_each_available_child_of_node(ctrl->dev.of_node, node) { + if (!of_get_property(node, "compatible", NULL)) + continue; + + dev_dbg(&ctrl->dev, "adding child %s\n", node->full_name); + + serdev = serdev_device_alloc(ctrl); + if (!serdev) + continue; + + serdev->dev.of_node = node; + + err = serdev_device_add(serdev); + if (err) { + dev_err(&serdev->dev, + "failure adding device. status %d\n", err); + serdev_device_put(serdev); + } else + found = true; + } + if (!found) + return -ENODEV; + + return 0; +} + +/** + * serdev_controller_add() - Add an serdev controller + * @ctrl: controller to be registered. + * + * Register a controller previously allocated via serdev_controller_alloc() with + * the serdev core. + */ +int serdev_controller_add(struct serdev_controller *ctrl) +{ + int ret; + + /* Can't register until after driver model init */ + if (WARN_ON(!is_registered)) + return -EAGAIN; + + ret = device_add(&ctrl->dev); + if (ret) + return ret; + + ret = of_serdev_register_devices(ctrl); + if (ret) + goto out_dev_del; + + dev_dbg(&ctrl->dev, "serdev%d registered: dev:%p\n", + ctrl->nr, &ctrl->dev); + return 0; + +out_dev_del: + device_del(&ctrl->dev); + return ret; +}; +EXPORT_SYMBOL_GPL(serdev_controller_add); + +/* Remove a device associated with a controller */ +static int serdev_remove_device(struct device *dev, void *data) +{ + struct serdev_device *serdev = to_serdev_device(dev); + if (dev->type == &serdev_device_type) + serdev_device_remove(serdev); + return 0; +} + +/** + * serdev_controller_remove(): remove an serdev controller + * @ctrl: controller to remove + * + * Remove a serdev controller. Caller is responsible for calling + * serdev_controller_put() to discard the allocated controller. + */ +void serdev_controller_remove(struct serdev_controller *ctrl) +{ + int dummy; + + if (!ctrl) + return; + + dummy = device_for_each_child(&ctrl->dev, NULL, + serdev_remove_device); + device_del(&ctrl->dev); +} +EXPORT_SYMBOL_GPL(serdev_controller_remove); + +/** + * serdev_driver_register() - Register client driver with serdev core + * @sdrv: client driver to be associated with client-device. + * + * This API will register the client driver with the serdev framework. + * It is typically called from the driver's module-init function. + */ +int __serdev_device_driver_register(struct serdev_device_driver *sdrv, struct module *owner) +{ + sdrv->driver.bus = &serdev_bus_type; + sdrv->driver.owner = owner; + + /* force drivers to async probe so I/O is possible in probe */ + sdrv->driver.probe_type = PROBE_PREFER_ASYNCHRONOUS; + + return driver_register(&sdrv->driver); +} +EXPORT_SYMBOL_GPL(__serdev_device_driver_register); + +static void __exit serdev_exit(void) +{ + bus_unregister(&serdev_bus_type); +} +module_exit(serdev_exit); + +static int __init serdev_init(void) +{ + int ret; + + ret = bus_register(&serdev_bus_type); + if (ret) + return ret; + + is_registered = true; + return 0; +} +/* Must be before serial drivers register */ +postcore_initcall(serdev_init); + +MODULE_AUTHOR("Rob Herring <robh@kernel.org>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Serial attached device bus"); diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c new file mode 100644 index 000000000000..d05393594f15 --- /dev/null +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <robh@kernel.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/kernel.h> +#include <linux/serdev.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> + +#define SERPORT_ACTIVE 1 + +struct serport { + struct tty_port *port; + struct tty_struct *tty; + struct tty_driver *tty_drv; + int tty_idx; + unsigned long flags; +}; + +/* + * Callback functions from the tty port. + */ + +static int ttyport_receive_buf(struct tty_port *port, const unsigned char *cp, + const unsigned char *fp, size_t count) +{ + struct serdev_controller *ctrl = port->client_data; + struct serport *serport = serdev_controller_get_drvdata(ctrl); + + if (!test_bit(SERPORT_ACTIVE, &serport->flags)) + return 0; + + return serdev_controller_receive_buf(ctrl, cp, count); +} + +static void ttyport_write_wakeup(struct tty_port *port) +{ + struct serdev_controller *ctrl = port->client_data; + struct serport *serport = serdev_controller_get_drvdata(ctrl); + + if (!test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags)) + return; + + if (test_bit(SERPORT_ACTIVE, &serport->flags)) + serdev_controller_write_wakeup(ctrl); +} + +static const struct tty_port_client_operations client_ops = { + .receive_buf = ttyport_receive_buf, + .write_wakeup = ttyport_write_wakeup, +}; + +/* + * Callback functions from the serdev core. + */ + +static int ttyport_write_buf(struct serdev_controller *ctrl, const unsigned char *data, size_t len) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + + if (!test_bit(SERPORT_ACTIVE, &serport->flags)) + return 0; + + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + return tty->ops->write(serport->tty, data, len); +} + +static void ttyport_write_flush(struct serdev_controller *ctrl) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + + tty_driver_flush_buffer(tty); +} + +static int ttyport_write_room(struct serdev_controller *ctrl) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + + return tty_write_room(tty); +} + +static int ttyport_open(struct serdev_controller *ctrl) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty; + struct ktermios ktermios; + + tty = tty_init_dev(serport->tty_drv, serport->tty_idx); + if (IS_ERR(tty)) + return PTR_ERR(tty); + serport->tty = tty; + + serport->port->client_ops = &client_ops; + serport->port->client_data = ctrl; + + if (tty->ops->open) + tty->ops->open(serport->tty, NULL); + else + tty_port_open(serport->port, tty, NULL); + + /* Bring the UART into a known 8 bits no parity hw fc state */ + ktermios = tty->termios; + ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | + INLCR | IGNCR | ICRNL | IXON); + ktermios.c_oflag &= ~OPOST; + ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + ktermios.c_cflag &= ~(CSIZE | PARENB); + ktermios.c_cflag |= CS8; + ktermios.c_cflag |= CRTSCTS; + tty_set_termios(tty, &ktermios); + + set_bit(SERPORT_ACTIVE, &serport->flags); + + tty_unlock(serport->tty); + return 0; +} + +static void ttyport_close(struct serdev_controller *ctrl) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + + clear_bit(SERPORT_ACTIVE, &serport->flags); + + if (tty->ops->close) + tty->ops->close(tty, NULL); + + tty_release_struct(tty, serport->tty_idx); +} + +static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigned int speed) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + struct ktermios ktermios = tty->termios; + + ktermios.c_cflag &= ~CBAUD; + tty_termios_encode_baud_rate(&ktermios, speed, speed); + + /* tty_set_termios() return not checked as it is always 0 */ + tty_set_termios(tty, &ktermios); + return speed; +} + +static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + struct ktermios ktermios = tty->termios; + + if (enable) + ktermios.c_cflag |= CRTSCTS; + else + ktermios.c_cflag &= ~CRTSCTS; + + tty_set_termios(tty, &ktermios); +} + +static const struct serdev_controller_ops ctrl_ops = { + .write_buf = ttyport_write_buf, + .write_flush = ttyport_write_flush, + .write_room = ttyport_write_room, + .open = ttyport_open, + .close = ttyport_close, + .set_flow_control = ttyport_set_flow_control, + .set_baudrate = ttyport_set_baudrate, +}; + +struct device *serdev_tty_port_register(struct tty_port *port, + struct device *parent, + struct tty_driver *drv, int idx) +{ + struct serdev_controller *ctrl; + struct serport *serport; + int ret; + + if (!port || !drv || !parent) + return ERR_PTR(-ENODEV); + + ctrl = serdev_controller_alloc(parent, sizeof(struct serport)); + if (!ctrl) + return ERR_PTR(-ENOMEM); + serport = serdev_controller_get_drvdata(ctrl); + + serport->port = port; + serport->tty_idx = idx; + serport->tty_drv = drv; + + ctrl->ops = &ctrl_ops; + + ret = serdev_controller_add(ctrl); + if (ret) + goto err_controller_put; + + dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx); + return &ctrl->dev; + +err_controller_put: + serdev_controller_put(ctrl); + return ERR_PTR(ret); +} + +void serdev_tty_port_unregister(struct tty_port *port) +{ + struct serdev_controller *ctrl = port->client_data; + struct serport *serport = serdev_controller_get_drvdata(ctrl); + + if (!serport) + return; + + serdev_controller_remove(ctrl); + port->client_ops = NULL; + port->client_data = NULL; + serdev_controller_put(ctrl); +} diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index c89fafc972b6..6ee55a2d47bb 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -201,8 +201,31 @@ static unsigned int dw8250_serial_in32be(struct uart_port *p, int offset) static int dw8250_handle_irq(struct uart_port *p) { + struct uart_8250_port *up = up_to_u8250p(p); struct dw8250_data *d = p->private_data; unsigned int iir = p->serial_in(p, UART_IIR); + unsigned int status; + unsigned long flags; + + /* + * There are ways to get Designware-based UARTs into a state where + * they are asserting UART_IIR_RX_TIMEOUT but there is no actual + * data available. If we see such a case then we'll do a bogus + * read. If we don't do this then the "RX TIMEOUT" interrupt will + * fire forever. + * + * This problem has only been observed so far when not in DMA mode + * so we limit the workaround only to non-DMA mode. + */ + if (!up->dma && ((iir & 0x3f) == UART_IIR_RX_TIMEOUT)) { + spin_lock_irqsave(&p->lock, flags); + status = p->serial_in(p, UART_LSR); + + if (!(status & (UART_LSR_DR | UART_LSR_BI))) + (void) p->serial_in(p, UART_RX); + + spin_unlock_irqrestore(&p->lock, flags); + } if (serial8250_handle_irq(p, iir)) return 1; @@ -248,11 +271,11 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, if (!ret) p->uartclk = rate; +out: p->status &= ~UPSTAT_AUTOCTS; if (termios->c_cflag & CRTSCTS) p->status |= UPSTAT_AUTOCTS; -out: serial8250_do_set_termios(p, termios, old); } @@ -326,13 +349,11 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) p->serial_in = dw8250_serial_in32; data->uart_16550_compatible = true; } - p->set_termios = dw8250_set_termios; } /* Platforms with iDMA */ if (platform_get_resource_byname(to_platform_device(p->dev), IORESOURCE_MEM, "lpss_priv")) { - p->set_termios = dw8250_set_termios; data->dma.rx_param = p->dev->parent; data->dma.tx_param = p->dev->parent; data->dma.fn = dw8250_idma_filter; @@ -414,6 +435,7 @@ static int dw8250_probe(struct platform_device *pdev) p->serial_in = dw8250_serial_in; p->serial_out = dw8250_serial_out; p->set_ldisc = dw8250_set_ldisc; + p->set_termios = dw8250_set_termios; p->membase = devm_ioremap(dev, regs->start, resource_size(regs)); if (!p->membase) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c new file mode 100644 index 000000000000..b89c4ffc0486 --- /dev/null +++ b/drivers/tty/serial/8250/8250_exar.c @@ -0,0 +1,487 @@ +/* + * Probe module for 8250/16550-type Exar chips PCI serial ports. + * + * Based on drivers/tty/serial/8250/8250_pci.c, + * + * Copyright (C) 2017 Sudip Mukherjee, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/tty.h> +#include <linux/8250_pci.h> + +#include <asm/byteorder.h> + +#include "8250.h" + +#define PCI_DEVICE_ID_COMMTECH_4224PCI335 0x0002 +#define PCI_DEVICE_ID_COMMTECH_4222PCI335 0x0004 +#define PCI_DEVICE_ID_COMMTECH_2324PCI335 0x000a +#define PCI_DEVICE_ID_COMMTECH_2328PCI335 0x000b +#define PCI_DEVICE_ID_COMMTECH_4224PCIE 0x0020 +#define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 +#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 +#define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358 +#define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358 + +#define UART_EXAR_8XMODE 0x88 /* 8X sampling rate select */ + +#define UART_EXAR_FCTR 0x08 /* Feature Control Register */ +#define UART_FCTR_EXAR_IRDA 0x10 /* IrDa data encode select */ +#define UART_FCTR_EXAR_485 0x20 /* Auto 485 half duplex dir ctl */ +#define UART_FCTR_EXAR_TRGA 0x00 /* FIFO trigger table A */ +#define UART_FCTR_EXAR_TRGB 0x60 /* FIFO trigger table B */ +#define UART_FCTR_EXAR_TRGC 0x80 /* FIFO trigger table C */ +#define UART_FCTR_EXAR_TRGD 0xc0 /* FIFO trigger table D programmable */ + +#define UART_EXAR_TXTRG 0x0a /* Tx FIFO trigger level write-only */ +#define UART_EXAR_RXTRG 0x0b /* Rx FIFO trigger level write-only */ + +#define UART_EXAR_MPIOINT_7_0 0x8f /* MPIOINT[7:0] */ +#define UART_EXAR_MPIOLVL_7_0 0x90 /* MPIOLVL[7:0] */ +#define UART_EXAR_MPIO3T_7_0 0x91 /* MPIO3T[7:0] */ +#define UART_EXAR_MPIOINV_7_0 0x92 /* MPIOINV[7:0] */ +#define UART_EXAR_MPIOSEL_7_0 0x93 /* MPIOSEL[7:0] */ +#define UART_EXAR_MPIOOD_7_0 0x94 /* MPIOOD[7:0] */ +#define UART_EXAR_MPIOINT_15_8 0x95 /* MPIOINT[15:8] */ +#define UART_EXAR_MPIOLVL_15_8 0x96 /* MPIOLVL[15:8] */ +#define UART_EXAR_MPIO3T_15_8 0x97 /* MPIO3T[15:8] */ +#define UART_EXAR_MPIOINV_15_8 0x98 /* MPIOINV[15:8] */ +#define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[15:8] */ +#define UART_EXAR_MPIOOD_15_8 0x9a /* MPIOOD[15:8] */ + +struct exar8250; + +/** + * struct exar8250_board - board information + * @num_ports: number of serial ports + * @reg_shift: describes UART register mapping in PCI memory + */ +struct exar8250_board { + unsigned int num_ports; + unsigned int reg_shift; + bool has_slave; + int (*setup)(struct exar8250 *, struct pci_dev *, + struct uart_8250_port *, int); + void (*exit)(struct pci_dev *pcidev); +}; + +struct exar8250 { + unsigned int nr; + struct exar8250_board *board; + int line[0]; +}; + +static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev, + int idx, unsigned int offset, + struct uart_8250_port *port) +{ + const struct exar8250_board *board = priv->board; + unsigned int bar = 0; + + if (!pcim_iomap_table(pcidev)[bar] && !pcim_iomap(pcidev, bar, 0)) + return -ENOMEM; + + port->port.iotype = UPIO_MEM; + port->port.mapbase = pci_resource_start(pcidev, bar) + offset; + port->port.membase = pcim_iomap_table(pcidev)[bar] + offset; + port->port.regshift = board->reg_shift; + + return 0; +} + +static int +pci_fastcom335_setup(struct exar8250 *priv, struct pci_dev *pcidev, + struct uart_8250_port *port, int idx) +{ + unsigned int offset = idx * 0x200; + unsigned int baud = 1843200; + u8 __iomem *p; + int err; + + port->port.flags |= UPF_EXAR_EFR; + port->port.uartclk = baud * 16; + + err = default_setup(priv, pcidev, idx, offset, port); + if (err) + return err; + + p = port->port.membase; + + writeb(0x00, p + UART_EXAR_8XMODE); + writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); + writeb(32, p + UART_EXAR_TXTRG); + writeb(32, p + UART_EXAR_RXTRG); + + /* + * Setup Multipurpose Input/Output pins. + */ + if (idx == 0) { + switch (pcidev->device) { + case PCI_DEVICE_ID_COMMTECH_4222PCI335: + case PCI_DEVICE_ID_COMMTECH_4224PCI335: + writeb(0x78, p + UART_EXAR_MPIOLVL_7_0); + writeb(0x00, p + UART_EXAR_MPIOINV_7_0); + writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); + break; + case PCI_DEVICE_ID_COMMTECH_2324PCI335: + case PCI_DEVICE_ID_COMMTECH_2328PCI335: + writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); + writeb(0xc0, p + UART_EXAR_MPIOINV_7_0); + writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0); + break; + } + writeb(0x00, p + UART_EXAR_MPIOINT_7_0); + writeb(0x00, p + UART_EXAR_MPIO3T_7_0); + writeb(0x00, p + UART_EXAR_MPIOOD_7_0); + } + + return 0; +} + +static int +pci_connect_tech_setup(struct exar8250 *priv, struct pci_dev *pcidev, + struct uart_8250_port *port, int idx) +{ + unsigned int offset = idx * 0x200; + unsigned int baud = 1843200; + + port->port.uartclk = baud * 16; + return default_setup(priv, pcidev, idx, offset, port); +} + +static int +pci_xr17c154_setup(struct exar8250 *priv, struct pci_dev *pcidev, + struct uart_8250_port *port, int idx) +{ + unsigned int offset = idx * 0x200; + unsigned int baud = 921600; + + port->port.uartclk = baud * 16; + return default_setup(priv, pcidev, idx, offset, port); +} + +static void setup_gpio(u8 __iomem *p) +{ + writeb(0x00, p + UART_EXAR_MPIOINT_7_0); + writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); + writeb(0x00, p + UART_EXAR_MPIO3T_7_0); + writeb(0x00, p + UART_EXAR_MPIOINV_7_0); + writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); + writeb(0x00, p + UART_EXAR_MPIOOD_7_0); + writeb(0x00, p + UART_EXAR_MPIOINT_15_8); + writeb(0x00, p + UART_EXAR_MPIOLVL_15_8); + writeb(0x00, p + UART_EXAR_MPIO3T_15_8); + writeb(0x00, p + UART_EXAR_MPIOINV_15_8); + writeb(0x00, p + UART_EXAR_MPIOSEL_15_8); + writeb(0x00, p + UART_EXAR_MPIOOD_15_8); +} + +static void * +xr17v35x_register_gpio(struct pci_dev *pcidev) +{ + struct platform_device *pdev; + + pdev = platform_device_alloc("gpio_exar", PLATFORM_DEVID_AUTO); + if (!pdev) + return NULL; + + platform_set_drvdata(pdev, pcidev); + if (platform_device_add(pdev) < 0) { + platform_device_put(pdev); + return NULL; + } + + return pdev; +} + +static int +pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, + struct uart_8250_port *port, int idx) +{ + const struct exar8250_board *board = priv->board; + unsigned int offset = idx * 0x400; + unsigned int baud = 7812500; + u8 __iomem *p; + int ret; + + port->port.uartclk = baud * 16; + /* + * Setup the uart clock for the devices on expansion slot to + * half the clock speed of the main chip (which is 125MHz) + */ + if (board->has_slave && idx >= 8) + port->port.uartclk /= 2; + + ret = default_setup(priv, pcidev, idx, offset, port); + if (ret) + return ret; + + p = port->port.membase; + + writeb(0x00, p + UART_EXAR_8XMODE); + writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); + writeb(128, p + UART_EXAR_TXTRG); + writeb(128, p + UART_EXAR_RXTRG); + + if (idx == 0) { + /* Setup Multipurpose Input/Output pins. */ + setup_gpio(p); + + port->port.private_data = xr17v35x_register_gpio(pcidev); + } + + return 0; +} + +static void pci_xr17v35x_exit(struct pci_dev *pcidev) +{ + struct exar8250 *priv = pci_get_drvdata(pcidev); + struct uart_8250_port *port = serial8250_get_port(priv->line[0]); + struct platform_device *pdev = port->port.private_data; + + platform_device_unregister(pdev); + port->port.private_data = NULL; +} + +static int +exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) +{ + unsigned int nr_ports, i, bar = 0, maxnr; + struct exar8250_board *board; + struct uart_8250_port uart; + struct exar8250 *priv; + int rc; + + board = (struct exar8250_board *)ent->driver_data; + if (!board) + return -EINVAL; + + rc = pcim_enable_device(pcidev); + if (rc) + return rc; + + maxnr = pci_resource_len(pcidev, bar) >> (board->reg_shift + 3); + + nr_ports = board->num_ports ? board->num_ports : pcidev->device & 0x0f; + + priv = devm_kzalloc(&pcidev->dev, sizeof(*priv) + + sizeof(unsigned int) * nr_ports, + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->board = board; + + pci_set_master(pcidev); + + rc = pci_alloc_irq_vectors(pcidev, 1, 1, PCI_IRQ_ALL_TYPES); + if (rc < 0) + return rc; + + memset(&uart, 0, sizeof(uart)); + uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ + | UPF_EXAR_EFR; + uart.port.irq = pci_irq_vector(pcidev, 0); + uart.port.dev = &pcidev->dev; + + for (i = 0; i < nr_ports && i < maxnr; i++) { + rc = board->setup(priv, pcidev, &uart, i); + if (rc) { + dev_err(&pcidev->dev, "Failed to setup port %u\n", i); + break; + } + + dev_dbg(&pcidev->dev, "Setup PCI port: port %lx, irq %d, type %d\n", + uart.port.iobase, uart.port.irq, uart.port.iotype); + + priv->line[i] = serial8250_register_8250_port(&uart); + if (priv->line[i] < 0) { + dev_err(&pcidev->dev, + "Couldn't register serial port %lx, irq %d, type %d, error %d\n", + uart.port.iobase, uart.port.irq, + uart.port.iotype, priv->line[i]); + break; + } + } + priv->nr = i; + pci_set_drvdata(pcidev, priv); + return 0; +} + +static void exar_pci_remove(struct pci_dev *pcidev) +{ + struct exar8250 *priv = pci_get_drvdata(pcidev); + unsigned int i; + + for (i = 0; i < priv->nr; i++) + serial8250_unregister_port(priv->line[i]); + + if (priv->board->exit) + priv->board->exit(pcidev); +} + +static int __maybe_unused exar_suspend(struct device *dev) +{ + struct pci_dev *pcidev = to_pci_dev(dev); + struct exar8250 *priv = pci_get_drvdata(pcidev); + unsigned int i; + + for (i = 0; i < priv->nr; i++) + if (priv->line[i] >= 0) + serial8250_suspend_port(priv->line[i]); + + /* Ensure that every init quirk is properly torn down */ + if (priv->board->exit) + priv->board->exit(pcidev); + + return 0; +} + +static int __maybe_unused exar_resume(struct device *dev) +{ + struct pci_dev *pcidev = to_pci_dev(dev); + struct exar8250 *priv = pci_get_drvdata(pcidev); + unsigned int i; + + for (i = 0; i < priv->nr; i++) + if (priv->line[i] >= 0) + serial8250_resume_port(priv->line[i]); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(exar_pci_pm, exar_suspend, exar_resume); + +static const struct exar8250_board pbn_fastcom335_2 = { + .num_ports = 2, + .setup = pci_fastcom335_setup, +}; + +static const struct exar8250_board pbn_fastcom335_4 = { + .num_ports = 4, + .setup = pci_fastcom335_setup, +}; + +static const struct exar8250_board pbn_fastcom335_8 = { + .num_ports = 8, + .setup = pci_fastcom335_setup, +}; + +static const struct exar8250_board pbn_connect = { + .setup = pci_connect_tech_setup, +}; + +static const struct exar8250_board pbn_exar_ibm_saturn = { + .num_ports = 1, + .setup = pci_xr17c154_setup, +}; + +static const struct exar8250_board pbn_exar_XR17C15x = { + .setup = pci_xr17c154_setup, +}; + +static const struct exar8250_board pbn_exar_XR17V35x = { + .setup = pci_xr17v35x_setup, + .exit = pci_xr17v35x_exit, +}; + +static const struct exar8250_board pbn_exar_XR17V4358 = { + .num_ports = 12, + .has_slave = true, + .setup = pci_xr17v35x_setup, + .exit = pci_xr17v35x_exit, +}; + +static const struct exar8250_board pbn_exar_XR17V8358 = { + .num_ports = 16, + .has_slave = true, + .setup = pci_xr17v35x_setup, + .exit = pci_xr17v35x_exit, +}; + +#define CONNECT_DEVICE(devid, sdevid, bd) { \ + PCI_DEVICE_SUB( \ + PCI_VENDOR_ID_EXAR, \ + PCI_DEVICE_ID_EXAR_##devid, \ + PCI_SUBVENDOR_ID_CONNECT_TECH, \ + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_##sdevid), 0, 0, \ + (kernel_ulong_t)&bd \ + } + +#define EXAR_DEVICE(vend, devid, bd) { \ + PCI_VDEVICE(vend, PCI_DEVICE_ID_##devid), (kernel_ulong_t)&bd \ + } + +#define IBM_DEVICE(devid, sdevid, bd) { \ + PCI_DEVICE_SUB( \ + PCI_VENDOR_ID_EXAR, \ + PCI_DEVICE_ID_EXAR_##devid, \ + PCI_VENDOR_ID_IBM, \ + PCI_SUBDEVICE_ID_IBM_##sdevid), 0, 0, \ + (kernel_ulong_t)&bd \ + } + +static struct pci_device_id exar_pci_tbl[] = { + CONNECT_DEVICE(XR17C152, UART_2_232, pbn_connect), + CONNECT_DEVICE(XR17C154, UART_4_232, pbn_connect), + CONNECT_DEVICE(XR17C158, UART_8_232, pbn_connect), + CONNECT_DEVICE(XR17C152, UART_1_1, pbn_connect), + CONNECT_DEVICE(XR17C154, UART_2_2, pbn_connect), + CONNECT_DEVICE(XR17C158, UART_4_4, pbn_connect), + CONNECT_DEVICE(XR17C152, UART_2, pbn_connect), + CONNECT_DEVICE(XR17C154, UART_4, pbn_connect), + CONNECT_DEVICE(XR17C158, UART_8, pbn_connect), + CONNECT_DEVICE(XR17C152, UART_2_485, pbn_connect), + CONNECT_DEVICE(XR17C154, UART_4_485, pbn_connect), + CONNECT_DEVICE(XR17C158, UART_8_485, pbn_connect), + + IBM_DEVICE(XR17C152, SATURN_SERIAL_ONE_PORT, pbn_exar_ibm_saturn), + + /* Exar Corp. XR17C15[248] Dual/Quad/Octal UART */ + EXAR_DEVICE(EXAR, EXAR_XR17C152, pbn_exar_XR17C15x), + EXAR_DEVICE(EXAR, EXAR_XR17C154, pbn_exar_XR17C15x), + EXAR_DEVICE(EXAR, EXAR_XR17C158, pbn_exar_XR17C15x), + + /* Exar Corp. XR17V[48]35[248] Dual/Quad/Octal/Hexa PCIe UARTs */ + EXAR_DEVICE(EXAR, EXAR_XR17V352, pbn_exar_XR17V35x), + EXAR_DEVICE(EXAR, EXAR_XR17V354, pbn_exar_XR17V35x), + EXAR_DEVICE(EXAR, EXAR_XR17V358, pbn_exar_XR17V35x), + EXAR_DEVICE(EXAR, EXAR_XR17V4358, pbn_exar_XR17V4358), + EXAR_DEVICE(EXAR, EXAR_XR17V8358, pbn_exar_XR17V8358), + EXAR_DEVICE(COMMTECH, COMMTECH_4222PCIE, pbn_exar_XR17V35x), + EXAR_DEVICE(COMMTECH, COMMTECH_4224PCIE, pbn_exar_XR17V35x), + EXAR_DEVICE(COMMTECH, COMMTECH_4228PCIE, pbn_exar_XR17V35x), + + EXAR_DEVICE(COMMTECH, COMMTECH_4222PCI335, pbn_fastcom335_2), + EXAR_DEVICE(COMMTECH, COMMTECH_4224PCI335, pbn_fastcom335_4), + EXAR_DEVICE(COMMTECH, COMMTECH_2324PCI335, pbn_fastcom335_4), + EXAR_DEVICE(COMMTECH, COMMTECH_2328PCI335, pbn_fastcom335_8), + { 0, } +}; +MODULE_DEVICE_TABLE(pci, exar_pci_tbl); + +static struct pci_driver exar_pci_driver = { + .name = "exar_serial", + .probe = exar_pci_probe, + .remove = exar_pci_remove, + .driver = { + .pm = &exar_pci_pm, + }, + .id_table = exar_pci_tbl, +}; +module_pci_driver(exar_pci_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Exar Serial Dricer"); +MODULE_AUTHOR("Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>"); diff --git a/drivers/tty/serial/8250/8250_gsc.c b/drivers/tty/serial/8250/8250_gsc.c index b1e6ae9f1ff9..63306de4390d 100644 --- a/drivers/tty/serial/8250/8250_gsc.c +++ b/drivers/tty/serial/8250/8250_gsc.c @@ -60,6 +60,10 @@ static int __init serial_init_chip(struct parisc_device *dev) 7272727 : 1843200; uart.port.mapbase = address; uart.port.membase = ioremap_nocache(address, 16); + if (!uart.port.membase) { + dev_warn(&dev->dev, "Failed to map memory\n"); + return -ENOMEM; + } uart.port.irq = dev->irq; uart.port.flags = UPF_BOOT_AUTOCONF; uart.port.dev = &dev->dev; diff --git a/drivers/tty/serial/8250/8250_hp300.c b/drivers/tty/serial/8250/8250_hp300.c index 38166db2b824..115190b7962a 100644 --- a/drivers/tty/serial/8250/8250_hp300.c +++ b/drivers/tty/serial/8250/8250_hp300.c @@ -19,7 +19,7 @@ #include "8250.h" -#if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI) +#if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI) && !defined(CONFIG_COMPILE_TEST) #warning CONFIG_SERIAL_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure? #endif diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index 58cbb30a9401..f3ea90f0e411 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -332,10 +332,10 @@ static void lpss8250_remove(struct pci_dev *pdev) { struct lpss8250 *lpss = pci_get_drvdata(pdev); + serial8250_unregister_port(lpss->line); + if (lpss->board->exit) lpss->board->exit(lpss); - - serial8250_unregister_port(lpss->line); } static const struct lpss8250_board byt_board = { diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index ac013edf4992..ec957cce8c9a 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -75,6 +75,37 @@ static int pnw_setup(struct mid8250 *mid, struct uart_port *p) return 0; } +static int tng_handle_irq(struct uart_port *p) +{ + struct mid8250 *mid = p->private_data; + struct uart_8250_port *up = up_to_u8250p(p); + struct hsu_dma_chip *chip; + u32 status; + int ret = 0; + int err; + + chip = pci_get_drvdata(mid->dma_dev); + + /* Rx DMA */ + err = hsu_dma_get_status(chip, mid->dma_index * 2 + 1, &status); + if (err > 0) { + serial8250_rx_dma_flush(up); + ret |= 1; + } else if (err == 0) + ret |= hsu_dma_do_irq(chip, mid->dma_index * 2 + 1, status); + + /* Tx DMA */ + err = hsu_dma_get_status(chip, mid->dma_index * 2, &status); + if (err > 0) + ret |= 1; + else if (err == 0) + ret |= hsu_dma_do_irq(chip, mid->dma_index * 2, status); + + /* UART */ + ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR)); + return IRQ_RETVAL(ret); +} + static int tng_setup(struct mid8250 *mid, struct uart_port *p) { struct pci_dev *pdev = to_pci_dev(p->dev); @@ -90,6 +121,8 @@ static int tng_setup(struct mid8250 *mid, struct uart_port *p) mid->dma_index = index; mid->dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(5, 0)); + + p->handle_irq = tng_handle_irq; return 0; } @@ -131,8 +164,16 @@ static int dnv_setup(struct mid8250 *mid, struct uart_port *p) unsigned int bar = FL_GET_BASE(mid->board->flags); int ret; + pci_set_master(pdev); + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) + return ret; + + p->irq = pci_irq_vector(pdev, 0); + chip->dev = &pdev->dev; - chip->irq = pdev->irq; + chip->irq = pci_irq_vector(pdev, 0); chip->regs = p->membase; chip->length = pci_resource_len(pdev, bar); chip->offset = DNV_DMA_CHAN_OFFSET; @@ -250,8 +291,6 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) return ret; - pci_set_master(pdev); - mid = devm_kzalloc(&pdev->dev, sizeof(*mid), GFP_KERNEL); if (!mid) return -ENOMEM; diff --git a/drivers/tty/serial/8250/8250_moxa.c b/drivers/tty/serial/8250/8250_moxa.c index 26eb5393a263..d5069b2d4d79 100644 --- a/drivers/tty/serial/8250/8250_moxa.c +++ b/drivers/tty/serial/8250/8250_moxa.c @@ -68,6 +68,7 @@ static int moxa8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) sizeof(unsigned int) * nr_ports, GFP_KERNEL); if (!brd) return -ENOMEM; + brd->num_ports = nr_ports; memset(&uart, 0, sizeof(struct uart_8250_port)); diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index d25ab1cd4295..1cbadafc6889 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -172,7 +172,8 @@ static int of_platform_serial_probe(struct platform_device *ofdev) { const struct of_device_id *match; struct of_serial_info *info; - struct uart_port port; + struct uart_8250_port port8250; + u32 tx_threshold; int port_type; int ret; @@ -188,41 +189,24 @@ static int of_platform_serial_probe(struct platform_device *ofdev) return -ENOMEM; port_type = (unsigned long)match->data; - ret = of_platform_serial_setup(ofdev, port_type, &port, info); + memset(&port8250, 0, sizeof(port8250)); + ret = of_platform_serial_setup(ofdev, port_type, &port8250.port, info); if (ret) goto out; - switch (port_type) { - case PORT_8250 ... PORT_MAX_8250: - { - u32 tx_threshold; - struct uart_8250_port port8250; - memset(&port8250, 0, sizeof(port8250)); - port8250.port = port; + if (port8250.port.fifosize) + port8250.capabilities = UART_CAP_FIFO; - if (port.fifosize) - port8250.capabilities = UART_CAP_FIFO; + /* Check for TX FIFO threshold & set tx_loadsz */ + if ((of_property_read_u32(ofdev->dev.of_node, "tx-threshold", + &tx_threshold) == 0) && + (tx_threshold < port8250.port.fifosize)) + port8250.tx_loadsz = port8250.port.fifosize - tx_threshold; - /* Check for TX FIFO threshold & set tx_loadsz */ - if ((of_property_read_u32(ofdev->dev.of_node, "tx-threshold", - &tx_threshold) == 0) && - (tx_threshold < port.fifosize)) - port8250.tx_loadsz = port.fifosize - tx_threshold; + if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control")) + port8250.capabilities |= UART_CAP_AFE; - if (of_property_read_bool(ofdev->dev.of_node, - "auto-flow-control")) - port8250.capabilities |= UART_CAP_AFE; - - ret = serial8250_register_8250_port(&port8250); - break; - } - default: - /* need to add code for these */ - case PORT_UNKNOWN: - dev_info(&ofdev->dev, "Unknown serial port found, ignored\n"); - ret = -ENODEV; - break; - } + ret = serial8250_register_8250_port(&port8250); if (ret < 0) goto out; @@ -232,7 +216,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev) return 0; out: kfree(info); - irq_dispose_mapping(port.irq); + irq_dispose_mapping(port8250.port.irq); return ret; } @@ -242,14 +226,8 @@ out: static int of_platform_serial_remove(struct platform_device *ofdev) { struct of_serial_info *info = platform_get_drvdata(ofdev); - switch (info->type) { - case PORT_8250 ... PORT_MAX_8250: - serial8250_unregister_port(info->line); - break; - default: - /* need to add code for these */ - break; - } + + serial8250_unregister_port(info->line); if (info->clk) clk_disable_unprepare(info->clk); @@ -258,18 +236,23 @@ static int of_platform_serial_remove(struct platform_device *ofdev) } #ifdef CONFIG_PM_SLEEP -static void of_serial_suspend_8250(struct of_serial_info *info) +static int of_serial_suspend(struct device *dev) { + struct of_serial_info *info = dev_get_drvdata(dev); struct uart_8250_port *port8250 = serial8250_get_port(info->line); struct uart_port *port = &port8250->port; serial8250_suspend_port(info->line); + if (info->clk && (!uart_console(port) || console_suspend_enabled)) clk_disable_unprepare(info->clk); + + return 0; } -static void of_serial_resume_8250(struct of_serial_info *info) +static int of_serial_resume(struct device *dev) { + struct of_serial_info *info = dev_get_drvdata(dev); struct uart_8250_port *port8250 = serial8250_get_port(info->line); struct uart_port *port = &port8250->port; @@ -277,34 +260,6 @@ static void of_serial_resume_8250(struct of_serial_info *info) clk_prepare_enable(info->clk); serial8250_resume_port(info->line); -} - -static int of_serial_suspend(struct device *dev) -{ - struct of_serial_info *info = dev_get_drvdata(dev); - - switch (info->type) { - case PORT_8250 ... PORT_MAX_8250: - of_serial_suspend_8250(info); - break; - default: - break; - } - - return 0; -} - -static int of_serial_resume(struct device *dev) -{ - struct of_serial_info *info = dev_get_drvdata(dev); - - switch (info->type) { - case PORT_8250 ... PORT_MAX_8250: - of_serial_resume_8250(info); - break; - default: - break; - } return 0; } @@ -332,6 +287,7 @@ static const struct of_device_id of_platform_serial_table[] = { .data = (void *)PORT_ALTR_16550_F128, }, { .compatible = "mrvl,mmp-uart", .data = (void *)PORT_XSCALE, }, + { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, }, { /* end of list */ }, }; MODULE_DEVICE_TABLE(of, of_platform_serial_table); diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 61ad6c3b20a0..e7e64913a748 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -790,6 +790,7 @@ static void omap_8250_rx_dma_flush(struct uart_8250_port *p) { struct omap8250_priv *priv = p->port.private_data; struct uart_8250_dma *dma = p->dma; + struct dma_tx_state state; unsigned long flags; int ret; @@ -800,10 +801,12 @@ static void omap_8250_rx_dma_flush(struct uart_8250_port *p) return; } - ret = dmaengine_pause(dma->rxchan); - if (WARN_ON_ONCE(ret)) - priv->rx_dma_broken = true; - + ret = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + if (ret == DMA_IN_PROGRESS) { + ret = dmaengine_pause(dma->rxchan); + if (WARN_ON_ONCE(ret)) + priv->rx_dma_broken = true; + } spin_unlock_irqrestore(&priv->rx_dma_lock, flags); __dma_rx_do_complete(p); @@ -1075,15 +1078,15 @@ static int omap8250_no_handle_irq(struct uart_port *port) } static const u8 am3352_habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE; -static const u8 am4372_habit = UART_ERRATA_CLOCK_DISABLE; +static const u8 dra742_habit = UART_ERRATA_CLOCK_DISABLE; static const struct of_device_id omap8250_dt_ids[] = { { .compatible = "ti,omap2-uart" }, { .compatible = "ti,omap3-uart" }, { .compatible = "ti,omap4-uart" }, { .compatible = "ti,am3352-uart", .data = &am3352_habit, }, - { .compatible = "ti,am4372-uart", .data = &am4372_habit, }, - { .compatible = "ti,dra742-uart", .data = &am4372_habit, }, + { .compatible = "ti,am4372-uart", .data = &am3352_habit, }, + { .compatible = "ti,dra742-uart", .data = &dra742_habit, }, {}, }; MODULE_DEVICE_TABLE(of, omap8250_dt_ids); @@ -1218,14 +1221,6 @@ static int omap8250_probe(struct platform_device *pdev) priv->omap8250_dma.rx_size = RX_TRIGGER; priv->omap8250_dma.rxconf.src_maxburst = RX_TRIGGER; priv->omap8250_dma.txconf.dst_maxburst = TX_TRIGGER; - - if (of_machine_is_compatible("ti,am33xx")) - priv->habit |= OMAP_DMA_TX_KICK; - /* - * pause is currently not supported atleast on omap-sdma - * and edma on most earlier kernels. - */ - priv->rx_dma_broken = true; } } #endif @@ -1240,7 +1235,8 @@ static int omap8250_probe(struct platform_device *pdev) pm_runtime_put_autosuspend(&pdev->dev); return 0; err: - pm_runtime_put(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return ret; } @@ -1249,6 +1245,7 @@ static int omap8250_remove(struct platform_device *pdev) { struct omap8250_priv *priv = platform_get_drvdata(pdev); + pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); serial8250_unregister_port(priv->line); @@ -1348,6 +1345,10 @@ static int omap8250_runtime_suspend(struct device *dev) struct omap8250_priv *priv = dev_get_drvdata(dev); struct uart_8250_port *up; + /* In case runtime-pm tries this before we are setup */ + if (!priv) + return 0; + up = serial8250_get_port(priv->line); /* * When using 'no_console_suspend', the console UART must not be diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 116436b7fa52..00e51a064388 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1477,11 +1477,16 @@ static int pci_fintek_init(struct pci_dev *dev) { unsigned long iobase; u32 max_port, i; - u32 bar_data[3]; + resource_size_t bar_data[3]; u8 config_base; struct serial_private *priv = pci_get_drvdata(dev); struct uart_8250_port *port; + if (!(pci_resource_flags(dev, 5) & IORESOURCE_IO) || + !(pci_resource_flags(dev, 4) & IORESOURCE_IO) || + !(pci_resource_flags(dev, 3) & IORESOURCE_IO)) + return -ENODEV; + switch (dev->device) { case 0x1104: /* 4 ports */ case 0x1108: /* 8 ports */ @@ -1495,9 +1500,9 @@ static int pci_fintek_init(struct pci_dev *dev) } /* Get the io address dispatch from the BIOS */ - pci_read_config_dword(dev, 0x24, &bar_data[0]); - pci_read_config_dword(dev, 0x20, &bar_data[1]); - pci_read_config_dword(dev, 0x1c, &bar_data[2]); + bar_data[0] = pci_resource_start(dev, 5); + bar_data[1] = pci_resource_start(dev, 4); + bar_data[2] = pci_resource_start(dev, 3); for (i = 0; i < max_port; ++i) { /* UART0 configuration offset start from 0x40 */ @@ -1605,135 +1610,6 @@ static int pci_eg20t_init(struct pci_dev *dev) #endif } -#define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358 -#define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358 - -#define UART_EXAR_MPIOINT_7_0 0x8f /* MPIOINT[7:0] */ -#define UART_EXAR_MPIOLVL_7_0 0x90 /* MPIOLVL[7:0] */ -#define UART_EXAR_MPIO3T_7_0 0x91 /* MPIO3T[7:0] */ -#define UART_EXAR_MPIOINV_7_0 0x92 /* MPIOINV[7:0] */ -#define UART_EXAR_MPIOSEL_7_0 0x93 /* MPIOSEL[7:0] */ -#define UART_EXAR_MPIOOD_7_0 0x94 /* MPIOOD[7:0] */ -#define UART_EXAR_MPIOINT_15_8 0x95 /* MPIOINT[15:8] */ -#define UART_EXAR_MPIOLVL_15_8 0x96 /* MPIOLVL[15:8] */ -#define UART_EXAR_MPIO3T_15_8 0x97 /* MPIO3T[15:8] */ -#define UART_EXAR_MPIOINV_15_8 0x98 /* MPIOINV[15:8] */ -#define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[15:8] */ -#define UART_EXAR_MPIOOD_15_8 0x9a /* MPIOOD[15:8] */ - -static int -pci_xr17c154_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_8250_port *port, int idx) -{ - port->port.flags |= UPF_EXAR_EFR; - return pci_default_setup(priv, board, port, idx); -} - -static inline int -xr17v35x_has_slave(struct serial_private *priv) -{ - const int dev_id = priv->dev->device; - - return ((dev_id == PCI_DEVICE_ID_EXAR_XR17V4358) || - (dev_id == PCI_DEVICE_ID_EXAR_XR17V8358)); -} - -static int -pci_xr17v35x_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_8250_port *port, int idx) -{ - u8 __iomem *p; - - p = pci_ioremap_bar(priv->dev, 0); - if (p == NULL) - return -ENOMEM; - - port->port.flags |= UPF_EXAR_EFR; - - /* - * Setup the uart clock for the devices on expansion slot to - * half the clock speed of the main chip (which is 125MHz) - */ - if (xr17v35x_has_slave(priv) && idx >= 8) - port->port.uartclk = (7812500 * 16 / 2); - - /* - * Setup Multipurpose Input/Output pins. - */ - if (idx == 0) { - writeb(0x00, p + UART_EXAR_MPIOINT_7_0); - writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); - writeb(0x00, p + UART_EXAR_MPIO3T_7_0); - writeb(0x00, p + UART_EXAR_MPIOINV_7_0); - writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); - writeb(0x00, p + UART_EXAR_MPIOOD_7_0); - writeb(0x00, p + UART_EXAR_MPIOINT_15_8); - writeb(0x00, p + UART_EXAR_MPIOLVL_15_8); - writeb(0x00, p + UART_EXAR_MPIO3T_15_8); - writeb(0x00, p + UART_EXAR_MPIOINV_15_8); - writeb(0x00, p + UART_EXAR_MPIOSEL_15_8); - writeb(0x00, p + UART_EXAR_MPIOOD_15_8); - } - writeb(0x00, p + UART_EXAR_8XMODE); - writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); - writeb(128, p + UART_EXAR_TXTRG); - writeb(128, p + UART_EXAR_RXTRG); - iounmap(p); - - return pci_default_setup(priv, board, port, idx); -} - -#define PCI_DEVICE_ID_COMMTECH_4222PCI335 0x0004 -#define PCI_DEVICE_ID_COMMTECH_4224PCI335 0x0002 -#define PCI_DEVICE_ID_COMMTECH_2324PCI335 0x000a -#define PCI_DEVICE_ID_COMMTECH_2328PCI335 0x000b - -static int -pci_fastcom335_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_8250_port *port, int idx) -{ - u8 __iomem *p; - - p = pci_ioremap_bar(priv->dev, 0); - if (p == NULL) - return -ENOMEM; - - port->port.flags |= UPF_EXAR_EFR; - - /* - * Setup Multipurpose Input/Output pins. - */ - if (idx == 0) { - switch (priv->dev->device) { - case PCI_DEVICE_ID_COMMTECH_4222PCI335: - case PCI_DEVICE_ID_COMMTECH_4224PCI335: - writeb(0x78, p + UART_EXAR_MPIOLVL_7_0); - writeb(0x00, p + UART_EXAR_MPIOINV_7_0); - writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); - break; - case PCI_DEVICE_ID_COMMTECH_2324PCI335: - case PCI_DEVICE_ID_COMMTECH_2328PCI335: - writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); - writeb(0xc0, p + UART_EXAR_MPIOINV_7_0); - writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0); - break; - } - writeb(0x00, p + UART_EXAR_MPIOINT_7_0); - writeb(0x00, p + UART_EXAR_MPIO3T_7_0); - writeb(0x00, p + UART_EXAR_MPIOOD_7_0); - } - writeb(0x00, p + UART_EXAR_8XMODE); - writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); - writeb(32, p + UART_EXAR_TXTRG); - writeb(32, p + UART_EXAR_RXTRG); - iounmap(p); - - return pci_default_setup(priv, board, port, idx); -} - static int pci_wch_ch353_setup(struct serial_private *priv, const struct pciserial_board *board, @@ -1809,9 +1685,6 @@ pci_wch_ch38x_setup(struct serial_private *priv, #define PCI_VENDOR_ID_AGESTAR 0x5372 #define PCI_DEVICE_ID_AGESTAR_9375 0x6872 #define PCI_VENDOR_ID_ASIX 0x9710 -#define PCI_DEVICE_ID_COMMTECH_4224PCIE 0x0020 -#define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 -#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 #define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a #define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e @@ -2273,65 +2146,6 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .setup = pci_timedia_setup, }, /* - * Exar cards - */ - { - .vendor = PCI_VENDOR_ID_EXAR, - .device = PCI_DEVICE_ID_EXAR_XR17C152, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_xr17c154_setup, - }, - { - .vendor = PCI_VENDOR_ID_EXAR, - .device = PCI_DEVICE_ID_EXAR_XR17C154, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_xr17c154_setup, - }, - { - .vendor = PCI_VENDOR_ID_EXAR, - .device = PCI_DEVICE_ID_EXAR_XR17C158, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_xr17c154_setup, - }, - { - .vendor = PCI_VENDOR_ID_EXAR, - .device = PCI_DEVICE_ID_EXAR_XR17V352, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_xr17v35x_setup, - }, - { - .vendor = PCI_VENDOR_ID_EXAR, - .device = PCI_DEVICE_ID_EXAR_XR17V354, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_xr17v35x_setup, - }, - { - .vendor = PCI_VENDOR_ID_EXAR, - .device = PCI_DEVICE_ID_EXAR_XR17V358, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_xr17v35x_setup, - }, - { - .vendor = PCI_VENDOR_ID_EXAR, - .device = PCI_DEVICE_ID_EXAR_XR17V4358, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_xr17v35x_setup, - }, - { - .vendor = PCI_VENDOR_ID_EXAR, - .device = PCI_DEVICE_ID_EXAR_XR17V8358, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_xr17v35x_setup, - }, - /* * Xircom cards */ { @@ -2556,59 +2370,6 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .setup = pci_asix_setup, }, /* - * Commtech, Inc. Fastcom adapters - * - */ - { - .vendor = PCI_VENDOR_ID_COMMTECH, - .device = PCI_DEVICE_ID_COMMTECH_4222PCI335, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_fastcom335_setup, - }, - { - .vendor = PCI_VENDOR_ID_COMMTECH, - .device = PCI_DEVICE_ID_COMMTECH_4224PCI335, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_fastcom335_setup, - }, - { - .vendor = PCI_VENDOR_ID_COMMTECH, - .device = PCI_DEVICE_ID_COMMTECH_2324PCI335, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_fastcom335_setup, - }, - { - .vendor = PCI_VENDOR_ID_COMMTECH, - .device = PCI_DEVICE_ID_COMMTECH_2328PCI335, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_fastcom335_setup, - }, - { - .vendor = PCI_VENDOR_ID_COMMTECH, - .device = PCI_DEVICE_ID_COMMTECH_4222PCIE, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_xr17v35x_setup, - }, - { - .vendor = PCI_VENDOR_ID_COMMTECH, - .device = PCI_DEVICE_ID_COMMTECH_4224PCIE, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_xr17v35x_setup, - }, - { - .vendor = PCI_VENDOR_ID_COMMTECH, - .device = PCI_DEVICE_ID_COMMTECH_4228PCIE, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_xr17v35x_setup, - }, - /* * Broadcom TruManage (NetXtreme) */ { @@ -2719,17 +2480,11 @@ enum pci_board_num_t { pbn_b0_4_1152000, - pbn_b0_2_1152000_200, - pbn_b0_4_1152000_200, - pbn_b0_8_1152000_200, + pbn_b0_4_1250000, pbn_b0_2_1843200, pbn_b0_4_1843200, - pbn_b0_2_1843200_200, - pbn_b0_4_1843200_200, - pbn_b0_8_1843200_200, - pbn_b0_1_4000000, pbn_b0_bt_1_115200, @@ -2820,15 +2575,6 @@ enum pci_board_num_t { pbn_computone_6, pbn_computone_8, pbn_sbsxrsio, - pbn_exar_XR17C152, - pbn_exar_XR17C154, - pbn_exar_XR17C158, - pbn_exar_XR17V352, - pbn_exar_XR17V354, - pbn_exar_XR17V358, - pbn_exar_XR17V4358, - pbn_exar_XR17V8358, - pbn_exar_ibm_saturn, pbn_pasemi_1682M, pbn_ni8430_2, pbn_ni8430_4, @@ -2933,25 +2679,11 @@ static struct pciserial_board pci_boards[] = { .uart_offset = 8, }, - [pbn_b0_2_1152000_200] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 1152000, - .uart_offset = 0x200, - }, - - [pbn_b0_4_1152000_200] = { + [pbn_b0_4_1250000] = { .flags = FL_BASE0, .num_ports = 4, - .base_baud = 1152000, - .uart_offset = 0x200, - }, - - [pbn_b0_8_1152000_200] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 1152000, - .uart_offset = 0x200, + .base_baud = 1250000, + .uart_offset = 8, }, [pbn_b0_2_1843200] = { @@ -2967,24 +2699,6 @@ static struct pciserial_board pci_boards[] = { .uart_offset = 8, }, - [pbn_b0_2_1843200_200] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 1843200, - .uart_offset = 0x200, - }, - [pbn_b0_4_1843200_200] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 1843200, - .uart_offset = 0x200, - }, - [pbn_b0_8_1843200_200] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 1843200, - .uart_offset = 0x200, - }, [pbn_b0_1_4000000] = { .flags = FL_BASE0, .num_ports = 1, @@ -3469,76 +3183,6 @@ static struct pciserial_board pci_boards[] = { .reg_shift = 4, }, /* - * Exar Corp. XR17C15[248] Dual/Quad/Octal UART - * Only basic 16550A support. - * XR17C15[24] are not tested, but they should work. - */ - [pbn_exar_XR17C152] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 0x200, - }, - [pbn_exar_XR17C154] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 0x200, - }, - [pbn_exar_XR17C158] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 921600, - .uart_offset = 0x200, - }, - [pbn_exar_XR17V352] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 7812500, - .uart_offset = 0x400, - .reg_shift = 0, - .first_offset = 0, - }, - [pbn_exar_XR17V354] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 7812500, - .uart_offset = 0x400, - .reg_shift = 0, - .first_offset = 0, - }, - [pbn_exar_XR17V358] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 7812500, - .uart_offset = 0x400, - .reg_shift = 0, - .first_offset = 0, - }, - [pbn_exar_XR17V4358] = { - .flags = FL_BASE0, - .num_ports = 12, - .base_baud = 7812500, - .uart_offset = 0x400, - .reg_shift = 0, - .first_offset = 0, - }, - [pbn_exar_XR17V8358] = { - .flags = FL_BASE0, - .num_ports = 16, - .base_baud = 7812500, - .uart_offset = 0x400, - .reg_shift = 0, - .first_offset = 0, - }, - [pbn_exar_ibm_saturn] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 921600, - .uart_offset = 0x200, - }, - - /* * PA Semi PWRficient PA6T-1682M on-chip UART */ [pbn_pasemi_1682M] = { @@ -3734,6 +3378,10 @@ static const struct pci_device_id blacklist[] = { { PCI_VDEVICE(INTEL, 0x228c), }, { PCI_VDEVICE(INTEL, 0x9ce3), }, { PCI_VDEVICE(INTEL, 0x9ce4), }, + + /* Exar devices */ + { PCI_VDEVICE(EXAR, PCI_ANY_ID), }, + { PCI_VDEVICE(COMMTECH, PCI_ANY_ID), }, }; /* @@ -3908,7 +3556,7 @@ err_out: } EXPORT_SYMBOL_GPL(pciserial_init_ports); -void pciserial_detach_ports(struct serial_private *priv) +static void pciserial_detach_ports(struct serial_private *priv) { struct pci_serial_quirk *quirk; int i; @@ -4159,58 +3807,6 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_VENDOR_ID_AFAVLAB, PCI_SUBDEVICE_ID_AFAVLAB_P061, 0, 0, pbn_b0_4_1152000 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232, 0, 0, - pbn_b0_2_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232, 0, 0, - pbn_b0_4_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232, 0, 0, - pbn_b0_8_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1, 0, 0, - pbn_b0_2_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2, 0, 0, - pbn_b0_4_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4, 0, 0, - pbn_b0_8_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2, 0, 0, - pbn_b0_2_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4, 0, 0, - pbn_b0_4_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8, 0, 0, - pbn_b0_8_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485, 0, 0, - pbn_b0_2_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485, 0, 0, - pbn_b0_4_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485, 0, 0, - pbn_b0_8_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_VENDOR_ID_IBM, PCI_SUBDEVICE_ID_IBM_SATURN_SERIAL_ONE_PORT, - 0, 0, pbn_exar_ibm_saturn }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_bt_1_115200 }, @@ -4938,45 +4534,6 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b3_8_115200 }, - - /* - * Exar Corp. XR17C15[248] Dual/Quad/Octal UART - */ - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17C152 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17C154 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17C158 }, - /* - * Exar Corp. XR17V[48]35[248] Dual/Quad/Octal/Hexa PCIe UARTs - */ - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V352, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17V352 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V354, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17V354 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V358, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17V358 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V4358, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17V4358 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V8358, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17V8358 }, /* * Pericom PI7C9X795[1248] Uno/Dual/Quad/Octal UART */ @@ -5552,43 +5109,15 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_wch384_4 }, - /* - * Commtech, Inc. Fastcom adapters - */ - { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4222PCI335, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_b0_2_1152000_200 }, - { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4224PCI335, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_b0_4_1152000_200 }, - { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_2324PCI335, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_b0_4_1152000_200 }, - { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_2328PCI335, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_b0_8_1152000_200 }, - { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4222PCIE, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17V352 }, - { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4224PCIE, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17V354 }, - { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4228PCIE, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17V358 }, - /* Fintek PCI serial cards */ { PCI_DEVICE(0x1c29, 0x1104), .driver_data = pbn_fintek_4 }, { PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 }, { PCI_DEVICE(0x1c29, 0x1112), .driver_data = pbn_fintek_12 }, + /* MKS Tenta SCOM-080x serial cards */ + { PCI_DEVICE(0x1601, 0x0800), .driver_data = pbn_b0_4_1250000 }, + { PCI_DEVICE(0x1601, 0xa801), .driver_data = pbn_b0_4_1250000 }, + /* * These entries match devices with class COMMUNICATION_SERIAL, * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index c13fec451d03..6119516ef5fc 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -45,6 +45,12 @@ #include "8250.h" /* + * These are definitions for the Exar XR17V35X and XR17(C|D)15X + */ +#define UART_EXAR_SLEEP 0x8b /* Sleep mode */ +#define UART_EXAR_DVID 0x8d /* Device identification */ + +/* * Debugging. */ #if 0 @@ -273,6 +279,15 @@ static const struct serial8250_config uart_config[] = { .rxtrig_bytes = {1, 4, 8, 14}, .flags = UART_CAP_FIFO, }, + [PORT_DA830] = { + .name = "TI DA8xx/66AK2x", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_DMA_SELECT | UART_FCR_ENABLE_FIFO | + UART_FCR_R_TRIG_10, + .rxtrig_bytes = {1, 4, 8, 14}, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, }; /* Uart divisor latch read */ @@ -1753,8 +1768,6 @@ void serial8250_tx_chars(struct uart_8250_port *up) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); - pr_debug("%s: THRE\n", __func__); - /* * With RPM enabled, we have to wait until the FIFO is empty before the * HW can go idle. So we get here once again with empty FIFO and disable @@ -1819,8 +1832,6 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) status = serial_port_in(port, UART_LSR); - pr_debug("%s: status = %x\n", __func__, status); - if (status & (UART_LSR_DR | UART_LSR_BI)) { if (!up->dma || handle_rx_dma(up, iir)) status = serial8250_rx_chars(up, status); @@ -2118,6 +2129,19 @@ int serial8250_do_startup(struct uart_port *port) serial_port_out(port, UART_LCR, 0); } + if (port->type == PORT_DA830) { + /* Reset the port */ + serial_port_out(port, UART_IER, 0); + serial_port_out(port, UART_DA830_PWREMU_MGMT, 0); + mdelay(10); + + /* Enable Tx, Rx and free run mode */ + serial_port_out(port, UART_DA830_PWREMU_MGMT, + UART_DA830_PWREMU_MGMT_UTRST | + UART_DA830_PWREMU_MGMT_URRST | + UART_DA830_PWREMU_MGMT_FREE); + } + #ifdef CONFIG_SERIAL_8250_RSA /* * If this is an RSA port, see if we can kick it up to the diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 0b8b6740ba43..a65fb8197aec 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -117,7 +117,7 @@ config SERIAL_8250_DMA compatible UART controllers that support DMA signaling. config SERIAL_8250_PCI - tristate "8250/16550 PCI device support" if EXPERT + tristate "8250/16550 PCI device support" depends on SERIAL_8250 && PCI default SERIAL_8250 help @@ -127,6 +127,11 @@ config SERIAL_8250_PCI Note that serial ports on NetMos 9835 Multi-I/O cards are handled by the parport_serial driver, enabled with CONFIG_PARPORT_SERIAL. +config SERIAL_8250_EXAR + tristate "8250/16550 PCI device support" + depends on SERIAL_8250_PCI + default SERIAL_8250 + config SERIAL_8250_HP300 tristate depends on SERIAL_8250 && HP300 @@ -402,7 +407,7 @@ config SERIAL_8250_INGENIC its UARTs, say Y to this option. If unsure, say N. config SERIAL_8250_LPSS - tristate "Support for serial ports on Intel LPSS platforms" if EXPERT + tristate "Support for serial ports on Intel LPSS platforms" default SERIAL_8250 depends on SERIAL_8250 && PCI depends on X86 || COMPILE_TEST @@ -417,7 +422,7 @@ config SERIAL_8250_LPSS - Intel Quark X1000 SoC config SERIAL_8250_MID - tristate "Support for serial ports on Intel MID platforms" if EXPERT + tristate "Support for serial ports on Intel MID platforms" default SERIAL_8250 depends on SERIAL_8250 && PCI depends on X86 || COMPILE_TEST diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 850e721877a9..2f30f9ecdb1b 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_SERIAL_8250) += 8250.o 8250_base.o 8250_base-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o +obj-$(CONFIG_SERIAL_8250_EXAR) += 8250_exar.o obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index e9cf5b67f1b7..6117ac8da48f 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1161,6 +1161,7 @@ config SERIAL_LANTIQ depends on LANTIQ select SERIAL_CORE select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON help Support for console and UART on Lantiq SoCs. diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index 5d41d5b92619..f2f251075109 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -554,7 +554,7 @@ static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser) return ret; } -static struct uart_ops amba_pl010_pops = { +static const struct uart_ops amba_pl010_pops = { .tx_empty = pl010_tx_empty, .set_mctrl = pl010_set_mctrl, .get_mctrl = pl010_get_mctrl, diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index d4171d71a258..8789ea423ccf 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -97,6 +97,7 @@ struct vendor_data { unsigned int fr_dsr; unsigned int fr_cts; unsigned int fr_ri; + unsigned int inv_fr; bool access_32b; bool oversampling; bool dma_threshold; @@ -141,6 +142,30 @@ static struct vendor_data vendor_sbsa = { .fixed_options = true, }; +/* + * Erratum 44 for QDF2432v1 and QDF2400v1 SoCs describes the BUSY bit as + * occasionally getting stuck as 1. To avoid the potential for a hang, check + * TXFE == 0 instead of BUSY == 1. This may not be suitable for all UART + * implementations, so only do so if an affected platform is detected in + * parse_spcr(). + */ +static bool qdf2400_e44_present = false; + +static struct vendor_data vendor_qdt_qdf2400_e44 = { + .reg_offset = pl011_std_offsets, + .fr_busy = UART011_FR_TXFE, + .fr_dsr = UART01x_FR_DSR, + .fr_cts = UART01x_FR_CTS, + .fr_ri = UART011_FR_RI, + .inv_fr = UART011_FR_TXFE, + .access_32b = true, + .oversampling = false, + .dma_threshold = false, + .cts_event_workaround = false, + .always_enabled = true, + .fixed_options = true, +}; + static u16 pl011_st_offsets[REG_ARRAY_SIZE] = { [REG_DR] = UART01x_DR, [REG_ST_DMAWM] = ST_UART011_DMAWM, @@ -1518,7 +1543,10 @@ static unsigned int pl011_tx_empty(struct uart_port *port) { struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); - unsigned int status = pl011_read(uap, REG_FR); + + /* Allow feature register bits to be inverted to work around errata */ + unsigned int status = pl011_read(uap, REG_FR) ^ uap->vendor->inv_fr; + return status & (uap->vendor->fr_busy | UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT; } @@ -2114,7 +2142,7 @@ static int pl011_verify_port(struct uart_port *port, struct serial_struct *ser) return ret; } -static struct uart_ops amba_pl011_pops = { +static const struct uart_ops amba_pl011_pops = { .tx_empty = pl011_tx_empty, .set_mctrl = pl011_set_mctrl, .get_mctrl = pl011_get_mctrl, @@ -2215,10 +2243,12 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) uart_console_write(&uap->port, s, count, pl011_console_putchar); /* - * Finally, wait for transmitter to become empty - * and restore the TCR + * Finally, wait for transmitter to become empty and restore the + * TCR. Allow feature register bits to be inverted to work around + * errata. */ - while (pl011_read(uap, REG_FR) & uap->vendor->fr_busy) + while ((pl011_read(uap, REG_FR) ^ uap->vendor->inv_fr) + & uap->vendor->fr_busy) cpu_relax(); if (!uap->vendor->always_enabled) pl011_write(old_cr, uap, REG_CR); @@ -2340,8 +2370,12 @@ static int __init pl011_console_match(struct console *co, char *name, int idx, resource_size_t addr; int i; - if (strcmp(name, "pl011") != 0) + if (strcmp(name, "qdf2400_e44") == 0) { + pr_info_once("UART: Working around QDF2400 SoC erratum 44"); + qdf2400_e44_present = true; + } else if (strcmp(name, "pl011") != 0 || strcmp(name, "ttyAMA") != 0) { return -ENODEV; + } if (uart_parse_earlycon(options, &iotype, &addr, &options)) return -ENODEV; @@ -2376,13 +2410,29 @@ static struct console amba_console = { .device = uart_console_device, .setup = pl011_console_setup, .match = pl011_console_match, - .flags = CON_PRINTBUFFER, + .flags = CON_PRINTBUFFER | CON_ANYTIME, .index = -1, .data = &amba_reg, }; #define AMBA_CONSOLE (&amba_console) +static void qdf2400_e44_putc(struct uart_port *port, int c) +{ + while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF) + cpu_relax(); + writel(c, port->membase + UART01x_DR); + while (!(readl(port->membase + UART01x_FR) & UART011_FR_TXFE)) + cpu_relax(); +} + +static void qdf2400_e44_early_write(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, qdf2400_e44_putc); +} + static void pl011_putc(struct uart_port *port, int c) { while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF) @@ -2408,7 +2458,8 @@ static int __init pl011_early_console_setup(struct earlycon_device *device, if (!device->port.membase) return -ENODEV; - device->con->write = pl011_early_write; + device->con->write = qdf2400_e44_present ? + qdf2400_e44_early_write : pl011_early_write; return 0; } OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup); @@ -2645,7 +2696,8 @@ static int sbsa_uart_probe(struct platform_device *pdev) uap->port.irq = ret; uap->reg_offset = vendor_sbsa.reg_offset; - uap->vendor = &vendor_sbsa; + uap->vendor = qdf2400_e44_present ? + &vendor_qdt_qdf2400_e44 : &vendor_sbsa; uap->fifosize = 32; uap->port.iotype = vendor_sbsa.access_32b ? UPIO_MEM32 : UPIO_MEM; uap->port.ops = &sbsa_uart_pops; diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index 73137f4aac20..decc7f3c1ab2 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -493,7 +493,7 @@ static int ar933x_uart_verify_port(struct uart_port *port, return 0; } -static struct uart_ops ar933x_uart_ops = { +static const struct uart_ops ar933x_uart_ops = { .tx_empty = ar933x_uart_tx_empty, .set_mctrl = ar933x_uart_set_mctrl, .get_mctrl = ar933x_uart_get_mctrl, diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index fabbe76203bb..dcebb28ffbc4 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -175,6 +175,17 @@ struct atmel_uart_port { unsigned int pending_status; spinlock_t lock_suspended; + struct { + u32 cr; + u32 mr; + u32 imr; + u32 brgr; + u32 rtor; + u32 ttgr; + u32 fmr; + u32 fimr; + } cache; + int (*prepare_rx)(struct uart_port *port); int (*prepare_tx)(struct uart_port *port); void (*schedule_rx)(struct uart_port *port); @@ -1758,7 +1769,9 @@ static void atmel_get_ip_name(struct uart_port *port) /* * Only USART devices from at91sam9260 SOC implement fractional - * baudrate. + * baudrate. It is available for all asynchronous modes, with the + * following restriction: the sampling clock's duty cycle is not + * constant. */ atmel_port->has_frac_baudrate = false; atmel_port->has_hw_timer = false; @@ -2202,8 +2215,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, * then * 8 CD + FP = selected clock / (2 * baudrate) */ - if (atmel_port->has_frac_baudrate && - (mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_NORMAL) { + if (atmel_port->has_frac_baudrate) { div = DIV_ROUND_CLOSEST(port->uartclk, baud * 2); cd = div >> 3; fp = div & ATMEL_US_FP_MASK; @@ -2659,6 +2671,20 @@ static int atmel_serial_suspend(struct platform_device *pdev, cpu_relax(); } + if (atmel_is_console_port(port) && !console_suspend_enabled) { + /* Cache register values as we won't get a full shutdown/startup + * cycle + */ + atmel_port->cache.mr = atmel_uart_readl(port, ATMEL_US_MR); + atmel_port->cache.imr = atmel_uart_readl(port, ATMEL_US_IMR); + atmel_port->cache.brgr = atmel_uart_readl(port, ATMEL_US_BRGR); + atmel_port->cache.rtor = atmel_uart_readl(port, + atmel_port->rtor); + atmel_port->cache.ttgr = atmel_uart_readl(port, ATMEL_US_TTGR); + atmel_port->cache.fmr = atmel_uart_readl(port, ATMEL_US_FMR); + atmel_port->cache.fimr = atmel_uart_readl(port, ATMEL_US_FIMR); + } + /* we can not wake up if we're running on slow clock */ atmel_port->may_wakeup = device_may_wakeup(&pdev->dev); if (atmel_serial_clk_will_stop()) { @@ -2681,6 +2707,25 @@ static int atmel_serial_resume(struct platform_device *pdev) struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsigned long flags; + if (atmel_is_console_port(port) && !console_suspend_enabled) { + atmel_uart_writel(port, ATMEL_US_MR, atmel_port->cache.mr); + atmel_uart_writel(port, ATMEL_US_IER, atmel_port->cache.imr); + atmel_uart_writel(port, ATMEL_US_BRGR, atmel_port->cache.brgr); + atmel_uart_writel(port, atmel_port->rtor, + atmel_port->cache.rtor); + atmel_uart_writel(port, ATMEL_US_TTGR, atmel_port->cache.ttgr); + + if (atmel_port->fifo_size) { + atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_FIFOEN | + ATMEL_US_RXFCLR | ATMEL_US_TXFLCLR); + atmel_uart_writel(port, ATMEL_US_FMR, + atmel_port->cache.fmr); + atmel_uart_writel(port, ATMEL_US_FIER, + atmel_port->cache.fimr); + } + atmel_start_rx(port); + } + spin_lock_irqsave(&atmel_port->lock_suspended, flags); if (atmel_port->pending) { atmel_handle_receive(port, atmel_port->pending); diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c index 984e1c050096..6b03fb12cd19 100644 --- a/drivers/tty/serial/bfin_sport_uart.c +++ b/drivers/tty/serial/bfin_sport_uart.c @@ -740,7 +740,7 @@ static int sport_uart_resume(struct device *dev) return 0; } -static struct dev_pm_ops bfin_sport_uart_dev_pm_ops = { +static const struct dev_pm_ops bfin_sport_uart_dev_pm_ops = { .suspend = sport_uart_suspend, .resume = sport_uart_resume, }; diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index d3e3d42c0c12..f6bcc19c99d5 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -1302,7 +1302,7 @@ static int __init cpm_uart_console_setup(struct console *co, char *options) struct uart_cpm_port *pinfo; struct uart_port *port; - struct device_node *np = NULL; + struct device_node *np; int i = 0; if (co->index >= UART_NR) { @@ -1311,17 +1311,19 @@ static int __init cpm_uart_console_setup(struct console *co, char *options) return -ENODEV; } - do { - np = of_find_node_by_type(np, "serial"); - if (!np) - return -ENODEV; - + for_each_node_by_type(np, "serial") { if (!of_device_is_compatible(np, "fsl,cpm1-smc-uart") && !of_device_is_compatible(np, "fsl,cpm1-scc-uart") && !of_device_is_compatible(np, "fsl,cpm2-smc-uart") && !of_device_is_compatible(np, "fsl,cpm2-scc-uart")) - i--; - } while (i++ != co->index); + continue; + + if (i++ == co->index) + break; + } + + if (!np) + return -ENODEV; pinfo = &cpm_uart_ports[co->index]; diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index c121f16a973f..ff465ff43577 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -739,7 +739,7 @@ static int dz_verify_port(struct uart_port *uport, struct serial_struct *ser) return ret; } -static struct uart_ops dz_ops = { +static const struct uart_ops dz_ops = { .tx_empty = dz_tx_empty, .get_mctrl = dz_get_mctrl, .set_mctrl = dz_set_mctrl, diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c index 195acc868763..ebd8569f9ad5 100644 --- a/drivers/tty/serial/efm32-uart.c +++ b/drivers/tty/serial/efm32-uart.c @@ -487,7 +487,7 @@ static int efm32_uart_verify_port(struct uart_port *port, return ret; } -static struct uart_ops efm32_uart_pops = { +static const struct uart_ops efm32_uart_pops = { .tx_empty = efm32_uart_tx_empty, .set_mctrl = efm32_uart_set_mctrl, .get_mctrl = efm32_uart_get_mctrl, diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index a1c6519837a4..f02934ffb329 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1407,6 +1407,18 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, /* ask the core to calculate the divisor */ baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); + /* + * Need to update the Ring buffer length according to the selected + * baud rate and restart Rx DMA path. + * + * Since timer function acqures sport->port.lock, need to stop before + * acquring same lock because otherwise del_timer_sync() can deadlock. + */ + if (old && sport->lpuart_dma_rx_use) { + del_timer_sync(&sport->lpuart_timer); + lpuart_dma_rx_free(&sport->port); + } + spin_lock_irqsave(&sport->port.lock, flags); sport->port.read_status_mask = 0; @@ -1456,22 +1468,11 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, /* restore control register */ writeb(old_cr2, sport->port.membase + UARTCR2); - /* - * If new baud rate is set, we will also need to update the Ring buffer - * length according to the selected baud rate and restart Rx DMA path. - */ - if (old) { - if (sport->lpuart_dma_rx_use) { - del_timer_sync(&sport->lpuart_timer); - lpuart_dma_rx_free(&sport->port); - } - - if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) { - sport->lpuart_dma_rx_use = true; + if (old && sport->lpuart_dma_rx_use) { + if (!lpuart_start_rx_dma(sport)) rx_dma_timer_init(sport); - } else { + else sport->lpuart_dma_rx_use = false; - } } spin_unlock_irqrestore(&sport->port.lock, flags); @@ -2131,12 +2132,10 @@ static int lpuart_resume(struct device *dev) if (sport->lpuart_dma_rx_use) { if (sport->port.irq_wake) { - if (!lpuart_start_rx_dma(sport)) { - sport->lpuart_dma_rx_use = true; + if (!lpuart_start_rx_dma(sport)) rx_dma_timer_init(sport); - } else { + else sport->lpuart_dma_rx_use = false; - } } } diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index d83783cfbade..fe92d74f4ea5 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -1286,7 +1286,7 @@ static void icom_config_port(struct uart_port *port, int flags) port->type = PORT_ICOM; } -static struct uart_ops icom_ops = { +static const struct uart_ops icom_ops = { .tx_empty = icom_tx_empty, .set_mctrl = icom_set_mctrl, .get_mctrl = icom_get_mctrl, diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index a70356dad1b7..e3e152cbc75e 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -205,6 +205,7 @@ struct imx_port { struct timer_list timer; unsigned int old_status; unsigned int have_rtscts:1; + unsigned int have_rtsgpio:1; unsigned int dte_mode:1; unsigned int irda_inv_rx:1; unsigned int irda_inv_tx:1; @@ -335,15 +336,15 @@ static void imx_port_ucrs_restore(struct uart_port *port, static void imx_port_rts_active(struct imx_port *sport, unsigned long *ucr2) { - *ucr2 &= ~UCR2_CTSC; - *ucr2 |= UCR2_CTS; + *ucr2 &= ~(UCR2_CTSC | UCR2_CTS); mctrl_gpio_set(sport->gpios, sport->port.mctrl | TIOCM_RTS); } static void imx_port_rts_inactive(struct imx_port *sport, unsigned long *ucr2) { - *ucr2 &= ~(UCR2_CTSC | UCR2_CTS); + *ucr2 &= ~UCR2_CTSC; + *ucr2 |= UCR2_CTS; mctrl_gpio_set(sport->gpios, sport->port.mctrl & ~TIOCM_RTS); } @@ -376,9 +377,9 @@ static void imx_stop_tx(struct uart_port *port) readl(port->membase + USR2) & USR2_TXDC) { temp = readl(port->membase + UCR2); if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) - imx_port_rts_inactive(sport, &temp); - else imx_port_rts_active(sport, &temp); + else + imx_port_rts_inactive(sport, &temp); temp |= UCR2_RXEN; writel(temp, port->membase + UCR2); @@ -584,9 +585,9 @@ static void imx_start_tx(struct uart_port *port) if (port->rs485.flags & SER_RS485_ENABLED) { temp = readl(port->membase + UCR2); if (port->rs485.flags & SER_RS485_RTS_ON_SEND) - imx_port_rts_inactive(sport, &temp); - else imx_port_rts_active(sport, &temp); + else + imx_port_rts_inactive(sport, &temp); if (!(port->rs485.flags & SER_RS485_RX_DURING_TX)) temp &= ~UCR2_RXEN; writel(temp, port->membase + UCR2); @@ -1476,9 +1477,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, */ if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) - imx_port_rts_inactive(sport, &ucr2); - else imx_port_rts_active(sport, &ucr2); + else + imx_port_rts_inactive(sport, &ucr2); } else { imx_port_rts_auto(sport, &ucr2); } @@ -1488,9 +1489,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, } else if (port->rs485.flags & SER_RS485_ENABLED) { /* disable transmitter */ if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) - imx_port_rts_inactive(sport, &ucr2); - else imx_port_rts_active(sport, &ucr2); + else + imx_port_rts_inactive(sport, &ucr2); } @@ -1725,16 +1726,16 @@ static int imx_rs485_config(struct uart_port *port, rs485conf->delay_rts_after_send = 0; /* RTS is required to control the transmitter */ - if (!sport->have_rtscts) + if (!sport->have_rtscts && !sport->have_rtsgpio) rs485conf->flags &= ~SER_RS485_ENABLED; if (rs485conf->flags & SER_RS485_ENABLED) { /* disable transmitter */ temp = readl(sport->port.membase + UCR2); if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND) - imx_port_rts_inactive(sport, &temp); - else imx_port_rts_active(sport, &temp); + else + imx_port_rts_inactive(sport, &temp); writel(temp, sport->port.membase + UCR2); } @@ -2048,6 +2049,9 @@ static int serial_imx_probe_dt(struct imx_port *sport, if (of_get_property(np, "fsl,dte-mode", NULL)) sport->dte_mode = 1; + if (of_get_property(np, "rts-gpios", NULL)) + sport->have_rtsgpio = 1; + return 0; } #else diff --git a/drivers/tty/serial/ioc3_serial.c b/drivers/tty/serial/ioc3_serial.c index 27b5fefac171..2a61dd6b4009 100644 --- a/drivers/tty/serial/ioc3_serial.c +++ b/drivers/tty/serial/ioc3_serial.c @@ -1873,7 +1873,7 @@ static int ic3_request_port(struct uart_port *port) } /* Associate the uart functions above - given to serial core */ -static struct uart_ops ioc3_ops = { +static const struct uart_ops ioc3_ops = { .tx_empty = ic3_tx_empty, .set_mctrl = ic3_set_mctrl, .get_mctrl = ic3_get_mctrl, diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c index 3be051abb2a2..6ad26f802b51 100644 --- a/drivers/tty/serial/ioc4_serial.c +++ b/drivers/tty/serial/ioc4_serial.c @@ -2596,7 +2596,7 @@ static int ic4_request_port(struct uart_port *port) /* Associate the uart functions above - given to serial core */ -static struct uart_ops ioc4_ops = { +static const struct uart_ops ioc4_ops = { .tx_empty = ic4_tx_empty, .set_mctrl = ic4_set_mctrl, .get_mctrl = ic4_get_mctrl, diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c index 991e6dce916e..7ddddb4c3844 100644 --- a/drivers/tty/serial/ip22zilog.c +++ b/drivers/tty/serial/ip22zilog.c @@ -930,7 +930,7 @@ static int ip22zilog_verify_port(struct uart_port *port, struct serial_struct *s return -EINVAL; } -static struct uart_ops ip22zilog_pops = { +static const struct uart_ops ip22zilog_pops = { .tx_empty = ip22zilog_tx_empty, .set_mctrl = ip22zilog_set_mctrl, .get_mctrl = ip22zilog_get_mctrl, diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index b88832e8ee82..22df94f107e5 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -16,7 +16,7 @@ * * Copyright (C) 2004 Infineon IFAP DC COM CPE * Copyright (C) 2007 Felix Fietkau <nbd@openwrt.org> - * Copyright (C) 2007 John Crispin <blogic@openwrt.org> + * Copyright (C) 2007 John Crispin <john@phrozen.org> * Copyright (C) 2010 Thomas Langer, <thomas.langer@lantiq.com> */ @@ -557,7 +557,7 @@ lqasc_verify_port(struct uart_port *port, return ret; } -static struct uart_ops lqasc_pops = { +static const struct uart_ops lqasc_pops = { .tx_empty = lqasc_tx_empty, .set_mctrl = lqasc_set_mctrl, .get_mctrl = lqasc_get_mctrl, @@ -590,13 +590,20 @@ lqasc_console_putchar(struct uart_port *port, int ch) ltq_w8(ch, port->membase + LTQ_ASC_TBUF); } +static void lqasc_serial_port_write(struct uart_port *port, const char *s, + u_int count) +{ + unsigned long flags; + + spin_lock_irqsave(<q_asc_lock, flags); + uart_console_write(port, s, count, lqasc_console_putchar); + spin_unlock_irqrestore(<q_asc_lock, flags); +} static void lqasc_console_write(struct console *co, const char *s, u_int count) { struct ltq_uart_port *ltq_port; - struct uart_port *port; - unsigned long flags; if (co->index >= MAXPORTS) return; @@ -605,11 +612,7 @@ lqasc_console_write(struct console *co, const char *s, u_int count) if (!ltq_port) return; - port = <q_port->port; - - spin_lock_irqsave(<q_asc_lock, flags); - uart_console_write(port, s, count, lqasc_console_putchar); - spin_unlock_irqrestore(<q_asc_lock, flags); + lqasc_serial_port_write(<q_port->port, s, count); } static int __init @@ -659,6 +662,27 @@ lqasc_console_init(void) } console_initcall(lqasc_console_init); +static void lqasc_serial_early_console_write(struct console *co, + const char *s, + u_int count) +{ + struct earlycon_device *dev = co->data; + + lqasc_serial_port_write(&dev->port, s, count); +} + +static int __init +lqasc_serial_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = lqasc_serial_early_console_write; + return 0; +} +OF_EARLYCON_DECLARE(lantiq, DRVNAME, lqasc_serial_early_console_setup); + static struct uart_driver lqasc_reg = { .owner = THIS_MODULE, .driver_name = DRVNAME, diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index 7eb04ae71cc8..cea57ff32c33 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -645,7 +645,7 @@ static int serial_lpc32xx_verify_port(struct uart_port *port, return ret; } -static struct uart_ops serial_lpc32xx_pops = { +static const struct uart_ops serial_lpc32xx_pops = { .tx_empty = serial_lpc32xx_tx_empty, .set_mctrl = serial_lpc32xx_set_mctrl, .get_mctrl = serial_lpc32xx_get_mctrl, diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 8a3e92638e10..9dfedbe6c071 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -236,7 +236,7 @@ /* Misc definitions */ #define MAX310X_FIFO_SIZE (128) -#define MAX310x_REV_MASK (0xfc) +#define MAX310x_REV_MASK (0xf8) /* MAX3107 specific */ #define MAX3107_REV_ID (0xa0) diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 6aea0f4a9165..60f16795d16b 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -364,7 +364,7 @@ static void meson_uart_set_termios(struct uart_port *port, writel(val, port->membase + AML_UART_CONTROL); - baud = uart_get_baud_rate(port, termios, old, 9600, 115200); + baud = uart_get_baud_rate(port, termios, old, 9600, 4000000); meson_uart_change_speed(port, baud); port->read_status_mask = AML_UART_TX_FIFO_WERR; diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c index 4a3021bcc859..1a60a2063e75 100644 --- a/drivers/tty/serial/mpsc.c +++ b/drivers/tty/serial/mpsc.c @@ -1670,7 +1670,7 @@ static void mpsc_put_poll_char(struct uart_port *port, } #endif -static struct uart_ops mpsc_pops = { +static const struct uart_ops mpsc_pops = { .tx_empty = mpsc_tx_empty, .set_mctrl = mpsc_set_mctrl, .get_mctrl = mpsc_get_mctrl, diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 7312e7e01b7e..6788e7532dff 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1809,6 +1809,7 @@ static const struct of_device_id msm_match_table[] = { { .compatible = "qcom,msm-uartdm" }, {} }; +MODULE_DEVICE_TABLE(of, msm_match_table); static struct platform_driver msm_platform_driver = { .remove = msm_serial_remove, diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 8c1c9112b3fd..6989b227d134 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -95,6 +95,7 @@ #define AUART_LINECTRL_BAUD_DIVFRAC_SHIFT 8 #define AUART_LINECTRL_BAUD_DIVFRAC_MASK 0x00003f00 #define AUART_LINECTRL_BAUD_DIVFRAC(v) (((v) & 0x3f) << 8) +#define AUART_LINECTRL_SPS (1 << 7) #define AUART_LINECTRL_WLEN_MASK 0x00000060 #define AUART_LINECTRL_WLEN(v) (((v) & 0x3) << 5) #define AUART_LINECTRL_FEN (1 << 4) @@ -1014,6 +1015,8 @@ static void mxs_auart_settermios(struct uart_port *u, ctrl |= AUART_LINECTRL_PEN; if ((cflag & PARODD) == 0) ctrl |= AUART_LINECTRL_EPS; + if (cflag & CMSPAR) + ctrl |= AUART_LINECTRL_SPS; } u->read_status_mask = AUART_STAT_OERR; diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index a2a529994ba5..6c6f82ad8d5c 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1234,6 +1234,61 @@ out: #ifdef CONFIG_SERIAL_OMAP_CONSOLE +#ifdef CONFIG_SERIAL_EARLYCON +static unsigned int __init omap_serial_early_in(struct uart_port *port, + int offset) +{ + offset <<= port->regshift; + return readw(port->membase + offset); +} + +static void __init omap_serial_early_out(struct uart_port *port, int offset, + int value) +{ + offset <<= port->regshift; + writew(value, port->membase + offset); +} + +static void __init omap_serial_early_putc(struct uart_port *port, int c) +{ + unsigned int status; + + for (;;) { + status = omap_serial_early_in(port, UART_LSR); + if ((status & BOTH_EMPTY) == BOTH_EMPTY) + break; + cpu_relax(); + } + omap_serial_early_out(port, UART_TX, c); +} + +static void __init early_omap_serial_write(struct console *console, + const char *s, unsigned int count) +{ + struct earlycon_device *device = console->data; + struct uart_port *port = &device->port; + + uart_console_write(port, s, count, omap_serial_early_putc); +} + +static int __init early_omap_serial_setup(struct earlycon_device *device, + const char *options) +{ + struct uart_port *port = &device->port; + + if (!(device->port.membase || device->port.iobase)) + return -ENODEV; + + port->regshift = 2; + device->con->write = early_omap_serial_write; + return 0; +} + +OF_EARLYCON_DECLARE(omapserial, "ti,omap2-uart", early_omap_serial_setup); +OF_EARLYCON_DECLARE(omapserial, "ti,omap3-uart", early_omap_serial_setup); +OF_EARLYCON_DECLARE(omapserial, "ti,omap4-uart", early_omap_serial_setup); +#endif /* CONFIG_SERIAL_EARLYCON */ + static struct uart_omap_port *serial_omap_console_ports[OMAP_MAX_HSUART_PORTS]; static struct uart_driver serial_omap_reg; @@ -1395,7 +1450,7 @@ serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) return 0; } -static struct uart_ops serial_omap_pops = { +static const struct uart_ops serial_omap_pops = { .tx_empty = serial_omap_tx_empty, .set_mctrl = serial_omap_set_mctrl, .get_mctrl = serial_omap_get_mctrl, diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c index 7f8e99bbcb73..00a33eb859d3 100644 --- a/drivers/tty/serial/pic32_uart.c +++ b/drivers/tty/serial/pic32_uart.c @@ -495,13 +495,13 @@ static int pic32_uart_startup(struct uart_port *port) out_t: kfree(sport->irq_tx_name); - free_irq(sport->irq_tx, sport); + free_irq(sport->irq_tx, port); out_r: kfree(sport->irq_rx_name); - free_irq(sport->irq_rx, sport); + free_irq(sport->irq_rx, port); out_f: kfree(sport->irq_fault_name); - free_irq(sport->irq_fault, sport); + free_irq(sport->irq_fault, port); out_done: return ret; } diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index b24b0556f5a8..0da52947e59e 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -1379,7 +1379,7 @@ static void pmz_poll_put_char(struct uart_port *port, unsigned char c) #endif /* CONFIG_CONSOLE_POLL */ -static struct uart_ops pmz_pops = { +static const struct uart_ops pmz_pops = { .tx_empty = pmz_tx_empty, .set_mctrl = pmz_set_mctrl, .get_mctrl = pmz_get_mctrl, diff --git a/drivers/tty/serial/pnx8xxx_uart.c b/drivers/tty/serial/pnx8xxx_uart.c index 7a3bb9cf1f2e..dab2668d3879 100644 --- a/drivers/tty/serial/pnx8xxx_uart.c +++ b/drivers/tty/serial/pnx8xxx_uart.c @@ -631,7 +631,7 @@ pnx8xxx_verify_port(struct uart_port *port, struct serial_struct *ser) return ret; } -static struct uart_ops pnx8xxx_pops = { +static const struct uart_ops pnx8xxx_pops = { .tx_empty = pnx8xxx_tx_empty, .set_mctrl = pnx8xxx_set_mctrl, .get_mctrl = pnx8xxx_get_mctrl, diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 75952811c0da..905631df1f8b 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -762,7 +762,7 @@ static struct console serial_pxa_console = { #define PXA_CONSOLE NULL #endif -static struct uart_ops serial_pxa_pops = { +static const struct uart_ops serial_pxa_pops = { .tx_empty = serial_pxa_tx_empty, .set_mctrl = serial_pxa_set_mctrl, .get_mctrl = serial_pxa_get_mctrl, diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index f44615fa474d..b4f86c219db1 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -859,7 +859,6 @@ static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state) static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) { struct s3c24xx_uart_dma *dma = p->dma; - dma_cap_mask_t mask; unsigned long flags; /* Default slave configuration parameters */ @@ -876,21 +875,17 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) else dma->tx_conf.dst_maxburst = 1; - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); + dma->rx_chan = dma_request_chan(p->port.dev, "rx"); - dma->rx_chan = dma_request_slave_channel_compat(mask, dma->fn, - dma->rx_param, p->port.dev, "rx"); - if (!dma->rx_chan) - return -ENODEV; + if (IS_ERR(dma->rx_chan)) + return PTR_ERR(dma->rx_chan); dmaengine_slave_config(dma->rx_chan, &dma->rx_conf); - dma->tx_chan = dma_request_slave_channel_compat(mask, dma->fn, - dma->tx_param, p->port.dev, "tx"); - if (!dma->tx_chan) { + dma->tx_chan = dma_request_chan(p->port.dev, "tx"); + if (IS_ERR(dma->tx_chan)) { dma_release_channel(dma->rx_chan); - return -ENODEV; + return PTR_ERR(dma->tx_chan); } dmaengine_slave_config(dma->tx_chan, &dma->tx_conf); @@ -1921,6 +1916,7 @@ static int s3c24xx_serial_resume(struct device *dev) static int s3c24xx_serial_resume_noirq(struct device *dev) { struct uart_port *port = s3c24xx_dev_to_port(dev); + struct s3c24xx_uart_port *ourport = to_ourport(port); if (port) { /* restore IRQ mask */ @@ -1930,7 +1926,9 @@ static int s3c24xx_serial_resume_noirq(struct device *dev) uintm &= ~S3C64XX_UINTM_TXD_MSK; if (rx_enabled(port)) uintm &= ~S3C64XX_UINTM_RXD_MSK; + clk_prepare_enable(ourport->clk); wr_regl(port, S3C64XX_UINTM, uintm); + clk_disable_unprepare(ourport->clk); } } diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h index a04acef1cb20..965199b6c16f 100644 --- a/drivers/tty/serial/samsung.h +++ b/drivers/tty/serial/samsung.h @@ -44,10 +44,6 @@ struct s3c24xx_serial_drv_data { }; struct s3c24xx_uart_dma { - dma_filter_fn fn; - void *rx_param; - void *tx_param; - unsigned int rx_chan_id; unsigned int tx_chan_id; diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 731ac35acb31..d92a150c8733 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -1191,7 +1191,7 @@ static const char *tegra_uart_type(struct uart_port *u) return TEGRA_UART_TYPE; } -static struct uart_ops tegra_uart_ops = { +static const struct uart_ops tegra_uart_ops = { .tx_empty = tegra_uart_tx_empty, .set_mctrl = tegra_uart_set_mctrl, .get_mctrl = tegra_uart_get_mctrl, diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index f80312eed4fd..f80fead6c5fc 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -845,7 +845,7 @@ serial_txx9_type(struct uart_port *port) return "txx9"; } -static struct uart_ops serial_txx9_pops = { +static const struct uart_ops serial_txx9_pops = { .tx_empty = serial_txx9_tx_empty, .set_mctrl = serial_txx9_set_mctrl, .get_mctrl = serial_txx9_get_mctrl, diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 91e7dddbf72c..9a47cc4f16a2 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -101,23 +101,30 @@ enum SCI_CLKS { for ((_sr) = max_sr(_port); (_sr) >= min_sr(_port); (_sr)--) \ if ((_port)->sampling_rate_mask & SCI_SR((_sr))) +struct plat_sci_reg { + u8 offset, size; +}; + +struct sci_port_params { + const struct plat_sci_reg regs[SCIx_NR_REGS]; + unsigned int fifosize; + unsigned int overrun_reg; + unsigned int overrun_mask; + unsigned int sampling_rate_mask; + unsigned int error_mask; + unsigned int error_clear; +}; + struct sci_port { struct uart_port port; /* Platform configuration */ - struct plat_sci_port *cfg; - unsigned int overrun_reg; - unsigned int overrun_mask; - unsigned int error_mask; - unsigned int error_clear; + const struct sci_port_params *params; + const struct plat_sci_port *cfg; unsigned int sampling_rate_mask; resource_size_t reg_size; struct mctrl_gpios *gpios; - /* Break timer */ - struct timer_list break_timer; - int break_flag; - /* Clocks */ struct clk *clks[SCI_NUM_CLKS]; unsigned long clk_rates[SCI_NUM_CLKS]; @@ -141,7 +148,12 @@ struct sci_port { struct timer_list rx_timer; unsigned int rx_timeout; #endif + unsigned int rx_frame; + int rx_trigger; + struct timer_list rx_fifo_timer; + int rx_fifo_timeout; + bool has_rtscts; bool autorts; }; @@ -156,110 +168,97 @@ to_sci_port(struct uart_port *uart) return container_of(uart, struct sci_port, port); } -struct plat_sci_reg { - u8 offset, size; -}; - -/* Helper for invalidating specific entries of an inherited map. */ -#define sci_reg_invalid { .offset = 0, .size = 0 } - -static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { - [SCIx_PROBE_REGTYPE] = { - [0 ... SCIx_NR_REGS - 1] = sci_reg_invalid, - }, - +static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { /* * Common SCI definitions, dependent on the port's regshift * value. */ [SCIx_SCI_REGTYPE] = { - [SCSMR] = { 0x00, 8 }, - [SCBRR] = { 0x01, 8 }, - [SCSCR] = { 0x02, 8 }, - [SCxTDR] = { 0x03, 8 }, - [SCxSR] = { 0x04, 8 }, - [SCxRDR] = { 0x05, 8 }, - [SCFCR] = sci_reg_invalid, - [SCFDR] = sci_reg_invalid, - [SCTFDR] = sci_reg_invalid, - [SCRFDR] = sci_reg_invalid, - [SCSPTR] = sci_reg_invalid, - [SCLSR] = sci_reg_invalid, - [HSSRR] = sci_reg_invalid, - [SCPCR] = sci_reg_invalid, - [SCPDR] = sci_reg_invalid, - [SCDL] = sci_reg_invalid, - [SCCKS] = sci_reg_invalid, + .regs = { + [SCSMR] = { 0x00, 8 }, + [SCBRR] = { 0x01, 8 }, + [SCSCR] = { 0x02, 8 }, + [SCxTDR] = { 0x03, 8 }, + [SCxSR] = { 0x04, 8 }, + [SCxRDR] = { 0x05, 8 }, + }, + .fifosize = 1, + .overrun_reg = SCxSR, + .overrun_mask = SCI_ORER, + .sampling_rate_mask = SCI_SR(32), + .error_mask = SCI_DEFAULT_ERROR_MASK | SCI_ORER, + .error_clear = SCI_ERROR_CLEAR & ~SCI_ORER, }, /* - * Common definitions for legacy IrDA ports, dependent on - * regshift value. + * Common definitions for legacy IrDA ports. */ [SCIx_IRDA_REGTYPE] = { - [SCSMR] = { 0x00, 8 }, - [SCBRR] = { 0x01, 8 }, - [SCSCR] = { 0x02, 8 }, - [SCxTDR] = { 0x03, 8 }, - [SCxSR] = { 0x04, 8 }, - [SCxRDR] = { 0x05, 8 }, - [SCFCR] = { 0x06, 8 }, - [SCFDR] = { 0x07, 16 }, - [SCTFDR] = sci_reg_invalid, - [SCRFDR] = sci_reg_invalid, - [SCSPTR] = sci_reg_invalid, - [SCLSR] = sci_reg_invalid, - [HSSRR] = sci_reg_invalid, - [SCPCR] = sci_reg_invalid, - [SCPDR] = sci_reg_invalid, - [SCDL] = sci_reg_invalid, - [SCCKS] = sci_reg_invalid, + .regs = { + [SCSMR] = { 0x00, 8 }, + [SCBRR] = { 0x02, 8 }, + [SCSCR] = { 0x04, 8 }, + [SCxTDR] = { 0x06, 8 }, + [SCxSR] = { 0x08, 16 }, + [SCxRDR] = { 0x0a, 8 }, + [SCFCR] = { 0x0c, 8 }, + [SCFDR] = { 0x0e, 16 }, + }, + .fifosize = 1, + .overrun_reg = SCxSR, + .overrun_mask = SCI_ORER, + .sampling_rate_mask = SCI_SR(32), + .error_mask = SCI_DEFAULT_ERROR_MASK | SCI_ORER, + .error_clear = SCI_ERROR_CLEAR & ~SCI_ORER, }, /* * Common SCIFA definitions. */ [SCIx_SCIFA_REGTYPE] = { - [SCSMR] = { 0x00, 16 }, - [SCBRR] = { 0x04, 8 }, - [SCSCR] = { 0x08, 16 }, - [SCxTDR] = { 0x20, 8 }, - [SCxSR] = { 0x14, 16 }, - [SCxRDR] = { 0x24, 8 }, - [SCFCR] = { 0x18, 16 }, - [SCFDR] = { 0x1c, 16 }, - [SCTFDR] = sci_reg_invalid, - [SCRFDR] = sci_reg_invalid, - [SCSPTR] = sci_reg_invalid, - [SCLSR] = sci_reg_invalid, - [HSSRR] = sci_reg_invalid, - [SCPCR] = { 0x30, 16 }, - [SCPDR] = { 0x34, 16 }, - [SCDL] = sci_reg_invalid, - [SCCKS] = sci_reg_invalid, + .regs = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x20, 8 }, + [SCxSR] = { 0x14, 16 }, + [SCxRDR] = { 0x24, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCPCR] = { 0x30, 16 }, + [SCPDR] = { 0x34, 16 }, + }, + .fifosize = 64, + .overrun_reg = SCxSR, + .overrun_mask = SCIFA_ORER, + .sampling_rate_mask = SCI_SR_SCIFAB, + .error_mask = SCIF_DEFAULT_ERROR_MASK | SCIFA_ORER, + .error_clear = SCIF_ERROR_CLEAR & ~SCIFA_ORER, }, /* * Common SCIFB definitions. */ [SCIx_SCIFB_REGTYPE] = { - [SCSMR] = { 0x00, 16 }, - [SCBRR] = { 0x04, 8 }, - [SCSCR] = { 0x08, 16 }, - [SCxTDR] = { 0x40, 8 }, - [SCxSR] = { 0x14, 16 }, - [SCxRDR] = { 0x60, 8 }, - [SCFCR] = { 0x18, 16 }, - [SCFDR] = sci_reg_invalid, - [SCTFDR] = { 0x38, 16 }, - [SCRFDR] = { 0x3c, 16 }, - [SCSPTR] = sci_reg_invalid, - [SCLSR] = sci_reg_invalid, - [HSSRR] = sci_reg_invalid, - [SCPCR] = { 0x30, 16 }, - [SCPDR] = { 0x34, 16 }, - [SCDL] = sci_reg_invalid, - [SCCKS] = sci_reg_invalid, + .regs = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x40, 8 }, + [SCxSR] = { 0x14, 16 }, + [SCxRDR] = { 0x60, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCTFDR] = { 0x38, 16 }, + [SCRFDR] = { 0x3c, 16 }, + [SCPCR] = { 0x30, 16 }, + [SCPDR] = { 0x34, 16 }, + }, + .fifosize = 256, + .overrun_reg = SCxSR, + .overrun_mask = SCIFA_ORER, + .sampling_rate_mask = SCI_SR_SCIFAB, + .error_mask = SCIF_DEFAULT_ERROR_MASK | SCIFA_ORER, + .error_clear = SCIF_ERROR_CLEAR & ~SCIFA_ORER, }, /* @@ -267,69 +266,70 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { * count registers. */ [SCIx_SH2_SCIF_FIFODATA_REGTYPE] = { - [SCSMR] = { 0x00, 16 }, - [SCBRR] = { 0x04, 8 }, - [SCSCR] = { 0x08, 16 }, - [SCxTDR] = { 0x0c, 8 }, - [SCxSR] = { 0x10, 16 }, - [SCxRDR] = { 0x14, 8 }, - [SCFCR] = { 0x18, 16 }, - [SCFDR] = { 0x1c, 16 }, - [SCTFDR] = sci_reg_invalid, - [SCRFDR] = sci_reg_invalid, - [SCSPTR] = { 0x20, 16 }, - [SCLSR] = { 0x24, 16 }, - [HSSRR] = sci_reg_invalid, - [SCPCR] = sci_reg_invalid, - [SCPDR] = sci_reg_invalid, - [SCDL] = sci_reg_invalid, - [SCCKS] = sci_reg_invalid, + .regs = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCSPTR] = { 0x20, 16 }, + [SCLSR] = { 0x24, 16 }, + }, + .fifosize = 16, + .overrun_reg = SCLSR, + .overrun_mask = SCLSR_ORER, + .sampling_rate_mask = SCI_SR(32), + .error_mask = SCIF_DEFAULT_ERROR_MASK, + .error_clear = SCIF_ERROR_CLEAR, }, /* * Common SH-3 SCIF definitions. */ [SCIx_SH3_SCIF_REGTYPE] = { - [SCSMR] = { 0x00, 8 }, - [SCBRR] = { 0x02, 8 }, - [SCSCR] = { 0x04, 8 }, - [SCxTDR] = { 0x06, 8 }, - [SCxSR] = { 0x08, 16 }, - [SCxRDR] = { 0x0a, 8 }, - [SCFCR] = { 0x0c, 8 }, - [SCFDR] = { 0x0e, 16 }, - [SCTFDR] = sci_reg_invalid, - [SCRFDR] = sci_reg_invalid, - [SCSPTR] = sci_reg_invalid, - [SCLSR] = sci_reg_invalid, - [HSSRR] = sci_reg_invalid, - [SCPCR] = sci_reg_invalid, - [SCPDR] = sci_reg_invalid, - [SCDL] = sci_reg_invalid, - [SCCKS] = sci_reg_invalid, + .regs = { + [SCSMR] = { 0x00, 8 }, + [SCBRR] = { 0x02, 8 }, + [SCSCR] = { 0x04, 8 }, + [SCxTDR] = { 0x06, 8 }, + [SCxSR] = { 0x08, 16 }, + [SCxRDR] = { 0x0a, 8 }, + [SCFCR] = { 0x0c, 8 }, + [SCFDR] = { 0x0e, 16 }, + }, + .fifosize = 16, + .overrun_reg = SCLSR, + .overrun_mask = SCLSR_ORER, + .sampling_rate_mask = SCI_SR(32), + .error_mask = SCIF_DEFAULT_ERROR_MASK, + .error_clear = SCIF_ERROR_CLEAR, }, /* * Common SH-4(A) SCIF(B) definitions. */ [SCIx_SH4_SCIF_REGTYPE] = { - [SCSMR] = { 0x00, 16 }, - [SCBRR] = { 0x04, 8 }, - [SCSCR] = { 0x08, 16 }, - [SCxTDR] = { 0x0c, 8 }, - [SCxSR] = { 0x10, 16 }, - [SCxRDR] = { 0x14, 8 }, - [SCFCR] = { 0x18, 16 }, - [SCFDR] = { 0x1c, 16 }, - [SCTFDR] = sci_reg_invalid, - [SCRFDR] = sci_reg_invalid, - [SCSPTR] = { 0x20, 16 }, - [SCLSR] = { 0x24, 16 }, - [HSSRR] = sci_reg_invalid, - [SCPCR] = sci_reg_invalid, - [SCPDR] = sci_reg_invalid, - [SCDL] = sci_reg_invalid, - [SCCKS] = sci_reg_invalid, + .regs = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCSPTR] = { 0x20, 16 }, + [SCLSR] = { 0x24, 16 }, + }, + .fifosize = 16, + .overrun_reg = SCLSR, + .overrun_mask = SCLSR_ORER, + .sampling_rate_mask = SCI_SR(32), + .error_mask = SCIF_DEFAULT_ERROR_MASK, + .error_clear = SCIF_ERROR_CLEAR, }, /* @@ -337,46 +337,55 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { * External Clock (BRG). */ [SCIx_SH4_SCIF_BRG_REGTYPE] = { - [SCSMR] = { 0x00, 16 }, - [SCBRR] = { 0x04, 8 }, - [SCSCR] = { 0x08, 16 }, - [SCxTDR] = { 0x0c, 8 }, - [SCxSR] = { 0x10, 16 }, - [SCxRDR] = { 0x14, 8 }, - [SCFCR] = { 0x18, 16 }, - [SCFDR] = { 0x1c, 16 }, - [SCTFDR] = sci_reg_invalid, - [SCRFDR] = sci_reg_invalid, - [SCSPTR] = { 0x20, 16 }, - [SCLSR] = { 0x24, 16 }, - [HSSRR] = sci_reg_invalid, - [SCPCR] = sci_reg_invalid, - [SCPDR] = sci_reg_invalid, - [SCDL] = { 0x30, 16 }, - [SCCKS] = { 0x34, 16 }, + .regs = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCSPTR] = { 0x20, 16 }, + [SCLSR] = { 0x24, 16 }, + [SCDL] = { 0x30, 16 }, + [SCCKS] = { 0x34, 16 }, + }, + .fifosize = 16, + .overrun_reg = SCLSR, + .overrun_mask = SCLSR_ORER, + .sampling_rate_mask = SCI_SR(32), + .error_mask = SCIF_DEFAULT_ERROR_MASK, + .error_clear = SCIF_ERROR_CLEAR, }, /* * Common HSCIF definitions. */ [SCIx_HSCIF_REGTYPE] = { - [SCSMR] = { 0x00, 16 }, - [SCBRR] = { 0x04, 8 }, - [SCSCR] = { 0x08, 16 }, - [SCxTDR] = { 0x0c, 8 }, - [SCxSR] = { 0x10, 16 }, - [SCxRDR] = { 0x14, 8 }, - [SCFCR] = { 0x18, 16 }, - [SCFDR] = { 0x1c, 16 }, - [SCTFDR] = sci_reg_invalid, - [SCRFDR] = sci_reg_invalid, - [SCSPTR] = { 0x20, 16 }, - [SCLSR] = { 0x24, 16 }, - [HSSRR] = { 0x40, 16 }, - [SCPCR] = sci_reg_invalid, - [SCPDR] = sci_reg_invalid, - [SCDL] = { 0x30, 16 }, - [SCCKS] = { 0x34, 16 }, + .regs = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCSPTR] = { 0x20, 16 }, + [SCLSR] = { 0x24, 16 }, + [HSSRR] = { 0x40, 16 }, + [SCDL] = { 0x30, 16 }, + [SCCKS] = { 0x34, 16 }, + [HSRTRGR] = { 0x54, 16 }, + [HSTTRGR] = { 0x58, 16 }, + }, + .fifosize = 128, + .overrun_reg = SCLSR, + .overrun_mask = SCLSR_ORER, + .sampling_rate_mask = SCI_SR_RANGE(8, 32), + .error_mask = SCIF_DEFAULT_ERROR_MASK, + .error_clear = SCIF_ERROR_CLEAR, }, /* @@ -384,23 +393,23 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { * register. */ [SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE] = { - [SCSMR] = { 0x00, 16 }, - [SCBRR] = { 0x04, 8 }, - [SCSCR] = { 0x08, 16 }, - [SCxTDR] = { 0x0c, 8 }, - [SCxSR] = { 0x10, 16 }, - [SCxRDR] = { 0x14, 8 }, - [SCFCR] = { 0x18, 16 }, - [SCFDR] = { 0x1c, 16 }, - [SCTFDR] = sci_reg_invalid, - [SCRFDR] = sci_reg_invalid, - [SCSPTR] = sci_reg_invalid, - [SCLSR] = { 0x24, 16 }, - [HSSRR] = sci_reg_invalid, - [SCPCR] = sci_reg_invalid, - [SCPDR] = sci_reg_invalid, - [SCDL] = sci_reg_invalid, - [SCCKS] = sci_reg_invalid, + .regs = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCLSR] = { 0x24, 16 }, + }, + .fifosize = 16, + .overrun_reg = SCLSR, + .overrun_mask = SCLSR_ORER, + .sampling_rate_mask = SCI_SR(32), + .error_mask = SCIF_DEFAULT_ERROR_MASK, + .error_clear = SCIF_ERROR_CLEAR, }, /* @@ -408,23 +417,26 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { * count registers. */ [SCIx_SH4_SCIF_FIFODATA_REGTYPE] = { - [SCSMR] = { 0x00, 16 }, - [SCBRR] = { 0x04, 8 }, - [SCSCR] = { 0x08, 16 }, - [SCxTDR] = { 0x0c, 8 }, - [SCxSR] = { 0x10, 16 }, - [SCxRDR] = { 0x14, 8 }, - [SCFCR] = { 0x18, 16 }, - [SCFDR] = { 0x1c, 16 }, - [SCTFDR] = { 0x1c, 16 }, /* aliased to SCFDR */ - [SCRFDR] = { 0x20, 16 }, - [SCSPTR] = { 0x24, 16 }, - [SCLSR] = { 0x28, 16 }, - [HSSRR] = sci_reg_invalid, - [SCPCR] = sci_reg_invalid, - [SCPDR] = sci_reg_invalid, - [SCDL] = sci_reg_invalid, - [SCCKS] = sci_reg_invalid, + .regs = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = { 0x1c, 16 }, /* aliased to SCFDR */ + [SCRFDR] = { 0x20, 16 }, + [SCSPTR] = { 0x24, 16 }, + [SCLSR] = { 0x28, 16 }, + }, + .fifosize = 16, + .overrun_reg = SCLSR, + .overrun_mask = SCLSR_ORER, + .sampling_rate_mask = SCI_SR(32), + .error_mask = SCIF_DEFAULT_ERROR_MASK, + .error_clear = SCIF_ERROR_CLEAR, }, /* @@ -432,27 +444,26 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { * registers. */ [SCIx_SH7705_SCIF_REGTYPE] = { - [SCSMR] = { 0x00, 16 }, - [SCBRR] = { 0x04, 8 }, - [SCSCR] = { 0x08, 16 }, - [SCxTDR] = { 0x20, 8 }, - [SCxSR] = { 0x14, 16 }, - [SCxRDR] = { 0x24, 8 }, - [SCFCR] = { 0x18, 16 }, - [SCFDR] = { 0x1c, 16 }, - [SCTFDR] = sci_reg_invalid, - [SCRFDR] = sci_reg_invalid, - [SCSPTR] = sci_reg_invalid, - [SCLSR] = sci_reg_invalid, - [HSSRR] = sci_reg_invalid, - [SCPCR] = sci_reg_invalid, - [SCPDR] = sci_reg_invalid, - [SCDL] = sci_reg_invalid, - [SCCKS] = sci_reg_invalid, + .regs = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x20, 8 }, + [SCxSR] = { 0x14, 16 }, + [SCxRDR] = { 0x24, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + }, + .fifosize = 64, + .overrun_reg = SCxSR, + .overrun_mask = SCIFA_ORER, + .sampling_rate_mask = SCI_SR(16), + .error_mask = SCIF_DEFAULT_ERROR_MASK | SCIFA_ORER, + .error_clear = SCIF_ERROR_CLEAR & ~SCIFA_ORER, }, }; -#define sci_getreg(up, offset) (sci_regmap[to_sci_port(up)->cfg->regtype] + offset) +#define sci_getreg(up, offset) (&to_sci_port(up)->params->regs[offset]) /* * The "offset" here is rather misleading, in that it refers to an enum @@ -486,41 +497,6 @@ static void sci_serial_out(struct uart_port *p, int offset, int value) WARN(1, "Invalid register access\n"); } -static int sci_probe_regmap(struct plat_sci_port *cfg) -{ - switch (cfg->type) { - case PORT_SCI: - cfg->regtype = SCIx_SCI_REGTYPE; - break; - case PORT_IRDA: - cfg->regtype = SCIx_IRDA_REGTYPE; - break; - case PORT_SCIFA: - cfg->regtype = SCIx_SCIFA_REGTYPE; - break; - case PORT_SCIFB: - cfg->regtype = SCIx_SCIFB_REGTYPE; - break; - case PORT_SCIF: - /* - * The SH-4 is a bit of a misnomer here, although that's - * where this particular port layout originated. This - * configuration (or some slight variation thereof) - * remains the dominant model for all SCIFs. - */ - cfg->regtype = SCIx_SH4_SCIF_REGTYPE; - break; - case PORT_HSCIF: - cfg->regtype = SCIx_HSCIF_REGTYPE; - break; - default: - pr_err("Can't probe register map for given port\n"); - return -EINVAL; - } - - return 0; -} - static void sci_port_enable(struct sci_port *sci_port) { unsigned int i; @@ -544,14 +520,6 @@ static void sci_port_disable(struct sci_port *sci_port) if (!sci_port->port.dev) return; - /* Cancel the break timer to ensure that the timer handler will not try - * to access the hardware with clocks and power disabled. Reset the - * break flag to make the break debouncing state machine ready for the - * next break. - */ - del_timer_sync(&sci_port->break_timer); - sci_port->break_flag = 0; - for (i = SCI_NUM_CLKS; i-- > 0; ) clk_disable_unprepare(sci_port->clks[i]); @@ -646,7 +614,7 @@ static void sci_clear_SCxSR(struct uart_port *port, unsigned int mask) if (port->type == PORT_SCI) { /* Just store the mask */ serial_port_out(port, SCxSR, mask); - } else if (to_sci_port(port)->overrun_mask == SCIFA_ORER) { + } else if (to_sci_port(port)->params->overrun_mask == SCIFA_ORER) { /* SCIFA/SCIFB and SCIF on SH7705/SH7720/SH7721 */ /* Only clear the status bits we want to clear */ serial_port_out(port, SCxSR, @@ -719,7 +687,7 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag) /* Enable RXD and TXD pin functions */ ctrl &= ~(SCPCR_RXDC | SCPCR_TXDC); - if (to_sci_port(port)->cfg->capabilities & SCIx_HAVE_RTSCTS) { + if (to_sci_port(port)->has_rtscts) { /* RTS# is output, driven 1 */ ctrl |= SCPCR_RTSC; serial_port_out(port, SCPDR, @@ -741,11 +709,13 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag) static int sci_txfill(struct uart_port *port) { + struct sci_port *s = to_sci_port(port); + unsigned int fifo_mask = (s->params->fifosize << 1) - 1; const struct plat_sci_reg *reg; reg = sci_getreg(port, SCTFDR); if (reg->size) - return serial_port_in(port, SCTFDR) & ((port->fifosize << 1) - 1); + return serial_port_in(port, SCTFDR) & fifo_mask; reg = sci_getreg(port, SCFDR); if (reg->size) @@ -761,33 +731,21 @@ static int sci_txroom(struct uart_port *port) static int sci_rxfill(struct uart_port *port) { + struct sci_port *s = to_sci_port(port); + unsigned int fifo_mask = (s->params->fifosize << 1) - 1; const struct plat_sci_reg *reg; reg = sci_getreg(port, SCRFDR); if (reg->size) - return serial_port_in(port, SCRFDR) & ((port->fifosize << 1) - 1); + return serial_port_in(port, SCRFDR) & fifo_mask; reg = sci_getreg(port, SCFDR); if (reg->size) - return serial_port_in(port, SCFDR) & ((port->fifosize << 1) - 1); + return serial_port_in(port, SCFDR) & fifo_mask; return (serial_port_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; } -/* - * SCI helper for checking the state of the muxed port/RXD pins. - */ -static inline int sci_rxd_in(struct uart_port *port) -{ - struct sci_port *s = to_sci_port(port); - - if (s->cfg->port_reg <= 0) - return 1; - - /* Cast for ARM damage */ - return !!__raw_readb((void __iomem *)(uintptr_t)s->cfg->port_reg); -} - /* ********************************************************************** * * the interrupt related routines * * ********************************************************************** */ @@ -855,7 +813,6 @@ static void sci_transmit_chars(struct uart_port *port) static void sci_receive_chars(struct uart_port *port) { - struct sci_port *sci_port = to_sci_port(port); struct tty_port *tport = &port->state->port; int i, count, copied = 0; unsigned short status; @@ -875,8 +832,7 @@ static void sci_receive_chars(struct uart_port *port) if (port->type == PORT_SCI) { char c = serial_port_in(port, SCxRDR); - if (uart_handle_sysrq_char(port, c) || - sci_port->break_flag) + if (uart_handle_sysrq_char(port, c)) count = 0; else tty_insert_flip_char(tport, c, TTY_NORMAL); @@ -885,25 +841,6 @@ static void sci_receive_chars(struct uart_port *port) char c = serial_port_in(port, SCxRDR); status = serial_port_in(port, SCxSR); -#if defined(CONFIG_CPU_SH3) - /* Skip "chars" during break */ - if (sci_port->break_flag) { - if ((c == 0) && - (status & SCxSR_FER(port))) { - count--; i--; - continue; - } - - /* Nonzero => end-of-break */ - dev_dbg(port->dev, "debounce<%02x>\n", c); - sci_port->break_flag = 0; - - if (STEPFN(c)) { - count--; i--; - continue; - } - } -#endif /* CONFIG_CPU_SH3 */ if (uart_handle_sysrq_char(port, c)) { count--; i--; continue; @@ -941,37 +878,6 @@ static void sci_receive_chars(struct uart_port *port) } } -#define SCI_BREAK_JIFFIES (HZ/20) - -/* - * The sci generates interrupts during the break, - * 1 per millisecond or so during the break period, for 9600 baud. - * So dont bother disabling interrupts. - * But dont want more than 1 break event. - * Use a kernel timer to periodically poll the rx line until - * the break is finished. - */ -static inline void sci_schedule_break_timer(struct sci_port *port) -{ - mod_timer(&port->break_timer, jiffies + SCI_BREAK_JIFFIES); -} - -/* Ensure that two consecutive samples find the break over. */ -static void sci_break_timer(unsigned long data) -{ - struct sci_port *port = (struct sci_port *)data; - - if (sci_rxd_in(&port->port) == 0) { - port->break_flag = 1; - sci_schedule_break_timer(port); - } else if (port->break_flag == 1) { - /* break is over. */ - port->break_flag = 2; - sci_schedule_break_timer(port); - } else - port->break_flag = 0; -} - static int sci_handle_errors(struct uart_port *port) { int copied = 0; @@ -980,7 +886,7 @@ static int sci_handle_errors(struct uart_port *port) struct sci_port *s = to_sci_port(port); /* Handle overruns */ - if (status & s->overrun_mask) { + if (status & s->params->overrun_mask) { port->icount.overrun++; /* overrun error */ @@ -991,35 +897,13 @@ static int sci_handle_errors(struct uart_port *port) } if (status & SCxSR_FER(port)) { - if (sci_rxd_in(port) == 0) { - /* Notify of BREAK */ - struct sci_port *sci_port = to_sci_port(port); - - if (!sci_port->break_flag) { - port->icount.brk++; - - sci_port->break_flag = 1; - sci_schedule_break_timer(sci_port); + /* frame error */ + port->icount.frame++; - /* Do sysrq handling. */ - if (uart_handle_break(port)) - return 0; - - dev_dbg(port->dev, "BREAK detected\n"); - - if (tty_insert_flip_char(tport, 0, TTY_BREAK)) - copied++; - } - - } else { - /* frame error */ - port->icount.frame++; - - if (tty_insert_flip_char(tport, 0, TTY_FRAME)) - copied++; + if (tty_insert_flip_char(tport, 0, TTY_FRAME)) + copied++; - dev_notice(port->dev, "frame error\n"); - } + dev_notice(port->dev, "frame error\n"); } if (status & SCxSR_PER(port)) { @@ -1046,14 +930,14 @@ static int sci_handle_fifo_overrun(struct uart_port *port) int copied = 0; u16 status; - reg = sci_getreg(port, s->overrun_reg); + reg = sci_getreg(port, s->params->overrun_reg); if (!reg->size) return 0; - status = serial_port_in(port, s->overrun_reg); - if (status & s->overrun_mask) { - status &= ~s->overrun_mask; - serial_port_out(port, s->overrun_reg, status); + status = serial_port_in(port, s->params->overrun_reg); + if (status & s->params->overrun_mask) { + status &= ~s->params->overrun_mask; + serial_port_out(port, s->params->overrun_reg, status); port->icount.overrun++; @@ -1072,17 +956,11 @@ static int sci_handle_breaks(struct uart_port *port) int copied = 0; unsigned short status = serial_port_in(port, SCxSR); struct tty_port *tport = &port->state->port; - struct sci_port *s = to_sci_port(port); if (uart_handle_break(port)) return 0; - if (!s->break_flag && status & SCxSR_BRK(port)) { -#if defined(CONFIG_CPU_SH3) - /* Debounce break */ - s->break_flag = 1; -#endif - + if (status & SCxSR_BRK(port)) { port->icount.brk++; /* Notify of BREAK */ @@ -1100,6 +978,146 @@ static int sci_handle_breaks(struct uart_port *port) return copied; } +static int scif_set_rtrg(struct uart_port *port, int rx_trig) +{ + unsigned int bits; + + if (rx_trig < 1) + rx_trig = 1; + if (rx_trig >= port->fifosize) + rx_trig = port->fifosize; + + /* HSCIF can be set to an arbitrary level. */ + if (sci_getreg(port, HSRTRGR)->size) { + serial_port_out(port, HSRTRGR, rx_trig); + return rx_trig; + } + + switch (port->type) { + case PORT_SCIF: + if (rx_trig < 4) { + bits = 0; + rx_trig = 1; + } else if (rx_trig < 8) { + bits = SCFCR_RTRG0; + rx_trig = 4; + } else if (rx_trig < 14) { + bits = SCFCR_RTRG1; + rx_trig = 8; + } else { + bits = SCFCR_RTRG0 | SCFCR_RTRG1; + rx_trig = 14; + } + break; + case PORT_SCIFA: + case PORT_SCIFB: + if (rx_trig < 16) { + bits = 0; + rx_trig = 1; + } else if (rx_trig < 32) { + bits = SCFCR_RTRG0; + rx_trig = 16; + } else if (rx_trig < 48) { + bits = SCFCR_RTRG1; + rx_trig = 32; + } else { + bits = SCFCR_RTRG0 | SCFCR_RTRG1; + rx_trig = 48; + } + break; + default: + WARN(1, "unknown FIFO configuration"); + return 1; + } + + serial_port_out(port, SCFCR, + (serial_port_in(port, SCFCR) & + ~(SCFCR_RTRG1 | SCFCR_RTRG0)) | bits); + + return rx_trig; +} + +static int scif_rtrg_enabled(struct uart_port *port) +{ + if (sci_getreg(port, HSRTRGR)->size) + return serial_port_in(port, HSRTRGR) != 0; + else + return (serial_port_in(port, SCFCR) & + (SCFCR_RTRG0 | SCFCR_RTRG1)) != 0; +} + +static void rx_fifo_timer_fn(unsigned long arg) +{ + struct sci_port *s = (struct sci_port *)arg; + struct uart_port *port = &s->port; + + dev_dbg(port->dev, "Rx timed out\n"); + scif_set_rtrg(port, 1); +} + +static ssize_t rx_trigger_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct uart_port *port = dev_get_drvdata(dev); + struct sci_port *sci = to_sci_port(port); + + return sprintf(buf, "%d\n", sci->rx_trigger); +} + +static ssize_t rx_trigger_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct uart_port *port = dev_get_drvdata(dev); + struct sci_port *sci = to_sci_port(port); + long r; + + if (kstrtol(buf, 0, &r) == -EINVAL) + return -EINVAL; + + sci->rx_trigger = scif_set_rtrg(port, r); + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + scif_set_rtrg(port, 1); + + return count; +} + +static DEVICE_ATTR(rx_fifo_trigger, 0644, rx_trigger_show, rx_trigger_store); + +static ssize_t rx_fifo_timeout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct uart_port *port = dev_get_drvdata(dev); + struct sci_port *sci = to_sci_port(port); + + return sprintf(buf, "%d\n", sci->rx_fifo_timeout); +} + +static ssize_t rx_fifo_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct uart_port *port = dev_get_drvdata(dev); + struct sci_port *sci = to_sci_port(port); + long r; + + if (kstrtol(buf, 0, &r) == -EINVAL) + return -EINVAL; + sci->rx_fifo_timeout = r; + scif_set_rtrg(port, 1); + if (r > 0) + setup_timer(&sci->rx_fifo_timer, rx_fifo_timer_fn, + (unsigned long)sci); + return count; +} + +static DEVICE_ATTR(rx_fifo_timeout, 0644, rx_fifo_timeout_show, rx_fifo_timeout_store); + + #ifdef CONFIG_SERIAL_SH_SCI_DMA static void sci_dma_tx_complete(void *arg) { @@ -1410,20 +1428,14 @@ static void rx_timer_fn(unsigned long arg) } static struct dma_chan *sci_request_dma_chan(struct uart_port *port, - enum dma_transfer_direction dir, - unsigned int id) + enum dma_transfer_direction dir) { - dma_cap_mask_t mask; struct dma_chan *chan; struct dma_slave_config cfg; int ret; - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - chan = dma_request_slave_channel_compat(mask, shdma_chan_filter, - (void *)(unsigned long)id, port->dev, - dir == DMA_MEM_TO_DEV ? "tx" : "rx"); + chan = dma_request_slave_channel(port->dev, + dir == DMA_MEM_TO_DEV ? "tx" : "rx"); if (!chan) { dev_warn(port->dev, "dma_request_slave_channel_compat failed\n"); @@ -1459,12 +1471,11 @@ static void sci_request_dma(struct uart_port *port) dev_dbg(port->dev, "%s: port %d\n", __func__, port->line); - if (!port->dev->of_node && - (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0)) + if (!port->dev->of_node) return; s->cookie_tx = -EINVAL; - chan = sci_request_dma_chan(port, DMA_MEM_TO_DEV, s->cfg->dma_slave_tx); + chan = sci_request_dma_chan(port, DMA_MEM_TO_DEV); dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan); if (chan) { s->chan_tx = chan; @@ -1486,7 +1497,7 @@ static void sci_request_dma(struct uart_port *port) INIT_WORK(&s->work_tx, work_fn_tx); } - chan = sci_request_dma_chan(port, DMA_DEV_TO_MEM, s->cfg->dma_slave_rx); + chan = sci_request_dma_chan(port, DMA_DEV_TO_MEM); dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan); if (chan) { unsigned int i; @@ -1546,10 +1557,10 @@ static inline void sci_free_dma(struct uart_port *port) static irqreturn_t sci_rx_interrupt(int irq, void *ptr) { -#ifdef CONFIG_SERIAL_SH_SCI_DMA struct uart_port *port = ptr; struct sci_port *s = to_sci_port(port); +#ifdef CONFIG_SERIAL_SH_SCI_DMA if (s->chan_rx) { u16 scr = serial_port_in(port, SCSCR); u16 ssr = serial_port_in(port, SCxSR); @@ -1574,6 +1585,14 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) } #endif + if (s->rx_trigger > 1 && s->rx_fifo_timeout > 0) { + if (!scif_rtrg_enabled(port)) + scif_set_rtrg(port, s->rx_trigger); + + mod_timer(&s->rx_fifo_timer, jiffies + DIV_ROUND_UP( + s->rx_frame * s->rx_fifo_timeout, 1000)); + } + /* I think sci_receive_chars has to be called irrespective * of whether the I_IXOFF is set, otherwise, how is the interrupt * to be disabled? @@ -1642,12 +1661,10 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) ssr_status = serial_port_in(port, SCxSR); scr_status = serial_port_in(port, SCSCR); - if (s->overrun_reg == SCxSR) + if (s->params->overrun_reg == SCxSR) orer_status = ssr_status; - else { - if (sci_getreg(port, s->overrun_reg)->size) - orer_status = serial_port_in(port, s->overrun_reg); - } + else if (sci_getreg(port, s->params->overrun_reg)->size) + orer_status = serial_port_in(port, s->params->overrun_reg); err_enabled = scr_status & port_rx_irq_mask(port); @@ -1673,7 +1690,7 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) ret = sci_br_interrupt(irq, ptr); /* Overrun Interrupt */ - if (orer_status & s->overrun_mask) { + if (orer_status & s->params->overrun_mask) { sci_handle_fifo_overrun(port); ret = IRQ_HANDLED; } @@ -1743,8 +1760,10 @@ static int sci_request_irq(struct sci_port *port) desc = sci_irq_desc + i; port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s", dev_name(up->dev), desc->desc); - if (!port->irqstr[j]) + if (!port->irqstr[j]) { + ret = -ENOMEM; goto out_nomem; + } ret = request_irq(irq, desc->handler, up->irqflags, port->irqstr[j], port); @@ -1874,7 +1893,7 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) mctrl_gpio_set(s->gpios, mctrl); - if (!(s->cfg->capabilities & SCIx_HAVE_RTSCTS)) + if (!s->has_rtscts) return; if (!(mctrl & TIOCM_RTS)) { @@ -2136,6 +2155,7 @@ static void sci_reset(struct uart_port *port) { const struct plat_sci_reg *reg; unsigned int status; + struct sci_port *s = to_sci_port(port); do { status = serial_port_in(port, SCxSR); @@ -2155,12 +2175,26 @@ static void sci_reset(struct uart_port *port) status &= ~(SCLSR_TO | SCLSR_ORER); serial_port_out(port, SCLSR, status); } + + if (s->rx_trigger > 1) { + if (s->rx_fifo_timeout) { + scif_set_rtrg(port, 1); + setup_timer(&s->rx_fifo_timer, rx_fifo_timer_fn, + (unsigned long)s); + } else { + if (port->type == PORT_SCIFA || + port->type == PORT_SCIFB) + scif_set_rtrg(port, 1); + else + scif_set_rtrg(port, s->rx_trigger); + } + } } static void sci_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - unsigned int baud, smr_val = SCSMR_ASYNC, scr_val = 0, i; + unsigned int baud, smr_val = SCSMR_ASYNC, scr_val = 0, i, bits; unsigned int brr = 255, cks = 0, srr = 15, dl = 0, sccks = 0; unsigned int brr1 = 255, cks1 = 0, srr1 = 15, dl1 = 0; struct sci_port *s = to_sci_port(port); @@ -2341,7 +2375,8 @@ done: serial_port_out(port, SCFCR, ctrl); } - scr_val |= s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0); + scr_val |= SCSCR_RE | SCSCR_TE | + (s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0)); dev_dbg(port->dev, "SCSCR 0x%x\n", scr_val); serial_port_out(port, SCSCR, scr_val); if ((srr + 1 == 5) && @@ -2355,7 +2390,6 @@ done: udelay(DIV_ROUND_UP(10 * 1000000, baud)); } -#ifdef CONFIG_SERIAL_SH_SCI_DMA /* * Calculate delay for 2 DMA buffers (4 FIFO). * See serial_core.c::uart_update_timeout(). @@ -2366,36 +2400,34 @@ done: * value obtained by this formula is too small. Therefore, if the value * is smaller than 20ms, use 20ms as the timeout value for DMA. */ - if (s->chan_rx) { - unsigned int bits; + /* byte size and parity */ + switch (termios->c_cflag & CSIZE) { + case CS5: + bits = 7; + break; + case CS6: + bits = 8; + break; + case CS7: + bits = 9; + break; + default: + bits = 10; + break; + } - /* byte size and parity */ - switch (termios->c_cflag & CSIZE) { - case CS5: - bits = 7; - break; - case CS6: - bits = 8; - break; - case CS7: - bits = 9; - break; - default: - bits = 10; - break; - } + if (termios->c_cflag & CSTOPB) + bits++; + if (termios->c_cflag & PARENB) + bits++; - if (termios->c_cflag & CSTOPB) - bits++; - if (termios->c_cflag & PARENB) - bits++; - s->rx_timeout = DIV_ROUND_UP((s->buf_len_rx * 2 * bits * HZ) / - (baud / 10), 10); - dev_dbg(port->dev, "DMA Rx t-out %ums, tty t-out %u jiffies\n", - s->rx_timeout * 1000 / HZ, port->timeout); - if (s->rx_timeout < msecs_to_jiffies(20)) - s->rx_timeout = msecs_to_jiffies(20); - } + s->rx_frame = (100 * bits * HZ) / (baud / 10); +#ifdef CONFIG_SERIAL_SH_SCI_DMA + s->rx_timeout = DIV_ROUND_UP(s->buf_len_rx * 2 * s->rx_frame, 1000); + dev_dbg(port->dev, "DMA Rx t-out %ums, tty t-out %u jiffies\n", + s->rx_timeout * 1000 / HZ, port->timeout); + if (s->rx_timeout < msecs_to_jiffies(20)) + s->rx_timeout = msecs_to_jiffies(20); #endif if ((termios->c_cflag & CREAD) != 0) @@ -2452,7 +2484,7 @@ static int sci_remap_port(struct uart_port *port) if (port->membase) return 0; - if (port->flags & UPF_IOREMAP) { + if (port->dev->of_node || (port->flags & UPF_IOREMAP)) { port->membase = ioremap_nocache(port->mapbase, sport->reg_size); if (unlikely(!port->membase)) { dev_err(port->dev, "can't remap port#%d\n", port->line); @@ -2474,7 +2506,7 @@ static void sci_release_port(struct uart_port *port) { struct sci_port *sport = to_sci_port(port); - if (port->flags & UPF_IOREMAP) { + if (port->dev->of_node || (port->flags & UPF_IOREMAP)) { iounmap(port->membase); port->membase = NULL; } @@ -2604,9 +2636,50 @@ found: return 0; } +static const struct sci_port_params * +sci_probe_regmap(const struct plat_sci_port *cfg) +{ + unsigned int regtype; + + if (cfg->regtype != SCIx_PROBE_REGTYPE) + return &sci_port_params[cfg->regtype]; + + switch (cfg->type) { + case PORT_SCI: + regtype = SCIx_SCI_REGTYPE; + break; + case PORT_IRDA: + regtype = SCIx_IRDA_REGTYPE; + break; + case PORT_SCIFA: + regtype = SCIx_SCIFA_REGTYPE; + break; + case PORT_SCIFB: + regtype = SCIx_SCIFB_REGTYPE; + break; + case PORT_SCIF: + /* + * The SH-4 is a bit of a misnomer here, although that's + * where this particular port layout originated. This + * configuration (or some slight variation thereof) + * remains the dominant model for all SCIFs. + */ + regtype = SCIx_SH4_SCIF_REGTYPE; + break; + case PORT_HSCIF: + regtype = SCIx_HSCIF_REGTYPE; + break; + default: + pr_err("Can't probe register map for given port\n"); + return NULL; + } + + return &sci_port_params[regtype]; +} + static int sci_init_single(struct platform_device *dev, struct sci_port *sci_port, unsigned int index, - struct plat_sci_port *p, bool early) + const struct plat_sci_port *p, bool early) { struct uart_port *port = &sci_port->port; const struct resource *res; @@ -2643,57 +2716,41 @@ static int sci_init_single(struct platform_device *dev, sci_port->irqs[3] = sci_port->irqs[0]; } - if (p->regtype == SCIx_PROBE_REGTYPE) { - ret = sci_probe_regmap(p); - if (unlikely(ret)) - return ret; - } + sci_port->params = sci_probe_regmap(p); + if (unlikely(sci_port->params == NULL)) + return -EINVAL; switch (p->type) { case PORT_SCIFB: - port->fifosize = 256; - sci_port->overrun_reg = SCxSR; - sci_port->overrun_mask = SCIFA_ORER; - sci_port->sampling_rate_mask = SCI_SR_SCIFAB; + sci_port->rx_trigger = 48; break; case PORT_HSCIF: - port->fifosize = 128; - sci_port->overrun_reg = SCLSR; - sci_port->overrun_mask = SCLSR_ORER; - sci_port->sampling_rate_mask = SCI_SR_RANGE(8, 32); + sci_port->rx_trigger = 64; break; case PORT_SCIFA: - port->fifosize = 64; - sci_port->overrun_reg = SCxSR; - sci_port->overrun_mask = SCIFA_ORER; - sci_port->sampling_rate_mask = SCI_SR_SCIFAB; + sci_port->rx_trigger = 32; break; case PORT_SCIF: - port->fifosize = 16; - if (p->regtype == SCIx_SH7705_SCIF_REGTYPE) { - sci_port->overrun_reg = SCxSR; - sci_port->overrun_mask = SCIFA_ORER; - sci_port->sampling_rate_mask = SCI_SR(16); - } else { - sci_port->overrun_reg = SCLSR; - sci_port->overrun_mask = SCLSR_ORER; - sci_port->sampling_rate_mask = SCI_SR(32); - } + if (p->regtype == SCIx_SH7705_SCIF_REGTYPE) + /* RX triggering not implemented for this IP */ + sci_port->rx_trigger = 1; + else + sci_port->rx_trigger = 8; break; default: - port->fifosize = 1; - sci_port->overrun_reg = SCxSR; - sci_port->overrun_mask = SCI_ORER; - sci_port->sampling_rate_mask = SCI_SR(32); + sci_port->rx_trigger = 1; break; } + sci_port->rx_fifo_timeout = 0; + /* SCIFA on sh7723 and sh7724 need a custom sampling rate that doesn't * match the SoC datasheet, this should be investigated. Let platform * data override the sampling rate for now. */ - if (p->sampling_rate) - sci_port->sampling_rate_mask = SCI_SR(p->sampling_rate); + sci_port->sampling_rate_mask = p->sampling_rate + ? SCI_SR(p->sampling_rate) + : sci_port->params->sampling_rate_mask; if (!early) { ret = sci_init_clocks(sci_port, &dev->dev); @@ -2705,34 +2762,17 @@ static int sci_init_single(struct platform_device *dev, pm_runtime_enable(&dev->dev); } - sci_port->break_timer.data = (unsigned long)sci_port; - sci_port->break_timer.function = sci_break_timer; - init_timer(&sci_port->break_timer); - - /* - * Establish some sensible defaults for the error detection. - */ - if (p->type == PORT_SCI) { - sci_port->error_mask = SCI_DEFAULT_ERROR_MASK; - sci_port->error_clear = SCI_ERROR_CLEAR; - } else { - sci_port->error_mask = SCIF_DEFAULT_ERROR_MASK; - sci_port->error_clear = SCIF_ERROR_CLEAR; - } + port->type = p->type; + port->flags = UPF_FIXED_PORT | UPF_BOOT_AUTOCONF | p->flags; + port->fifosize = sci_port->params->fifosize; - /* - * Make the error mask inclusive of overrun detection, if - * supported. - */ - if (sci_port->overrun_reg == SCxSR) { - sci_port->error_mask |= sci_port->overrun_mask; - sci_port->error_clear &= ~sci_port->overrun_mask; + if (port->type == PORT_SCI) { + if (sci_port->reg_size >= 0x20) + port->regshift = 2; + else + port->regshift = 1; } - port->type = p->type; - port->flags = UPF_FIXED_PORT | p->flags; - port->regshift = p->regshift; - /* * The UART port needs an IRQ value, so we peg this to the RX IRQ * for the multi-IRQ ports, which is where we are primarily @@ -2746,10 +2786,6 @@ static int sci_init_single(struct platform_device *dev, port->serial_in = sci_serial_in; port->serial_out = sci_serial_out; - if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) - dev_dbg(port->dev, "DMA tx %d, rx %d\n", - p->dma_slave_tx, p->dma_slave_rx); - return 0; } @@ -2791,7 +2827,8 @@ static void serial_console_write(struct console *co, const char *s, /* first save SCSCR then disable interrupts, keep clock source */ ctrl = serial_port_in(port, SCSCR); - ctrl_temp = (sci_port->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0)) | + ctrl_temp = SCSCR_RE | SCSCR_TE | + (sci_port->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0)) | (ctrl & (SCSCR_CKE1 | SCSCR_CKE0)); serial_port_out(port, SCSCR, ctrl_temp); @@ -2866,7 +2903,7 @@ static char early_serial_buf[32]; static int sci_probe_earlyprintk(struct platform_device *pdev) { - struct plat_sci_port *cfg = dev_get_platdata(&pdev->dev); + const struct plat_sci_port *cfg = dev_get_platdata(&pdev->dev); if (early_serial_console.data) return -EEXIST; @@ -2916,6 +2953,15 @@ static int sci_remove(struct platform_device *dev) sci_cleanup_single(port); + if (port->port.fifosize > 1) { + sysfs_remove_file(&dev->dev.kobj, + &dev_attr_rx_fifo_trigger.attr); + } + if (port->port.type == PORT_SCIFA || port->port.type == PORT_SCIFB) { + sysfs_remove_file(&dev->dev.kobj, + &dev_attr_rx_fifo_timeout.attr); + } + return 0; } @@ -2963,12 +3009,13 @@ static const struct of_device_id of_sci_match[] = { }; MODULE_DEVICE_TABLE(of, of_sci_match); -static struct plat_sci_port * -sci_parse_dt(struct platform_device *pdev, unsigned int *dev_id) +static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev, + unsigned int *dev_id) { struct device_node *np = pdev->dev.of_node; const struct of_device_id *match; struct plat_sci_port *p; + struct sci_port *sp; int id; if (!IS_ENABLED(CONFIG_OF) || !np) @@ -2989,15 +3036,14 @@ sci_parse_dt(struct platform_device *pdev, unsigned int *dev_id) return NULL; } + sp = &sci_ports[id]; *dev_id = id; - p->flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; p->type = SCI_OF_TYPE(match->data); p->regtype = SCI_OF_REGTYPE(match->data); - p->scscr = SCSCR_RE | SCSCR_TE; if (of_find_property(np, "uart-has-rtscts", NULL)) - p->capabilities |= SCIx_HAVE_RTSCTS; + sp->has_rtscts = true; return p; } @@ -3025,7 +3071,7 @@ static int sci_probe_single(struct platform_device *dev, if (IS_ERR(sciport->gpios) && PTR_ERR(sciport->gpios) != -ENOSYS) return PTR_ERR(sciport->gpios); - if (p->capabilities & SCIx_HAVE_RTSCTS) { + if (sciport->has_rtscts) { if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(sciport->gpios, UART_GPIO_CTS)) || !IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(sciport->gpios, @@ -3081,6 +3127,24 @@ static int sci_probe(struct platform_device *dev) if (ret) return ret; + if (sp->port.fifosize > 1) { + ret = sysfs_create_file(&dev->dev.kobj, + &dev_attr_rx_fifo_trigger.attr); + if (ret) + return ret; + } + if (sp->port.type == PORT_SCIFA || sp->port.type == PORT_SCIFB) { + ret = sysfs_create_file(&dev->dev.kobj, + &dev_attr_rx_fifo_timeout.attr); + if (ret) { + if (sp->port.fifosize > 1) { + sysfs_remove_file(&dev->dev.kobj, + &dev_attr_rx_fifo_trigger.attr); + } + return ret; + } + } + #ifdef CONFIG_SH_STANDARD_BIOS sh_bios_gdb_detach(); #endif @@ -3159,12 +3223,12 @@ static int __init early_console_setup(struct earlycon_device *device, device->port.serial_out = sci_serial_out; device->port.type = type; memcpy(&sci_ports[0].port, &device->port, sizeof(struct uart_port)); + port_cfg.type = type; sci_ports[0].cfg = &port_cfg; - sci_ports[0].cfg->type = type; - sci_probe_regmap(sci_ports[0].cfg); - port_cfg.scscr = sci_serial_in(&sci_ports[0].port, SCSCR) | - SCSCR_RE | SCSCR_TE; - sci_serial_out(&sci_ports[0].port, SCSCR, port_cfg.scscr); + sci_ports[0].params = sci_probe_regmap(&port_cfg); + port_cfg.scscr = sci_serial_in(&sci_ports[0].port, SCSCR); + sci_serial_out(&sci_ports[0].port, SCSCR, + SCSCR_RE | SCSCR_TE | port_cfg.scscr); device->con->write = serial_console_write; return 0; diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index ffa6d688c335..971b2ab088d8 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -29,6 +29,8 @@ enum { SCPDR, /* Serial Port Data Register */ SCDL, /* BRG Frequency Division Register */ SCCKS, /* BRG Clock Select Register */ + HSRTRGR, /* Rx FIFO Data Count Trigger Register */ + HSTTRGR, /* Tx FIFO Data Count Trigger Register */ SCIx_NR_REGS, }; @@ -99,6 +101,10 @@ enum { #define SCIF_BREAK_CLEAR (u32)(~(SCIF_PER | SCIF_FER | SCIF_BRK)) /* SCFCR (FIFO Control Register) */ +#define SCFCR_RTRG1 BIT(7) /* Receive FIFO Data Count Trigger */ +#define SCFCR_RTRG0 BIT(6) +#define SCFCR_TTRG1 BIT(5) /* Transmit FIFO Data Count Trigger */ +#define SCFCR_TTRG0 BIT(4) #define SCFCR_MCE BIT(3) /* Modem Control Enable */ #define SCFCR_TFRST BIT(2) /* Transmit FIFO Data Register Reset */ #define SCFCR_RFRST BIT(1) /* Receive FIFO Data Register Reset */ @@ -145,18 +151,18 @@ enum { #define SCCKS_XIN BIT(14) /* SC_CLK uses bus clock (1) or SCIF_CLK (0) */ #define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND) -#define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF) +#define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_DR | SCIF_RDF) #define SCxSR_TDxE(port) (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE) #define SCxSR_FER(port) (((port)->type == PORT_SCI) ? SCI_FER : SCIF_FER) #define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER) #define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK) -#define SCxSR_ERRORS(port) (to_sci_port(port)->error_mask) +#define SCxSR_ERRORS(port) (to_sci_port(port)->params->error_mask) #define SCxSR_RDxF_CLEAR(port) \ (((port)->type == PORT_SCI) ? SCI_RDxF_CLEAR : SCIF_RDxF_CLEAR) #define SCxSR_ERROR_CLEAR(port) \ - (to_sci_port(port)->error_clear) + (to_sci_port(port)->params->error_clear) #define SCxSR_TDxE_CLEAR(port) \ (((port)->type == PORT_SCI) ? SCI_TDxE_CLEAR : SCIF_TDxE_CLEAR) #define SCxSR_BREAK_CLEAR(port) \ diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index b186c9c4f850..e03282d92b59 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -1061,7 +1061,7 @@ static void sirfsoc_uart_config_port(struct uart_port *port, int flags) } } -static struct uart_ops sirfsoc_uart_ops = { +static const struct uart_ops sirfsoc_uart_ops = { .tx_empty = sirfsoc_uart_tx_empty, .get_mctrl = sirfsoc_uart_get_mctrl, .set_mctrl = sirfsoc_uart_set_mctrl, diff --git a/drivers/tty/serial/sn_console.c b/drivers/tty/serial/sn_console.c index d4692d888e9d..9e0e6586c698 100644 --- a/drivers/tty/serial/sn_console.c +++ b/drivers/tty/serial/sn_console.c @@ -380,7 +380,7 @@ static void snp_config_port(struct uart_port *port, int flags) /* Associate the uart functions above - given to serial core */ -static struct uart_ops sn_console_ops = { +static const struct uart_ops sn_console_ops = { .tx_empty = snp_tx_empty, .set_mctrl = snp_set_mctrl, .get_mctrl = snp_get_mctrl, diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 699447aa8b43..d98e3dc4838e 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -498,7 +498,7 @@ static int sprd_verify_port(struct uart_port *port, return 0; } -static struct uart_ops serial_sprd_ops = { +static const struct uart_ops serial_sprd_ops = { .tx_empty = sprd_tx_empty, .get_mctrl = sprd_get_mctrl, .set_mctrl = sprd_set_mctrl, diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index 379e5bd37df9..bcf1d33e6ffe 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -18,6 +18,7 @@ #include <linux/serial.h> #include <linux/console.h> #include <linux/sysrq.h> +#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/io.h> #include <linux/irq.h> @@ -30,15 +31,23 @@ #include <linux/of_platform.h> #include <linux/serial_core.h> #include <linux/clk.h> +#include <linux/gpio/consumer.h> #define DRIVER_NAME "st-asc" #define ASC_SERIAL_NAME "ttyAS" #define ASC_FIFO_SIZE 16 #define ASC_MAX_PORTS 8 +/* Pinctrl states */ +#define DEFAULT 0 +#define NO_HW_FLOWCTRL 1 + struct asc_port { struct uart_port port; + struct gpio_desc *rts; struct clk *clk; + struct pinctrl *pinctrl; + struct pinctrl_state *states[2]; unsigned int hw_flow_control:1; unsigned int force_m1:1; }; @@ -287,9 +296,19 @@ static void asc_transmit_chars(struct uart_port *port) static void asc_receive_chars(struct uart_port *port) { struct tty_port *tport = &port->state->port; - unsigned long status; + unsigned long status, mode; unsigned long c = 0; char flag; + bool ignore_pe = false; + + /* + * Datasheet states: If the MODE field selects an 8-bit frame then + * this [parity error] bit is undefined. Software should ignore this + * bit when reading 8-bit frames. + */ + mode = asc_in(port, ASC_CTL) & ASC_CTL_MODE_MSK; + if (mode == ASC_CTL_MODE_8BIT || mode == ASC_CTL_MODE_8BIT_PAR) + ignore_pe = true; if (port->irq_wake) pm_wakeup_event(tport->tty->dev, 0); @@ -299,8 +318,8 @@ static void asc_receive_chars(struct uart_port *port) flag = TTY_NORMAL; port->icount.rx++; - if ((c & (ASC_RXBUF_FE | ASC_RXBUF_PE)) || - status & ASC_STA_OE) { + if (status & ASC_STA_OE || c & ASC_RXBUF_FE || + (c & ASC_RXBUF_PE && !ignore_pe)) { if (c & ASC_RXBUF_FE) { if (c == (ASC_RXBUF_FE | ASC_RXBUF_DUMMY_RX)) { @@ -381,12 +400,27 @@ static unsigned int asc_tx_empty(struct uart_port *port) static void asc_set_mctrl(struct uart_port *port, unsigned int mctrl) { + struct asc_port *ascport = to_asc_port(port); + /* - * This routine is used for seting signals of: DTR, DCD, CTS/RTS - * We use ASC's hardware for CTS/RTS, so don't need any for that. - * Some boards have DTR and DCD implemented using PIO pins, - * code to do this should be hooked in here. + * This routine is used for seting signals of: DTR, DCD, CTS and RTS. + * We use ASC's hardware for CTS/RTS when hardware flow-control is + * enabled, however if the RTS line is required for another purpose, + * commonly controlled using HUP from userspace, then we need to toggle + * it manually, using GPIO. + * + * Some boards also have DTR and DCD implemented using PIO pins, code to + * do this should be hooked in here. */ + + if (!ascport->rts) + return; + + /* If HW flow-control is enabled, we can't fiddle with the RTS line */ + if (asc_in(port, ASC_CTL) & ASC_CTL_CTSENABLE) + return; + + gpiod_set_value(ascport->rts, mctrl & TIOCM_RTS); } static unsigned int asc_get_mctrl(struct uart_port *port) @@ -479,6 +513,8 @@ static void asc_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct asc_port *ascport = to_asc_port(port); + struct device_node *np = port->dev->of_node; + struct gpio_desc *gpiod; unsigned int baud; u32 ctrl_val; tcflag_t cflag; @@ -522,9 +558,32 @@ static void asc_set_termios(struct uart_port *port, struct ktermios *termios, ctrl_val |= ASC_CTL_PARITYODD; /* hardware flow control */ - if ((cflag & CRTSCTS)) + if ((cflag & CRTSCTS)) { ctrl_val |= ASC_CTL_CTSENABLE; + /* If flow-control selected, stop handling RTS manually */ + if (ascport->rts) { + devm_gpiod_put(port->dev, ascport->rts); + ascport->rts = NULL; + + pinctrl_select_state(ascport->pinctrl, + ascport->states[DEFAULT]); + } + } else { + /* If flow-control disabled, it's safe to handle RTS manually */ + if (!ascport->rts && ascport->states[NO_HW_FLOWCTRL]) { + pinctrl_select_state(ascport->pinctrl, + ascport->states[NO_HW_FLOWCTRL]); + + gpiod = devm_get_gpiod_from_child(port->dev, "rts", + &np->fwnode); + if (!IS_ERR(gpiod)) { + gpiod_direction_output(gpiod, 0); + ascport->rts = gpiod; + } + } + } + if ((baud < 19200) && !ascport->force_m1) { asc_out(port, ASC_BAUDRATE, (port->uartclk / (16 * baud))); } else { @@ -667,6 +726,7 @@ static int asc_init_port(struct asc_port *ascport, { struct uart_port *port = &ascport->port; struct resource *res; + int ret; port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF; @@ -693,6 +753,27 @@ static int asc_init_port(struct asc_port *ascport, WARN_ON(ascport->port.uartclk == 0); clk_disable_unprepare(ascport->clk); + ascport->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(ascport->pinctrl)) { + ret = PTR_ERR(ascport->pinctrl); + dev_err(&pdev->dev, "Failed to get Pinctrl: %d\n", ret); + } + + ascport->states[DEFAULT] = + pinctrl_lookup_state(ascport->pinctrl, "default"); + if (IS_ERR(ascport->states[DEFAULT])) { + ret = PTR_ERR(ascport->states[DEFAULT]); + dev_err(&pdev->dev, + "Failed to look up Pinctrl state 'default': %d\n", ret); + return ret; + } + + /* "no-hw-flowctrl" state is optional */ + ascport->states[NO_HW_FLOWCTRL] = + pinctrl_lookup_state(ascport->pinctrl, "no-hw-flowctrl"); + if (IS_ERR(ascport->states[NO_HW_FLOWCTRL])) + ascport->states[NO_HW_FLOWCTRL] = NULL; + return 0; } @@ -713,9 +794,11 @@ static struct asc_port *asc_of_get_asc_port(struct platform_device *pdev) return NULL; asc_ports[id].hw_flow_control = of_property_read_bool(np, - "st,hw-flow-control"); + "uart-has-rtscts"); asc_ports[id].force_m1 = of_property_read_bool(np, "st,force_m1"); asc_ports[id].port.line = id; + asc_ports[id].rts = NULL; + return &asc_ports[id]; } diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 99ef5c6e4766..73abd89c0108 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -370,7 +370,7 @@ static int sunhv_verify_port(struct uart_port *port, struct serial_struct *ser) return -EINVAL; } -static struct uart_ops sunhv_pops = { +static const struct uart_ops sunhv_pops = { .tx_empty = sunhv_tx_empty, .set_mctrl = sunhv_set_mctrl, .get_mctrl = sunhv_get_mctrl, diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index 8b6ace341029..252cea49c068 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -1046,7 +1046,7 @@ static void sunzilog_put_poll_char(struct uart_port *port, } #endif /* CONFIG_CONSOLE_POLL */ -static struct uart_ops sunzilog_pops = { +static const struct uart_ops sunzilog_pops = { .tx_empty = sunzilog_tx_empty, .set_mctrl = sunzilog_set_mctrl, .get_mctrl = sunzilog_get_mctrl, diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c index 485de53c5d75..439057e8107a 100644 --- a/drivers/tty/serial/vr41xx_siu.c +++ b/drivers/tty/serial/vr41xx_siu.c @@ -681,7 +681,7 @@ static int siu_verify_port(struct uart_port *port, struct serial_struct *serial) return 0; } -static struct uart_ops siu_uart_ops = { +static const struct uart_ops siu_uart_ops = { .tx_empty = siu_tx_empty, .set_mctrl = siu_set_mctrl, .get_mctrl = siu_get_mctrl, diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 6b85adce0ac9..435a6f3260be 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -592,7 +592,7 @@ static void vt8500_put_poll_char(struct uart_port *port, unsigned char c) } #endif -static struct uart_ops vt8500_uart_pops = { +static const struct uart_ops vt8500_uart_pops = { .tx_empty = vt8500_tx_empty, .set_mctrl = vt8500_set_mctrl, .get_mctrl = vt8500_get_mctrl, diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index dd4c02fa4820..ad77d0ed0c46 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -93,6 +93,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); #define CDNS_UART_MR_CLKSEL 0x00000001 /* Pre-scalar selection */ #define CDNS_UART_MR_CHMODE_L_LOOP 0x00000200 /* Local loop back mode */ #define CDNS_UART_MR_CHMODE_NORM 0x00000000 /* Normal mode */ +#define CDNS_UART_MR_CHMODE_MASK 0x00000300 /* Mask for mode bits */ #define CDNS_UART_MR_STOPMODE_2_BIT 0x00000080 /* 2 stop bits */ #define CDNS_UART_MR_STOPMODE_1_BIT 0x00000000 /* 1 stop bit */ @@ -998,17 +999,25 @@ static unsigned int cdns_uart_get_mctrl(struct uart_port *port) static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { u32 val; + u32 mode_reg; val = readl(port->membase + CDNS_UART_MODEMCR); + mode_reg = readl(port->membase + CDNS_UART_MR); val &= ~(CDNS_UART_MODEMCR_RTS | CDNS_UART_MODEMCR_DTR); + mode_reg &= ~CDNS_UART_MR_CHMODE_MASK; if (mctrl & TIOCM_RTS) val |= CDNS_UART_MODEMCR_RTS; if (mctrl & TIOCM_DTR) val |= CDNS_UART_MODEMCR_DTR; + if (mctrl & TIOCM_LOOP) + mode_reg |= CDNS_UART_MR_CHMODE_L_LOOP; + else + mode_reg |= CDNS_UART_MR_CHMODE_NORM; writel(val, port->membase + CDNS_UART_MODEMCR); + writel(mode_reg, port->membase + CDNS_UART_MR); } #ifdef CONFIG_CONSOLE_POLL diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index eeefd76a30da..d32bd499d684 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -1045,7 +1045,7 @@ static int zs_verify_port(struct uart_port *uport, struct serial_struct *ser) } -static struct uart_ops zs_ops = { +static const struct uart_ops zs_ops = { .tx_empty = zs_tx_empty, .set_mctrl = zs_set_mctrl, .get_mctrl = zs_get_mctrl, diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index aa80dc94ddc2..4e7a4e9dcf4d 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -422,7 +422,7 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string); * * Returns the number of bytes not processed */ -int tty_ldisc_receive_buf(struct tty_ldisc *ld, unsigned char *p, +int tty_ldisc_receive_buf(struct tty_ldisc *ld, const unsigned char *p, char *f, int count) { if (ld->ops->receive_buf2) @@ -437,7 +437,7 @@ int tty_ldisc_receive_buf(struct tty_ldisc *ld, unsigned char *p, EXPORT_SYMBOL_GPL(tty_ldisc_receive_buf); static int -receive_buf(struct tty_ldisc *ld, struct tty_buffer *head, int count) +receive_buf(struct tty_port *port, struct tty_buffer *head, int count) { unsigned char *p = char_buf_ptr(head, head->read); char *f = NULL; @@ -445,7 +445,7 @@ receive_buf(struct tty_ldisc *ld, struct tty_buffer *head, int count) if (~head->flags & TTYB_NORMAL) f = flag_buf_ptr(head, head->read); - return tty_ldisc_receive_buf(ld, p, f, count); + return port->client_ops->receive_buf(port, p, f, count); } /** @@ -465,16 +465,6 @@ static void flush_to_ldisc(struct work_struct *work) { struct tty_port *port = container_of(work, struct tty_port, buf.work); struct tty_bufhead *buf = &port->buf; - struct tty_struct *tty; - struct tty_ldisc *disc; - - tty = READ_ONCE(port->itty); - if (tty == NULL) - return; - - disc = tty_ldisc_ref(tty); - if (disc == NULL) - return; mutex_lock(&buf->lock); @@ -504,7 +494,7 @@ static void flush_to_ldisc(struct work_struct *work) continue; } - count = receive_buf(disc, head, count); + count = receive_buf(port, head, count); if (!count) break; head->read += count; @@ -512,7 +502,6 @@ static void flush_to_ldisc(struct work_struct *work) mutex_unlock(&buf->lock); - tty_ldisc_deref(disc); } /** diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 734a635e7363..a1fd3f7d487a 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -855,7 +855,7 @@ static void tty_vhangup_session(struct tty_struct *tty) int tty_hung_up_p(struct file *filp) { - return (filp->f_op == &hung_up_tty_fops); + return (filp && filp->f_op == &hung_up_tty_fops); } EXPORT_SYMBOL(tty_hung_up_p); @@ -1745,6 +1745,37 @@ static int tty_release_checks(struct tty_struct *tty, int idx) } /** + * tty_release_struct - release a tty struct + * @tty: tty device + * @idx: index of the tty + * + * Performs the final steps to release and free a tty device. It is + * roughly the reverse of tty_init_dev. + */ +void tty_release_struct(struct tty_struct *tty, int idx) +{ + /* + * Ask the line discipline code to release its structures + */ + tty_ldisc_release(tty); + + /* Wait for pending work before tty destruction commmences */ + tty_flush_works(tty); + + tty_debug_hangup(tty, "freeing structure\n"); + /* + * The release_tty function takes care of the details of clearing + * the slots and preserving the termios structure. The tty_unlock_pair + * should be safe as we keep a kref while the tty is locked (so the + * unlock never unlocks a freed tty). + */ + mutex_lock(&tty_mutex); + release_tty(tty, idx); + mutex_unlock(&tty_mutex); +} +EXPORT_SYMBOL_GPL(tty_release_struct); + +/** * tty_release - vfs callback for close * @inode: inode of tty * @filp: file pointer for handle to tty @@ -1898,25 +1929,8 @@ int tty_release(struct inode *inode, struct file *filp) return 0; tty_debug_hangup(tty, "final close\n"); - /* - * Ask the line discipline code to release its structures - */ - tty_ldisc_release(tty); - - /* Wait for pending work before tty destruction commmences */ - tty_flush_works(tty); - - tty_debug_hangup(tty, "freeing structure\n"); - /* - * The release_tty function takes care of the details of clearing - * the slots and preserving the termios structure. The tty_unlock_pair - * should be safe as we keep a kref while the tty is locked (so the - * unlock never unlocks a freed tty). - */ - mutex_lock(&tty_mutex); - release_tty(tty, idx); - mutex_unlock(&tty_mutex); + tty_release_struct(tty, idx); return 0; } diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index c3f9d93ba227..5cd3cd932293 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -16,6 +16,45 @@ #include <linux/bitops.h> #include <linux/delay.h> #include <linux/module.h> +#include <linux/serdev.h> + +static int tty_port_default_receive_buf(struct tty_port *port, + const unsigned char *p, + const unsigned char *f, size_t count) +{ + int ret; + struct tty_struct *tty; + struct tty_ldisc *disc; + + tty = READ_ONCE(port->itty); + if (!tty) + return 0; + + disc = tty_ldisc_ref(tty); + if (!disc) + return 0; + + ret = tty_ldisc_receive_buf(disc, p, (char *)f, count); + + tty_ldisc_deref(disc); + + return ret; +} + +static void tty_port_default_wakeup(struct tty_port *port) +{ + struct tty_struct *tty = tty_port_tty_get(port); + + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } +} + +static const struct tty_port_client_operations default_client_ops = { + .receive_buf = tty_port_default_receive_buf, + .write_wakeup = tty_port_default_wakeup, +}; void tty_port_init(struct tty_port *port) { @@ -28,6 +67,7 @@ void tty_port_init(struct tty_port *port) spin_lock_init(&port->lock); port->close_delay = (50 * HZ) / 100; port->closing_wait = (3000 * HZ) / 100; + port->client_ops = &default_client_ops; kref_init(&port->kref); } EXPORT_SYMBOL(tty_port_init); @@ -67,8 +107,7 @@ struct device *tty_port_register_device(struct tty_port *port, struct tty_driver *driver, unsigned index, struct device *device) { - tty_port_link_device(port, driver, index); - return tty_register_device(driver, index, device); + return tty_port_register_device_attr(port, driver, index, device, NULL, NULL); } EXPORT_SYMBOL_GPL(tty_port_register_device); @@ -90,7 +129,15 @@ struct device *tty_port_register_device_attr(struct tty_port *port, struct device *device, void *drvdata, const struct attribute_group **attr_grp) { + struct device *dev; + tty_port_link_device(port, driver, index); + + dev = serdev_tty_port_register(port, device, driver, index); + if (PTR_ERR(dev) != -ENODEV) + /* Skip creating cdev if we registered a serdev device */ + return dev; + return tty_register_device_attr(driver, index, device, drvdata, attr_grp); } @@ -142,6 +189,9 @@ static void tty_port_destructor(struct kref *kref) /* check if last port ref was dropped before tty release */ if (WARN_ON(port->itty)) return; + + serdev_tty_port_unregister(port); + if (port->xmit_buf) free_page((unsigned long)port->xmit_buf); tty_port_destroy(port); @@ -273,12 +323,7 @@ EXPORT_SYMBOL_GPL(tty_port_tty_hangup); */ void tty_port_tty_wakeup(struct tty_port *port) { - struct tty_struct *tty = tty_port_tty_get(port); - - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + port->client_ops->write_wakeup(port); } EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); @@ -335,7 +380,7 @@ EXPORT_SYMBOL(tty_port_lower_dtr_rts); * tty_port_block_til_ready - Waiting logic for tty open * @port: the tty port being opened * @tty: the tty device being bound - * @filp: the file pointer of the opener + * @filp: the file pointer of the opener or NULL * * Implement the core POSIX/SuS tty behaviour when opening a tty device. * Handles: @@ -369,7 +414,7 @@ int tty_port_block_til_ready(struct tty_port *port, tty_port_set_active(port, 1); return 0; } - if (filp->f_flags & O_NONBLOCK) { + if (filp == NULL || (filp->f_flags & O_NONBLOCK)) { /* Indicate we are open */ if (C_BAUD(tty)) tty_port_raise_dtr_rts(port); diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 4c10a9df3b91..9d3ce505e7ab 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -625,6 +625,14 @@ static void save_screen(struct vc_data *vc) vc->vc_sw->con_save_screen(vc); } +static void flush_scrollback(struct vc_data *vc) +{ + WARN_CONSOLE_UNLOCKED(); + + if (vc->vc_sw->con_flush_scrollback) + vc->vc_sw->con_flush_scrollback(vc); +} + /* * Redrawing of screen */ @@ -1171,6 +1179,7 @@ static void csi_J(struct vc_data *vc, int vpar) case 3: /* erase scroll-back buffer (and whole display) */ scr_memsetw(vc->vc_screenbuf, vc->vc_video_erase_char, vc->vc_screenbuf_size); + flush_scrollback(vc); set_origin(vc); if (con_is_visible(vc)) update_screen(vc); |