diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-22 23:17:25 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-22 23:17:25 +0300 |
commit | 37c85961c3f87f2141c84e53df31e59db072fd2e (patch) | |
tree | ff020df86c8c893e4524dda9205254be202560cc /drivers | |
parent | caa59428971d5ad81d19512365c9ba580d83268c (diff) | |
parent | d8a4995bcea168dfac8ee41c28c79109907e4fba (diff) | |
download | linux-37c85961c3f87f2141c84e53df31e59db072fd2e.tar.xz |
Merge tag 'tty-4.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial driver updates from Greg KH:
"Here is the big tty/serial driver patchset for 4.11-rc1.
Not much here, but a lot of little fixes and individual serial driver
updates all over the subsystem. Majority are for the sh-sci driver and
platform (the arch-specific changes have acks from the maintainer).
The start of the "serial bus" code is here as well, but nothing is
converted to use it yet. That work is still ongoing, hopefully will
start to show up across different subsystems for 4.12 (bluetooth is
one major place that will be used.)
All of these have been in linux-next for a while with no reported
issues"
* tag 'tty-4.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (109 commits)
tty: pl011: Work around QDF2400 E44 stuck BUSY bit
atmel_serial: Use the fractional divider when possible
tty: Remove extra include in HVC console tty framework
serial: exar: Enable MSI support
serial: exar: Move register defines from uapi header to consumer site
serial: pci: Remove unused pci_boards entries
serial: exar: Move Commtech adapters to 8250_exar as well
serial: exar: Fix feature control register constants
serial: exar: Fix initialization of EXAR registers for ports > 0
serial: exar: Fix mapping of port I/O resources
serial: sh-sci: fix hardware RX trigger level setting
tty/serial: atmel: ensure state is restored after suspending
serial: 8250_dw: Avoid "too much work" from bogus rx timeout interrupt
serdev: ttyport: check whether tty_init_dev() fails
serial: 8250_pci: make pciserial_detach_ports() static
ARM: dts: STiH410-b2260: Enable HW flow-control
ARM: dts: STiH407-family: Use new Pinctrl groups
ARM: dts: STiH407-pinctrl: Add Pinctrl group for HW flow-control
ARM: dts: STiH410-b2260: Identify the UART RTS line
dt-bindings: serial: Update 'uart-has-rtscts' description
...
Diffstat (limited to 'drivers')
72 files changed, 2583 insertions, 1329 deletions
diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c index b8019c4c1d38..2b5d0fac81f0 100644 --- a/drivers/acpi/spcr.c +++ b/drivers/acpi/spcr.c @@ -16,6 +16,26 @@ #include <linux/kernel.h> #include <linux/serial_core.h> +/* + * Some Qualcomm Datacenter Technologies SoCs have a defective UART BUSY bit. + * Detect them by examining the OEM fields in the SPCR header, similiar to PCI + * quirk detection in pci_mcfg.c. + */ +static bool qdf2400_erratum_44_present(struct acpi_table_header *h) +{ + if (memcmp(h->oem_id, "QCOM ", ACPI_OEM_ID_SIZE)) + return false; + + if (!memcmp(h->oem_table_id, "QDF2432 ", ACPI_OEM_TABLE_ID_SIZE)) + return true; + + if (!memcmp(h->oem_table_id, "QDF2400 ", ACPI_OEM_TABLE_ID_SIZE) && + h->oem_revision == 0) + return true; + + return false; +} + /** * parse_spcr() - parse ACPI SPCR table and add preferred console * @@ -93,6 +113,9 @@ int __init parse_spcr(bool earlycon) goto done; } + if (qdf2400_erratum_44_present(&table->header)) + uart = "qdf2400_e44"; + snprintf(opts, sizeof(opts), "%s,%s,0x%llx,%d", uart, iotype, table->serial_port.address, baud_rate); diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 4ee2a10207d0..31adbebf812e 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -46,6 +46,7 @@ config SGI_MBCS say Y or M here, otherwise say N. source "drivers/tty/serial/Kconfig" +source "drivers/tty/serdev/Kconfig" config TTY_PRINTK tristate "TTY driver to output user messages via printk" diff --git a/drivers/dma/hsu/pci.c b/drivers/dma/hsu/pci.c index 4875fa428e81..ad45cd344bba 100644 --- a/drivers/dma/hsu/pci.c +++ b/drivers/dma/hsu/pci.c @@ -23,15 +23,28 @@ #define HSU_PCI_CHAN_OFFSET 0x100 +#define PCI_DEVICE_ID_INTEL_MFLD_HSU_DMA 0x081e +#define PCI_DEVICE_ID_INTEL_MRFLD_HSU_DMA 0x1192 + static irqreturn_t hsu_pci_irq(int irq, void *dev) { struct hsu_dma_chip *chip = dev; + struct pci_dev *pdev = to_pci_dev(chip->dev); u32 dmaisr; u32 status; unsigned short i; int ret = 0; int err; + /* + * On Intel Tangier B0 and Anniedale the interrupt line, disregarding + * to have different numbers, is shared between HSU DMA and UART IPs. + * Thus on such SoCs we are expecting that IRQ handler is called in + * UART driver only. + */ + if (pdev->device == PCI_DEVICE_ID_INTEL_MRFLD_HSU_DMA) + return IRQ_HANDLED; + dmaisr = readl(chip->regs + HSU_PCI_DMAISR); for (i = 0; i < chip->hsu->nr_channels; i++) { if (dmaisr & 0x1) { @@ -113,8 +126,8 @@ static void hsu_pci_remove(struct pci_dev *pdev) } static const struct pci_device_id hsu_pci_id_table[] = { - { PCI_VDEVICE(INTEL, 0x081e), 0 }, - { PCI_VDEVICE(INTEL, 0x1192), 0 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MFLD_HSU_DMA), 0 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MRFLD_HSU_DMA), 0 }, { } }; MODULE_DEVICE_TABLE(pci, hsu_pci_id_table); 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); diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index c3f1fb9ee820..5b71bd905a60 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -43,9 +43,30 @@ config VGACON_SOFT_SCROLLBACK_SIZE range 1 1024 default "64" help - Enter the amount of System RAM to allocate for the scrollback - buffer. Each 64KB will give you approximately 16 80x25 - screenfuls of scrollback buffer + Enter the amount of System RAM to allocate for scrollback + buffers of VGA consoles. Each 64KB will give you approximately + 16 80x25 screenfuls of scrollback buffer. + +config VGACON_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT + bool "Persistent Scrollback History for each console by default" + depends on VGACON_SOFT_SCROLLBACK + default n + help + Say Y here if the scrollback history should persist by default when + switching between consoles. Otherwise, the scrollback history will be + flushed each time the console is switched. This feature can also be + enabled using the boot command line parameter + 'vgacon.scrollback_persistent=1'. + + This feature might break your tool of choice to flush the scrollback + buffer, e.g. clear(1) will work fine but Debian's clear_console(1) + will be broken, which might cause security issues. + You can use the escape sequence \e[3J instead if this feature is + activated. + + Note that a buffer of VGACON_SOFT_SCROLLBACK_SIZE is taken for each + created tty device. + So if you use a RAM-constrained system, say N here. config MDA_CONSOLE depends on !M68K && !PARISC && ISA diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index c22a56232b7c..dc06cb6a15dc 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -162,70 +162,118 @@ static inline void vga_set_mem_top(struct vc_data *c) #ifdef CONFIG_VGACON_SOFT_SCROLLBACK /* software scrollback */ -static void *vgacon_scrollback; -static int vgacon_scrollback_tail; -static int vgacon_scrollback_size; -static int vgacon_scrollback_rows; -static int vgacon_scrollback_cnt; -static int vgacon_scrollback_cur; -static int vgacon_scrollback_save; -static int vgacon_scrollback_restore; - -static void vgacon_scrollback_init(int pitch) +struct vgacon_scrollback_info { + void *data; + int tail; + int size; + int rows; + int cnt; + int cur; + int save; + int restore; +}; + +static struct vgacon_scrollback_info *vgacon_scrollback_cur; +static struct vgacon_scrollback_info vgacon_scrollbacks[MAX_NR_CONSOLES]; +static bool scrollback_persistent = \ + IS_ENABLED(CONFIG_VGACON_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT); +module_param_named(scrollback_persistent, scrollback_persistent, bool, 0000); +MODULE_PARM_DESC(scrollback_persistent, "Enable persistent scrollback for all vga consoles"); + +static void vgacon_scrollback_reset(int vc_num, size_t reset_size) +{ + struct vgacon_scrollback_info *scrollback = &vgacon_scrollbacks[vc_num]; + + if (scrollback->data && reset_size > 0) + memset(scrollback->data, 0, reset_size); + + scrollback->cnt = 0; + scrollback->tail = 0; + scrollback->cur = 0; +} + +static void vgacon_scrollback_init(int vc_num) +{ + int pitch = vga_video_num_columns * 2; + size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024; + int rows = size / pitch; + void *data; + + data = kmalloc_array(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE, 1024, + GFP_NOWAIT); + + vgacon_scrollbacks[vc_num].data = data; + vgacon_scrollback_cur = &vgacon_scrollbacks[vc_num]; + + vgacon_scrollback_cur->rows = rows - 1; + vgacon_scrollback_cur->size = rows * pitch; + + vgacon_scrollback_reset(vc_num, size); +} + +static void vgacon_scrollback_switch(int vc_num) { - int rows = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024/pitch; - - if (vgacon_scrollback) { - vgacon_scrollback_cnt = 0; - vgacon_scrollback_tail = 0; - vgacon_scrollback_cur = 0; - vgacon_scrollback_rows = rows - 1; - vgacon_scrollback_size = rows * pitch; + if (!scrollback_persistent) + vc_num = 0; + + if (!vgacon_scrollbacks[vc_num].data) { + vgacon_scrollback_init(vc_num); + } else { + if (scrollback_persistent) { + vgacon_scrollback_cur = &vgacon_scrollbacks[vc_num]; + } else { + size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024; + + vgacon_scrollback_reset(vc_num, size); + } } } static void vgacon_scrollback_startup(void) { - vgacon_scrollback = kcalloc(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE, 1024, GFP_NOWAIT); - vgacon_scrollback_init(vga_video_num_columns * 2); + vgacon_scrollback_cur = &vgacon_scrollbacks[0]; + vgacon_scrollback_init(0); } static void vgacon_scrollback_update(struct vc_data *c, int t, int count) { void *p; - if (!vgacon_scrollback_size || c->vc_num != fg_console) + if (!vgacon_scrollback_cur->data || !vgacon_scrollback_cur->size || + c->vc_num != fg_console) return; p = (void *) (c->vc_origin + t * c->vc_size_row); while (count--) { - scr_memcpyw(vgacon_scrollback + vgacon_scrollback_tail, + scr_memcpyw(vgacon_scrollback_cur->data + + vgacon_scrollback_cur->tail, p, c->vc_size_row); - vgacon_scrollback_cnt++; + + vgacon_scrollback_cur->cnt++; p += c->vc_size_row; - vgacon_scrollback_tail += c->vc_size_row; + vgacon_scrollback_cur->tail += c->vc_size_row; - if (vgacon_scrollback_tail >= vgacon_scrollback_size) - vgacon_scrollback_tail = 0; + if (vgacon_scrollback_cur->tail >= vgacon_scrollback_cur->size) + vgacon_scrollback_cur->tail = 0; - if (vgacon_scrollback_cnt > vgacon_scrollback_rows) - vgacon_scrollback_cnt = vgacon_scrollback_rows; + if (vgacon_scrollback_cur->cnt > vgacon_scrollback_cur->rows) + vgacon_scrollback_cur->cnt = vgacon_scrollback_cur->rows; - vgacon_scrollback_cur = vgacon_scrollback_cnt; + vgacon_scrollback_cur->cur = vgacon_scrollback_cur->cnt; } } static void vgacon_restore_screen(struct vc_data *c) { - vgacon_scrollback_save = 0; + vgacon_scrollback_cur->save = 0; - if (!vga_is_gfx && !vgacon_scrollback_restore) { + if (!vga_is_gfx && !vgacon_scrollback_cur->restore) { scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf, c->vc_screenbuf_size > vga_vram_size ? vga_vram_size : c->vc_screenbuf_size); - vgacon_scrollback_restore = 1; - vgacon_scrollback_cur = vgacon_scrollback_cnt; + vgacon_scrollback_cur->restore = 1; + vgacon_scrollback_cur->cur = vgacon_scrollback_cur->cnt; } } @@ -239,41 +287,41 @@ static void vgacon_scrolldelta(struct vc_data *c, int lines) return; } - if (!vgacon_scrollback) + if (!vgacon_scrollback_cur->data) return; - if (!vgacon_scrollback_save) { + if (!vgacon_scrollback_cur->save) { vgacon_cursor(c, CM_ERASE); vgacon_save_screen(c); - vgacon_scrollback_save = 1; + vgacon_scrollback_cur->save = 1; } - vgacon_scrollback_restore = 0; - start = vgacon_scrollback_cur + lines; + vgacon_scrollback_cur->restore = 0; + start = vgacon_scrollback_cur->cur + lines; end = start + abs(lines); if (start < 0) start = 0; - if (start > vgacon_scrollback_cnt) - start = vgacon_scrollback_cnt; + if (start > vgacon_scrollback_cur->cnt) + start = vgacon_scrollback_cur->cnt; if (end < 0) end = 0; - if (end > vgacon_scrollback_cnt) - end = vgacon_scrollback_cnt; + if (end > vgacon_scrollback_cur->cnt) + end = vgacon_scrollback_cur->cnt; - vgacon_scrollback_cur = start; + vgacon_scrollback_cur->cur = start; count = end - start; - soff = vgacon_scrollback_tail - ((vgacon_scrollback_cnt - end) * - c->vc_size_row); + soff = vgacon_scrollback_cur->tail - + ((vgacon_scrollback_cur->cnt - end) * c->vc_size_row); soff -= count * c->vc_size_row; if (soff < 0) - soff += vgacon_scrollback_size; + soff += vgacon_scrollback_cur->size; - count = vgacon_scrollback_cnt - start; + count = vgacon_scrollback_cur->cnt - start; if (count > c->vc_rows) count = c->vc_rows; @@ -287,13 +335,13 @@ static void vgacon_scrolldelta(struct vc_data *c, int lines) count *= c->vc_size_row; /* how much memory to end of buffer left? */ - copysize = min(count, vgacon_scrollback_size - soff); - scr_memcpyw(d, vgacon_scrollback + soff, copysize); + copysize = min(count, vgacon_scrollback_cur->size - soff); + scr_memcpyw(d, vgacon_scrollback_cur->data + soff, copysize); d += copysize; count -= copysize; if (count) { - scr_memcpyw(d, vgacon_scrollback, count); + scr_memcpyw(d, vgacon_scrollback_cur->data, count); d += count; } @@ -302,10 +350,18 @@ static void vgacon_scrolldelta(struct vc_data *c, int lines) } else vgacon_cursor(c, CM_MOVE); } + +static void vgacon_flush_scrollback(struct vc_data *c) +{ + size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024; + + vgacon_scrollback_reset(c->vc_num, size); +} #else #define vgacon_scrollback_startup(...) do { } while (0) #define vgacon_scrollback_init(...) do { } while (0) #define vgacon_scrollback_update(...) do { } while (0) +#define vgacon_scrollback_switch(...) do { } while (0) static void vgacon_restore_screen(struct vc_data *c) { @@ -319,6 +375,10 @@ static void vgacon_scrolldelta(struct vc_data *c, int lines) vga_vram_size); vga_set_mem_top(c); } + +static void vgacon_flush_scrollback(struct vc_data *c) +{ +} #endif /* CONFIG_VGACON_SOFT_SCROLLBACK */ static const char *vgacon_startup(void) @@ -780,7 +840,7 @@ static int vgacon_switch(struct vc_data *c) vgacon_doresize(c, c->vc_cols, c->vc_rows); } - vgacon_scrollback_init(c->vc_size_row); + vgacon_scrollback_switch(c->vc_num); return 0; /* Redrawing not needed */ } @@ -1326,7 +1386,6 @@ static bool vgacon_scroll(struct vc_data *c, unsigned int t, unsigned int b, return true; } - /* * The console `switch' structure for the VGA based console */ @@ -1359,6 +1418,7 @@ const struct consw vga_con = { .con_save_screen = vgacon_save_screen, .con_build_attr = vgacon_build_attr, .con_invert_region = vgacon_invert_region, + .con_flush_scrollback = vgacon_flush_scrollback, }; EXPORT_SYMBOL(vga_con); |