summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/8250
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/8250')
-rw-r--r--drivers/tty/serial/8250/8250_core.c26
-rw-r--r--drivers/tty/serial/8250/8250_dma.c6
-rw-r--r--drivers/tty/serial/8250/8250_dw.c288
-rw-r--r--drivers/tty/serial/8250/8250_early.c4
-rw-r--r--drivers/tty/serial/8250/8250_ingenic.c88
-rw-r--r--drivers/tty/serial/8250/8250_mid.c326
-rw-r--r--drivers/tty/serial/8250/8250_omap.c8
-rw-r--r--drivers/tty/serial/8250/8250_pci.c229
-rw-r--r--drivers/tty/serial/8250/8250_port.c86
-rw-r--r--drivers/tty/serial/8250/Kconfig25
-rw-r--r--drivers/tty/serial/8250/Makefile1
11 files changed, 646 insertions, 441 deletions
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 271d12137649..39126460c1f5 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -569,6 +569,9 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev)
for (i = 0; i < nr_uarts; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
+ if (up->port.type == PORT_8250_CIR)
+ continue;
+
if (up->port.dev)
continue;
@@ -1027,13 +1030,24 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
if (up->dl_write)
uart->dl_write = up->dl_write;
- if (serial8250_isa_config != NULL)
- serial8250_isa_config(0, &uart->port,
- &uart->capabilities);
+ if (uart->port.type != PORT_8250_CIR) {
+ if (serial8250_isa_config != NULL)
+ serial8250_isa_config(0, &uart->port,
+ &uart->capabilities);
+
+ ret = uart_add_one_port(&serial8250_reg,
+ &uart->port);
+ if (ret == 0)
+ ret = uart->port.line;
+ } else {
+ dev_info(uart->port.dev,
+ "skipping CIR port at 0x%lx / 0x%llx, IRQ %d\n",
+ uart->port.iobase,
+ (unsigned long long)uart->port.mapbase,
+ uart->port.irq);
- ret = uart_add_one_port(&serial8250_reg, &uart->port);
- if (ret == 0)
- ret = uart->port.line;
+ ret = 0;
+ }
}
mutex_unlock(&serial_mutex);
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index e508939daea3..78259d3c6a55 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -54,9 +54,6 @@ static void __dma_rx_complete(void *param)
struct dma_tx_state state;
int count;
- dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr,
- dma->rx_size, DMA_FROM_DEVICE);
-
dma->rx_running = 0;
dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
@@ -152,9 +149,6 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
dma->rx_cookie = dmaengine_submit(desc);
- dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr,
- dma->rx_size, DMA_FROM_DEVICE);
-
dma_async_issue_pending(dma->rxchan);
return 0;
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 06324f17a0cb..a0cdbf35dcb1 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -63,6 +63,9 @@ struct dw8250_data {
struct clk *pclk;
struct reset_control *rst;
struct uart_8250_dma dma;
+
+ unsigned int skip_autocfg:1;
+ unsigned int uart_16550_compatible:1;
};
#define BYT_PRV_CLK 0x800
@@ -244,24 +247,77 @@ out:
serial8250_do_set_termios(p, termios, old);
}
-static bool dw8250_dma_filter(struct dma_chan *chan, void *param)
+/*
+ * dw8250_fallback_dma_filter will prevent the UART from getting just any free
+ * channel on platforms that have DMA engines, but don't have any channels
+ * assigned to the UART.
+ *
+ * REVISIT: This is a work around for limitation in the DMA Engine API. Once the
+ * core problem is fixed, this function is no longer needed.
+ */
+static bool dw8250_fallback_dma_filter(struct dma_chan *chan, void *param)
{
return false;
}
-static void dw8250_setup_port(struct uart_8250_port *up)
+static bool dw8250_idma_filter(struct dma_chan *chan, void *param)
+{
+ return param == chan->device->dev->parent;
+}
+
+static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
{
- struct uart_port *p = &up->port;
- u32 reg = readl(p->membase + DW_UART_UCV);
+ if (p->dev->of_node) {
+ struct device_node *np = p->dev->of_node;
+ int id;
+
+ /* get index of serial line, if found in DT aliases */
+ id = of_alias_get_id(np, "serial");
+ if (id >= 0)
+ p->line = id;
+#ifdef CONFIG_64BIT
+ if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) {
+ p->serial_in = dw8250_serial_inq;
+ p->serial_out = dw8250_serial_outq;
+ p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
+ p->type = PORT_OCTEON;
+ data->usr_reg = 0x27;
+ data->skip_autocfg = true;
+ }
+#endif
+ } else if (has_acpi_companion(p->dev)) {
+ p->iotype = UPIO_MEM32;
+ p->regshift = 2;
+ p->serial_in = dw8250_serial_in32;
+ p->set_termios = dw8250_set_termios;
+ /* So far none of there implement the Busy Functionality */
+ data->uart_16550_compatible = true;
+ }
+
+ /* 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;
+ }
+}
+
+static void dw8250_setup_port(struct uart_port *p)
+{
+ struct uart_8250_port *up = up_to_u8250p(p);
+ u32 reg;
/*
* If the Component Version Register returns zero, we know that
* ADDITIONAL_FEATURES are not enabled. No need to go any further.
*/
+ reg = readl(p->membase + DW_UART_UCV);
if (!reg)
return;
- dev_dbg_ratelimited(p->dev, "Designware UART version %c.%c%c\n",
+ dev_dbg(p->dev, "Designware UART version %c.%c%c\n",
(reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff);
reg = readl(p->membase + DW_UART_CPR);
@@ -273,7 +329,6 @@ static void dw8250_setup_port(struct uart_8250_port *up)
p->type = PORT_16550A;
p->flags |= UPF_FIXED_TYPE;
p->fifosize = DW_UART_CPR_FIFO_SIZE(reg);
- up->tx_loadsz = p->fifosize;
up->capabilities = UART_CAP_FIFO;
}
@@ -281,166 +336,91 @@ static void dw8250_setup_port(struct uart_8250_port *up)
up->capabilities |= UART_CAP_AFE;
}
-static int dw8250_probe_of(struct uart_port *p,
- struct dw8250_data *data)
+static int dw8250_probe(struct platform_device *pdev)
{
- struct device_node *np = p->dev->of_node;
- struct uart_8250_port *up = up_to_u8250p(p);
- u32 val;
- bool has_ucv = true;
- int id;
+ struct uart_8250_port uart = {};
+ struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int irq = platform_get_irq(pdev, 0);
+ struct uart_port *p = &uart.port;
+ struct dw8250_data *data;
+ int err;
+ u32 val;
-#ifdef CONFIG_64BIT
- if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) {
- p->serial_in = dw8250_serial_inq;
- p->serial_out = dw8250_serial_outq;
- p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
- p->type = PORT_OCTEON;
- data->usr_reg = 0x27;
- has_ucv = false;
- } else
-#endif
- if (!of_property_read_u32(np, "reg-io-width", &val)) {
- switch (val) {
- case 1:
- break;
- case 4:
- p->iotype = UPIO_MEM32;
- p->serial_in = dw8250_serial_in32;
- p->serial_out = dw8250_serial_out32;
- break;
- default:
- dev_err(p->dev, "unsupported reg-io-width (%u)\n", val);
- return -EINVAL;
- }
+ if (!regs) {
+ dev_err(&pdev->dev, "no registers defined\n");
+ return -EINVAL;
}
- if (has_ucv)
- dw8250_setup_port(up);
- /* if we have a valid fifosize, try hooking up DMA here */
- if (p->fifosize) {
- up->dma = &data->dma;
-
- up->dma->rxconf.src_maxburst = p->fifosize / 4;
- up->dma->txconf.dst_maxburst = p->fifosize / 4;
+ if (irq < 0) {
+ if (irq != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "cannot get irq\n");
+ return irq;
}
- if (!of_property_read_u32(np, "reg-shift", &val))
+ spin_lock_init(&p->lock);
+ p->mapbase = regs->start;
+ p->irq = irq;
+ p->handle_irq = dw8250_handle_irq;
+ p->pm = dw8250_do_pm;
+ p->type = PORT_8250;
+ p->flags = UPF_SHARE_IRQ | UPF_FIXED_PORT;
+ p->dev = &pdev->dev;
+ p->iotype = UPIO_MEM;
+ p->serial_in = dw8250_serial_in;
+ p->serial_out = dw8250_serial_out;
+
+ p->membase = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
+ if (!p->membase)
+ return -ENOMEM;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dma.fn = dw8250_fallback_dma_filter;
+ data->usr_reg = DW_UART_USR;
+ p->private_data = data;
+
+ data->uart_16550_compatible = device_property_read_bool(p->dev,
+ "snps,uart-16550-compatible");
+
+ err = device_property_read_u32(p->dev, "reg-shift", &val);
+ if (!err)
p->regshift = val;
- /* get index of serial line, if found in DT aliases */
- id = of_alias_get_id(np, "serial");
- if (id >= 0)
- p->line = id;
+ err = device_property_read_u32(p->dev, "reg-io-width", &val);
+ if (!err && val == 4) {
+ p->iotype = UPIO_MEM32;
+ p->serial_in = dw8250_serial_in32;
+ p->serial_out = dw8250_serial_out32;
+ }
- if (of_property_read_bool(np, "dcd-override")) {
+ if (device_property_read_bool(p->dev, "dcd-override")) {
/* Always report DCD as active */
data->msr_mask_on |= UART_MSR_DCD;
data->msr_mask_off |= UART_MSR_DDCD;
}
- if (of_property_read_bool(np, "dsr-override")) {
+ if (device_property_read_bool(p->dev, "dsr-override")) {
/* Always report DSR as active */
data->msr_mask_on |= UART_MSR_DSR;
data->msr_mask_off |= UART_MSR_DDSR;
}
- if (of_property_read_bool(np, "cts-override")) {
+ if (device_property_read_bool(p->dev, "cts-override")) {
/* Always report CTS as active */
data->msr_mask_on |= UART_MSR_CTS;
data->msr_mask_off |= UART_MSR_DCTS;
}
- if (of_property_read_bool(np, "ri-override")) {
+ if (device_property_read_bool(p->dev, "ri-override")) {
/* Always report Ring indicator as inactive */
data->msr_mask_off |= UART_MSR_RI;
data->msr_mask_off |= UART_MSR_TERI;
}
- return 0;
-}
-
-static bool dw8250_idma_filter(struct dma_chan *chan, void *param)
-{
- struct device *dev = param;
-
- if (dev != chan->device->dev->parent)
- return false;
-
- return true;
-}
-
-static int dw8250_probe_acpi(struct uart_8250_port *up,
- struct dw8250_data *data)
-{
- struct uart_port *p = &up->port;
-
- dw8250_setup_port(up);
-
- p->iotype = UPIO_MEM32;
- p->serial_in = dw8250_serial_in32;
- p->serial_out = dw8250_serial_out32;
- p->regshift = 2;
-
- /* Platforms with iDMA */
- if (platform_get_resource_byname(to_platform_device(up->port.dev),
- IORESOURCE_MEM, "lpss_priv")) {
- data->dma.rx_param = up->port.dev->parent;
- data->dma.tx_param = up->port.dev->parent;
- data->dma.fn = dw8250_idma_filter;
- }
-
- up->dma = &data->dma;
- up->dma->rxconf.src_maxburst = p->fifosize / 4;
- up->dma->txconf.dst_maxburst = p->fifosize / 4;
-
- up->port.set_termios = dw8250_set_termios;
-
- return 0;
-}
-
-static int dw8250_probe(struct platform_device *pdev)
-{
- struct uart_8250_port uart = {};
- struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- int irq = platform_get_irq(pdev, 0);
- struct dw8250_data *data;
- int err;
-
- if (!regs) {
- dev_err(&pdev->dev, "no registers defined\n");
- return -EINVAL;
- }
-
- if (irq < 0) {
- if (irq != -EPROBE_DEFER)
- dev_err(&pdev->dev, "cannot get irq\n");
- return irq;
- }
-
- spin_lock_init(&uart.port.lock);
- uart.port.mapbase = regs->start;
- uart.port.irq = irq;
- uart.port.handle_irq = dw8250_handle_irq;
- uart.port.pm = dw8250_do_pm;
- uart.port.type = PORT_8250;
- uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT;
- uart.port.dev = &pdev->dev;
-
- uart.port.membase = devm_ioremap(&pdev->dev, regs->start,
- resource_size(regs));
- if (!uart.port.membase)
- return -ENOMEM;
-
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- data->usr_reg = DW_UART_USR;
-
/* Always ask for fixed clock rate from a property. */
- device_property_read_u32(&pdev->dev, "clock-frequency",
- &uart.port.uartclk);
+ device_property_read_u32(p->dev, "clock-frequency", &p->uartclk);
/* If there is separate baudclk, get the rate from it. */
data->clk = devm_clk_get(&pdev->dev, "baudclk");
@@ -454,11 +434,11 @@ static int dw8250_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "could not enable optional baudclk: %d\n",
err);
else
- uart.port.uartclk = clk_get_rate(data->clk);
+ p->uartclk = clk_get_rate(data->clk);
}
/* If no clock rate is defined, fail. */
- if (!uart.port.uartclk) {
+ if (!p->uartclk) {
dev_err(&pdev->dev, "clock rate not defined\n");
return -EINVAL;
}
@@ -484,26 +464,22 @@ static int dw8250_probe(struct platform_device *pdev)
if (!IS_ERR(data->rst))
reset_control_deassert(data->rst);
- data->dma.rx_param = data;
- data->dma.tx_param = data;
- data->dma.fn = dw8250_dma_filter;
+ dw8250_quirks(p, data);
- uart.port.iotype = UPIO_MEM;
- uart.port.serial_in = dw8250_serial_in;
- uart.port.serial_out = dw8250_serial_out;
- uart.port.private_data = data;
+ /* If the Busy Functionality is not implemented, don't handle it */
+ if (data->uart_16550_compatible) {
+ p->serial_out = NULL;
+ p->handle_irq = NULL;
+ }
- if (pdev->dev.of_node) {
- err = dw8250_probe_of(&uart.port, data);
- if (err)
- goto err_reset;
- } else if (ACPI_HANDLE(&pdev->dev)) {
- err = dw8250_probe_acpi(&uart, data);
- if (err)
- goto err_reset;
- } else {
- err = -ENODEV;
- goto err_reset;
+ if (!data->skip_autocfg)
+ dw8250_setup_port(p);
+
+ /* If we have a valid fifosize, try hooking up DMA */
+ if (p->fifosize) {
+ data->dma.rxconf.src_maxburst = p->fifosize / 4;
+ data->dma.txconf.dst_maxburst = p->fifosize / 4;
+ uart.dma = &data->dma;
}
data->line = serial8250_register_8250_port(&uart);
diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index faed05f25bc2..ceb85792a5cf 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -29,6 +29,8 @@
#include <linux/tty.h>
#include <linux/init.h>
#include <linux/console.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/serial_reg.h>
#include <linux/serial.h>
#include <linux/serial_8250.h>
@@ -152,3 +154,5 @@ int __init early_serial8250_setup(struct earlycon_device *device,
}
EARLYCON_DECLARE(uart8250, early_serial8250_setup);
EARLYCON_DECLARE(uart, early_serial8250_setup);
+OF_EARLYCON_DECLARE(ns16550, "ns16550", early_serial8250_setup);
+OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup);
diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c
index 7c1e4be48e7b..49394b4c5cfd 100644
--- a/drivers/tty/serial/8250/8250_ingenic.c
+++ b/drivers/tty/serial/8250/8250_ingenic.c
@@ -21,19 +21,33 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/serial_8250.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
+#include "8250.h"
+
+/** ingenic_uart_config: SOC specific config data. */
+struct ingenic_uart_config {
+ int tx_loadsz;
+ int fifosize;
+};
+
struct ingenic_uart_data {
struct clk *clk_module;
struct clk *clk_baud;
int line;
};
+static const struct of_device_id of_match[];
+
#define UART_FCR_UME BIT(4)
+#define UART_MCR_MDCE BIT(7)
+#define UART_MCR_FCM BIT(6)
+
static struct earlycon_device *early_device;
static uint8_t __init early_in(struct uart_port *port, int offset)
@@ -129,6 +143,8 @@ OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart",
static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value)
{
+ int ier;
+
switch (offset) {
case UART_FCR:
/* UART module enable */
@@ -136,9 +152,22 @@ static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value)
break;
case UART_IER:
+ /* Enable receive timeout interrupt with the
+ * receive line status interrupt */
value |= (value & 0x4) << 2;
break;
+ case UART_MCR:
+ /* If we have enabled modem status IRQs we should enable modem
+ * mode. */
+ ier = p->serial_in(p, UART_IER);
+
+ if (ier & UART_IER_MSI)
+ value |= UART_MCR_MDCE | UART_MCR_FCM;
+ else
+ value &= ~(UART_MCR_MDCE | UART_MCR_FCM);
+ break;
+
default:
break;
}
@@ -146,14 +175,45 @@ static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value)
writeb(value, p->membase + (offset << p->regshift));
}
+static unsigned int ingenic_uart_serial_in(struct uart_port *p, int offset)
+{
+ unsigned int value;
+
+ value = readb(p->membase + (offset << p->regshift));
+
+ /* Hide non-16550 compliant bits from higher levels */
+ switch (offset) {
+ case UART_FCR:
+ value &= ~UART_FCR_UME;
+ break;
+
+ case UART_MCR:
+ value &= ~(UART_MCR_MDCE | UART_MCR_FCM);
+ break;
+
+ default:
+ break;
+ }
+ return value;
+}
+
static int ingenic_uart_probe(struct platform_device *pdev)
{
struct uart_8250_port uart = {};
struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
struct ingenic_uart_data *data;
+ const struct ingenic_uart_config *cdata;
+ const struct of_device_id *match;
int err, line;
+ match = of_match_device(of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+ cdata = match->data;
+
if (!regs || !irq) {
dev_err(&pdev->dev, "no registers/irq defined\n");
return -EINVAL;
@@ -164,14 +224,18 @@ static int ingenic_uart_probe(struct platform_device *pdev)
return -ENOMEM;
spin_lock_init(&uart.port.lock);
- uart.port.type = PORT_16550;
+ uart.port.type = PORT_16550A;
uart.port.flags = UPF_SKIP_TEST | UPF_IOREMAP | UPF_FIXED_TYPE;
uart.port.iotype = UPIO_MEM;
uart.port.mapbase = regs->start;
uart.port.regshift = 2;
uart.port.serial_out = ingenic_uart_serial_out;
+ uart.port.serial_in = ingenic_uart_serial_in;
uart.port.irq = irq->start;
uart.port.dev = &pdev->dev;
+ uart.port.fifosize = cdata->fifosize;
+ uart.tx_loadsz = cdata->tx_loadsz;
+ uart.capabilities = UART_CAP_FIFO | UART_CAP_RTOIE;
/* Check for a fixed line number */
line = of_alias_get_id(pdev->dev.of_node, "serial");
@@ -241,10 +305,26 @@ static int ingenic_uart_remove(struct platform_device *pdev)
return 0;
}
+static const struct ingenic_uart_config jz4740_uart_config = {
+ .tx_loadsz = 8,
+ .fifosize = 16,
+};
+
+static const struct ingenic_uart_config jz4760_uart_config = {
+ .tx_loadsz = 16,
+ .fifosize = 32,
+};
+
+static const struct ingenic_uart_config jz4780_uart_config = {
+ .tx_loadsz = 32,
+ .fifosize = 64,
+};
+
static const struct of_device_id of_match[] = {
- { .compatible = "ingenic,jz4740-uart" },
- { .compatible = "ingenic,jz4775-uart" },
- { .compatible = "ingenic,jz4780-uart" },
+ { .compatible = "ingenic,jz4740-uart", .data = &jz4740_uart_config },
+ { .compatible = "ingenic,jz4760-uart", .data = &jz4760_uart_config },
+ { .compatible = "ingenic,jz4775-uart", .data = &jz4760_uart_config },
+ { .compatible = "ingenic,jz4780-uart", .data = &jz4780_uart_config },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_match);
diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c
new file mode 100644
index 000000000000..88531a36b69c
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_mid.c
@@ -0,0 +1,326 @@
+/*
+ * 8250_mid.c - Driver for UART on Intel Penwell and various other Intel SOCs
+ *
+ * Copyright (C) 2015 Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/rational.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include <linux/dma/hsu.h>
+
+#include "8250.h"
+
+#define PCI_DEVICE_ID_INTEL_PNW_UART1 0x081b
+#define PCI_DEVICE_ID_INTEL_PNW_UART2 0x081c
+#define PCI_DEVICE_ID_INTEL_PNW_UART3 0x081d
+#define PCI_DEVICE_ID_INTEL_TNG_UART 0x1191
+#define PCI_DEVICE_ID_INTEL_DNV_UART 0x19d8
+
+/* Intel MID Specific registers */
+#define INTEL_MID_UART_PS 0x30
+#define INTEL_MID_UART_MUL 0x34
+#define INTEL_MID_UART_DIV 0x38
+
+struct mid8250;
+
+struct mid8250_board {
+ unsigned long freq;
+ unsigned int base_baud;
+ int (*setup)(struct mid8250 *, struct uart_port *p);
+ void (*exit)(struct mid8250 *);
+};
+
+struct mid8250 {
+ int line;
+ int dma_index;
+ struct pci_dev *dma_dev;
+ struct uart_8250_dma dma;
+ struct mid8250_board *board;
+ struct hsu_dma_chip dma_chip;
+};
+
+/*****************************************************************************/
+
+static int pnw_setup(struct mid8250 *mid, struct uart_port *p)
+{
+ struct pci_dev *pdev = to_pci_dev(p->dev);
+
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_INTEL_PNW_UART1:
+ mid->dma_index = 0;
+ break;
+ case PCI_DEVICE_ID_INTEL_PNW_UART2:
+ mid->dma_index = 1;
+ break;
+ case PCI_DEVICE_ID_INTEL_PNW_UART3:
+ mid->dma_index = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mid->dma_dev = pci_get_slot(pdev->bus,
+ PCI_DEVFN(PCI_SLOT(pdev->devfn), 3));
+ return 0;
+}
+
+static int tng_setup(struct mid8250 *mid, struct uart_port *p)
+{
+ struct pci_dev *pdev = to_pci_dev(p->dev);
+ int index = PCI_FUNC(pdev->devfn);
+
+ /* Currently no support for HSU port0 */
+ if (index-- == 0)
+ return -ENODEV;
+
+ mid->dma_index = index;
+ mid->dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(5, 0));
+ return 0;
+}
+
+static int dnv_handle_irq(struct uart_port *p)
+{
+ struct mid8250 *mid = p->private_data;
+ int ret;
+
+ ret = hsu_dma_irq(&mid->dma_chip, 0);
+ ret |= hsu_dma_irq(&mid->dma_chip, 1);
+
+ /* For now, letting the HW generate separate interrupt for the UART */
+ if (ret)
+ return ret;
+
+ return serial8250_handle_irq(p, serial_port_in(p, UART_IIR));
+}
+
+#define DNV_DMA_CHAN_OFFSET 0x80
+
+static int dnv_setup(struct mid8250 *mid, struct uart_port *p)
+{
+ struct hsu_dma_chip *chip = &mid->dma_chip;
+ struct pci_dev *pdev = to_pci_dev(p->dev);
+ int ret;
+
+ chip->dev = &pdev->dev;
+ chip->irq = pdev->irq;
+ chip->regs = p->membase;
+ chip->length = pci_resource_len(pdev, 0);
+ chip->offset = DNV_DMA_CHAN_OFFSET;
+
+ /* Falling back to PIO mode if DMA probing fails */
+ ret = hsu_dma_probe(chip);
+ if (ret)
+ return 0;
+
+ mid->dma_dev = pdev;
+
+ p->handle_irq = dnv_handle_irq;
+ return 0;
+}
+
+static void dnv_exit(struct mid8250 *mid)
+{
+ if (!mid->dma_dev)
+ return;
+ hsu_dma_remove(&mid->dma_chip);
+}
+
+/*****************************************************************************/
+
+static void mid8250_set_termios(struct uart_port *p,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ unsigned int baud = tty_termios_baud_rate(termios);
+ struct mid8250 *mid = p->private_data;
+ unsigned short ps = 16;
+ unsigned long fuart = baud * ps;
+ unsigned long w = BIT(24) - 1;
+ unsigned long mul, div;
+
+ if (mid->board->freq < fuart) {
+ /* Find prescaler value that satisfies Fuart < Fref */
+ if (mid->board->freq > baud)
+ ps = mid->board->freq / baud; /* baud rate too high */
+ else
+ ps = 1; /* PLL case */
+ fuart = baud * ps;
+ } else {
+ /* Get Fuart closer to Fref */
+ fuart *= rounddown_pow_of_two(mid->board->freq / fuart);
+ }
+
+ rational_best_approximation(fuart, mid->board->freq, w, w, &mul, &div);
+ p->uartclk = fuart * 16 / ps; /* core uses ps = 16 always */
+
+ writel(ps, p->membase + INTEL_MID_UART_PS); /* set PS */
+ writel(mul, p->membase + INTEL_MID_UART_MUL); /* set MUL */
+ writel(div, p->membase + INTEL_MID_UART_DIV);
+
+ serial8250_do_set_termios(p, termios, old);
+}
+
+static bool mid8250_dma_filter(struct dma_chan *chan, void *param)
+{
+ struct hsu_dma_slave *s = param;
+
+ if (s->dma_dev != chan->device->dev || s->chan_id != chan->chan_id)
+ return false;
+
+ chan->private = s;
+ return true;
+}
+
+static int mid8250_dma_setup(struct mid8250 *mid, struct uart_8250_port *port)
+{
+ struct uart_8250_dma *dma = &mid->dma;
+ struct device *dev = port->port.dev;
+ struct hsu_dma_slave *rx_param;
+ struct hsu_dma_slave *tx_param;
+
+ if (!mid->dma_dev)
+ return 0;
+
+ rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL);
+ if (!rx_param)
+ return -ENOMEM;
+
+ tx_param = devm_kzalloc(dev, sizeof(*tx_param), GFP_KERNEL);
+ if (!tx_param)
+ return -ENOMEM;
+
+ rx_param->chan_id = mid->dma_index * 2 + 1;
+ tx_param->chan_id = mid->dma_index * 2;
+
+ dma->rxconf.src_maxburst = 64;
+ dma->txconf.dst_maxburst = 64;
+
+ rx_param->dma_dev = &mid->dma_dev->dev;
+ tx_param->dma_dev = &mid->dma_dev->dev;
+
+ dma->fn = mid8250_dma_filter;
+ dma->rx_param = rx_param;
+ dma->tx_param = tx_param;
+
+ port->dma = dma;
+ return 0;
+}
+
+static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct uart_8250_port uart;
+ struct mid8250 *mid;
+ int ret;
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ pci_set_master(pdev);
+
+ mid = devm_kzalloc(&pdev->dev, sizeof(*mid), GFP_KERNEL);
+ if (!mid)
+ return -ENOMEM;
+
+ mid->board = (struct mid8250_board *)id->driver_data;
+
+ memset(&uart, 0, sizeof(struct uart_8250_port));
+
+ uart.port.dev = &pdev->dev;
+ uart.port.irq = pdev->irq;
+ uart.port.private_data = mid;
+ uart.port.type = PORT_16750;
+ uart.port.iotype = UPIO_MEM;
+ uart.port.uartclk = mid->board->base_baud * 16;
+ uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE;
+ uart.port.set_termios = mid8250_set_termios;
+
+ uart.port.mapbase = pci_resource_start(pdev, 0);
+ uart.port.membase = pcim_iomap(pdev, 0, 0);
+ if (!uart.port.membase)
+ return -ENOMEM;
+
+ if (mid->board->setup) {
+ ret = mid->board->setup(mid, &uart.port);
+ if (ret)
+ return ret;
+ }
+
+ ret = mid8250_dma_setup(mid, &uart);
+ if (ret)
+ goto err;
+
+ ret = serial8250_register_8250_port(&uart);
+ if (ret < 0)
+ goto err;
+
+ mid->line = ret;
+
+ pci_set_drvdata(pdev, mid);
+ return 0;
+err:
+ if (mid->board->exit)
+ mid->board->exit(mid);
+ return ret;
+}
+
+static void mid8250_remove(struct pci_dev *pdev)
+{
+ struct mid8250 *mid = pci_get_drvdata(pdev);
+
+ if (mid->board->exit)
+ mid->board->exit(mid);
+
+ serial8250_unregister_port(mid->line);
+}
+
+static const struct mid8250_board pnw_board = {
+ .freq = 50000000,
+ .base_baud = 115200,
+ .setup = pnw_setup,
+};
+
+static const struct mid8250_board tng_board = {
+ .freq = 38400000,
+ .base_baud = 1843200,
+ .setup = tng_setup,
+};
+
+static const struct mid8250_board dnv_board = {
+ .freq = 133333333,
+ .base_baud = 115200,
+ .setup = dnv_setup,
+ .exit = dnv_exit,
+};
+
+#define MID_DEVICE(id, board) { PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&board }
+
+static const struct pci_device_id pci_ids[] = {
+ MID_DEVICE(PCI_DEVICE_ID_INTEL_PNW_UART1, pnw_board),
+ MID_DEVICE(PCI_DEVICE_ID_INTEL_PNW_UART2, pnw_board),
+ MID_DEVICE(PCI_DEVICE_ID_INTEL_PNW_UART3, pnw_board),
+ MID_DEVICE(PCI_DEVICE_ID_INTEL_TNG_UART, tng_board),
+ MID_DEVICE(PCI_DEVICE_ID_INTEL_DNV_UART, dnv_board),
+ { },
+};
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static struct pci_driver mid8250_pci_driver = {
+ .name = "8250_mid",
+ .id_table = pci_ids,
+ .probe = mid8250_probe,
+ .remove = mid8250_remove,
+};
+
+module_pci_driver(mid8250_pci_driver);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel MID UART driver");
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 826c5c4a2103..a2c0734c76e2 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -439,7 +439,6 @@ static void omap_8250_set_termios(struct uart_port *port,
priv->xoff = termios->c_cc[VSTOP];
priv->efr = 0;
- up->mcr &= ~(UART_MCR_RTS | UART_MCR_XONANY);
up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
@@ -726,6 +725,7 @@ static void __dma_rx_do_complete(struct uart_8250_port *p, bool error)
struct dma_tx_state state;
int count;
unsigned long flags;
+ int ret;
dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr,
dma->rx_size, DMA_FROM_DEVICE);
@@ -741,8 +741,10 @@ static void __dma_rx_do_complete(struct uart_8250_port *p, bool error)
count = dma->rx_size - state.residue;
- tty_insert_flip_string(tty_port, dma->rx_buf, count);
- p->port.icount.rx += count;
+ ret = tty_insert_flip_string(tty_port, dma->rx_buf, count);
+
+ p->port.icount.rx += ret;
+ p->port.icount.buf_overrun += count - ret;
unlock:
spin_unlock_irqrestore(&priv->rx_dma_lock, flags);
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 68042dd1c525..4097f3f65b3b 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -28,7 +28,6 @@
#include <linux/dmaengine.h>
#include <linux/platform_data/dma-dw.h>
-#include <linux/platform_data/dma-hsu.h>
#include "8250.h"
@@ -1508,167 +1507,6 @@ byt_serial_setup(struct serial_private *priv,
return ret;
}
-#define INTEL_MID_UART_PS 0x30
-#define INTEL_MID_UART_MUL 0x34
-#define INTEL_MID_UART_DIV 0x38
-
-static void intel_mid_set_termios(struct uart_port *p,
- struct ktermios *termios,
- struct ktermios *old,
- unsigned long fref)
-{
- unsigned int baud = tty_termios_baud_rate(termios);
- unsigned short ps = 16;
- unsigned long fuart = baud * ps;
- unsigned long w = BIT(24) - 1;
- unsigned long mul, div;
-
- if (fref < fuart) {
- /* Find prescaler value that satisfies Fuart < Fref */
- if (fref > baud)
- ps = fref / baud; /* baud rate too high */
- else
- ps = 1; /* PLL case */
- fuart = baud * ps;
- } else {
- /* Get Fuart closer to Fref */
- fuart *= rounddown_pow_of_two(fref / fuart);
- }
-
- rational_best_approximation(fuart, fref, w, w, &mul, &div);
- p->uartclk = fuart * 16 / ps; /* core uses ps = 16 always */
-
- writel(ps, p->membase + INTEL_MID_UART_PS); /* set PS */
- writel(mul, p->membase + INTEL_MID_UART_MUL); /* set MUL */
- writel(div, p->membase + INTEL_MID_UART_DIV);
-
- serial8250_do_set_termios(p, termios, old);
-}
-
-static void intel_mid_set_termios_38_4M(struct uart_port *p,
- struct ktermios *termios,
- struct ktermios *old)
-{
- intel_mid_set_termios(p, termios, old, 38400000);
-}
-
-static void intel_mid_set_termios_50M(struct uart_port *p,
- struct ktermios *termios,
- struct ktermios *old)
-{
- /*
- * The uart clk is 50Mhz, and the baud rate come from:
- * baud = 50M * MUL / (DIV * PS * DLAB)
- */
- intel_mid_set_termios(p, termios, old, 50000000);
-}
-
-static bool intel_mid_dma_filter(struct dma_chan *chan, void *param)
-{
- struct hsu_dma_slave *s = param;
-
- if (s->dma_dev != chan->device->dev || s->chan_id != chan->chan_id)
- return false;
-
- chan->private = s;
- return true;
-}
-
-static int intel_mid_serial_setup(struct serial_private *priv,
- const struct pciserial_board *board,
- struct uart_8250_port *port, int idx,
- int index, struct pci_dev *dma_dev)
-{
- struct device *dev = port->port.dev;
- struct uart_8250_dma *dma;
- struct hsu_dma_slave *tx_param, *rx_param;
-
- dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
- if (!dma)
- return -ENOMEM;
-
- tx_param = devm_kzalloc(dev, sizeof(*tx_param), GFP_KERNEL);
- if (!tx_param)
- return -ENOMEM;
-
- rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL);
- if (!rx_param)
- return -ENOMEM;
-
- rx_param->chan_id = index * 2 + 1;
- tx_param->chan_id = index * 2;
-
- dma->rxconf.src_maxburst = 64;
- dma->txconf.dst_maxburst = 64;
-
- rx_param->dma_dev = &dma_dev->dev;
- tx_param->dma_dev = &dma_dev->dev;
-
- dma->fn = intel_mid_dma_filter;
- dma->rx_param = rx_param;
- dma->tx_param = tx_param;
-
- port->port.type = PORT_16750;
- port->port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE;
- port->dma = dma;
-
- return pci_default_setup(priv, board, port, idx);
-}
-
-#define PCI_DEVICE_ID_INTEL_PNW_UART1 0x081b
-#define PCI_DEVICE_ID_INTEL_PNW_UART2 0x081c
-#define PCI_DEVICE_ID_INTEL_PNW_UART3 0x081d
-
-static int pnw_serial_setup(struct serial_private *priv,
- const struct pciserial_board *board,
- struct uart_8250_port *port, int idx)
-{
- struct pci_dev *pdev = priv->dev;
- struct pci_dev *dma_dev;
- int index;
-
- switch (pdev->device) {
- case PCI_DEVICE_ID_INTEL_PNW_UART1:
- index = 0;
- break;
- case PCI_DEVICE_ID_INTEL_PNW_UART2:
- index = 1;
- break;
- case PCI_DEVICE_ID_INTEL_PNW_UART3:
- index = 2;
- break;
- default:
- return -EINVAL;
- }
-
- dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), 3));
-
- port->port.set_termios = intel_mid_set_termios_50M;
-
- return intel_mid_serial_setup(priv, board, port, idx, index, dma_dev);
-}
-
-#define PCI_DEVICE_ID_INTEL_TNG_UART 0x1191
-
-static int tng_serial_setup(struct serial_private *priv,
- const struct pciserial_board *board,
- struct uart_8250_port *port, int idx)
-{
- struct pci_dev *pdev = priv->dev;
- struct pci_dev *dma_dev;
- int index = PCI_FUNC(pdev->devfn);
-
- /* Currently no support for HSU port0 */
- if (index-- == 0)
- return -ENODEV;
-
- dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(5, 0));
-
- port->port.set_termios = intel_mid_set_termios_38_4M;
-
- return intel_mid_serial_setup(priv, board, port, idx, index, dma_dev);
-}
-
static int
pci_omegapci_setup(struct serial_private *priv,
const struct pciserial_board *board,
@@ -2212,34 +2050,6 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
},
{
.vendor = PCI_VENDOR_ID_INTEL,
- .device = PCI_DEVICE_ID_INTEL_PNW_UART1,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .setup = pnw_serial_setup,
- },
- {
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = PCI_DEVICE_ID_INTEL_PNW_UART2,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .setup = pnw_serial_setup,
- },
- {
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = PCI_DEVICE_ID_INTEL_PNW_UART3,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .setup = pnw_serial_setup,
- },
- {
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = PCI_DEVICE_ID_INTEL_TNG_UART,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .setup = tng_serial_setup,
- },
- {
- .vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_BSW_UART1,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
@@ -3119,8 +2929,6 @@ enum pci_board_num_t {
pbn_ADDIDATA_PCIe_8_3906250,
pbn_ce4100_1_115200,
pbn_byt,
- pbn_pnw,
- pbn_tng,
pbn_qrk,
pbn_omegapci,
pbn_NETMOS9900_2s_115200,
@@ -3907,16 +3715,6 @@ static struct pciserial_board pci_boards[] = {
.uart_offset = 0x80,
.reg_shift = 2,
},
- [pbn_pnw] = {
- .flags = FL_BASE0,
- .num_ports = 1,
- .base_baud = 115200,
- },
- [pbn_tng] = {
- .flags = FL_BASE0,
- .num_ports = 1,
- .base_baud = 1843200,
- },
[pbn_qrk] = {
.flags = FL_BASE0,
.num_ports = 1,
@@ -4005,6 +3803,13 @@ static const struct pci_device_id blacklist[] = {
{ PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */
{ PCI_DEVICE(0x1c00, 0x3250), }, /* WCH CH382 2S1P */
{ PCI_DEVICE(0x1c00, 0x3470), }, /* WCH CH384 4S */
+
+ /* Intel platforms with MID UART */
+ { PCI_VDEVICE(INTEL, 0x081b), },
+ { PCI_VDEVICE(INTEL, 0x081c), },
+ { PCI_VDEVICE(INTEL, 0x081d), },
+ { PCI_VDEVICE(INTEL, 0x1191), },
+ { PCI_VDEVICE(INTEL, 0x19d8), },
};
/*
@@ -5702,26 +5507,6 @@ static struct pci_device_id serial_pci_tbl[] = {
pbn_byt },
/*
- * Intel Penwell
- */
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PNW_UART1,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pnw},
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PNW_UART2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pnw},
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PNW_UART3,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pnw},
-
- /*
- * Intel Tangier
- */
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TNG_UART,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_tng},
-
- /*
* Intel Quark x1000
*/
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QRK_UART,
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 0bbf34035d6a..52d82d2ac726 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -284,7 +284,7 @@ static void default_serial_dl_write(struct uart_8250_port *up, int value)
serial_out(up, UART_DLM, value >> 8 & 0xff);
}
-#if defined(CONFIG_MIPS_ALCHEMY) || defined(CONFIG_SERIAL_8250_RT288X)
+#ifdef CONFIG_SERIAL_8250_RT288X
/* Au1x00/RT288x UART hardware has a weird register layout */
static const s8 au_io_in_map[8] = {
@@ -435,7 +435,7 @@ static void set_io_from_upio(struct uart_port *p)
p->serial_out = mem32be_serial_out;
break;
-#if defined(CONFIG_MIPS_ALCHEMY) || defined(CONFIG_SERIAL_8250_RT288X)
+#ifdef CONFIG_SERIAL_8250_RT288X
case UPIO_AU:
p->serial_in = au_serial_in;
p->serial_out = au_serial_out;
@@ -1246,6 +1246,9 @@ static void autoconfig_irq(struct uart_8250_port *up)
inb_p(ICP);
}
+ if (uart_console(port))
+ console_lock();
+
/* forget possible initially masked and pending IRQ */
probe_irq_off(probe_irq_on());
save_mcr = serial_in(up, UART_MCR);
@@ -1277,6 +1280,9 @@ static void autoconfig_irq(struct uart_8250_port *up)
if (port->flags & UPF_FOURPORT)
outb_p(save_ICP, ICP);
+ if (uart_console(port))
+ console_unlock();
+
port->irq = (irq > 0) ? irq : 0;
}
@@ -1807,9 +1813,6 @@ int serial8250_do_startup(struct uart_port *port)
unsigned char lsr, iir;
int retval;
- if (port->type == PORT_8250_CIR)
- return -ENODEV;
-
if (!port->fifosize)
port->fifosize = uart_config[port->type].fifo_size;
if (!up->tx_loadsz)
@@ -2230,6 +2233,23 @@ static void serial8250_set_divisor(struct uart_port *port, unsigned int baud,
serial_port_out(port, 0x2, quot_frac);
}
+static unsigned int
+serial8250_get_baud_rate(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ unsigned int tolerance = port->uartclk / 100;
+
+ /*
+ * Ask the core to calculate the divisor for us.
+ * Allow 1% tolerance at the upper limit so uart clks marginally
+ * slower than nominal still match standard baud rates without
+ * causing transmission errors.
+ */
+ return uart_get_baud_rate(port, termios, old,
+ port->uartclk / 16 / 0xffff,
+ (port->uartclk + tolerance) / 16);
+}
+
void
serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
@@ -2241,12 +2261,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
cval = serial8250_compute_lcr(up, termios->c_cflag);
- /*
- * Ask the core to calculate the divisor for us.
- */
- baud = uart_get_baud_rate(port, termios, old,
- port->uartclk / 16 / 0xffff,
- port->uartclk / 16);
+ baud = serial8250_get_baud_rate(port, termios, old);
quot = serial8250_get_divisor(up, baud, &frac);
/*
@@ -2513,14 +2528,8 @@ static void serial8250_release_port(struct uart_port *port)
static int serial8250_request_port(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
- int ret;
-
- if (port->type == PORT_8250_CIR)
- return -ENODEV;
-
- ret = serial8250_request_std_resource(up);
- return ret;
+ return serial8250_request_std_resource(up);
}
static int fcr_get_rxtrig_bytes(struct uart_8250_port *up)
@@ -2668,9 +2677,6 @@ static void serial8250_config_port(struct uart_port *port, int flags)
struct uart_8250_port *up = up_to_u8250p(port);
int ret;
- if (port->type == PORT_8250_CIR)
- return;
-
/*
* Find the region that we can probe for. This in turn
* tells us whether we can probe for the type of port.
@@ -2805,6 +2811,27 @@ static void serial8250_console_putchar(struct uart_port *port, int ch)
}
/*
+ * Restore serial console when h/w power-off detected
+ */
+static void serial8250_console_restore(struct uart_8250_port *up)
+{
+ struct uart_port *port = &up->port;
+ struct ktermios termios;
+ unsigned int baud, quot, frac = 0;
+
+ termios.c_cflag = port->cons->cflag;
+ if (port->state->port.tty && termios.c_cflag == 0)
+ termios.c_cflag = port->state->port.tty->termios.c_cflag;
+
+ baud = serial8250_get_baud_rate(port, &termios, NULL);
+ quot = serial8250_get_divisor(up, baud, &frac);
+
+ serial8250_set_divisor(port, baud, quot, frac);
+ serial_port_out(port, UART_LCR, up->lcr);
+ serial_port_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
+}
+
+/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port...
*
@@ -2841,22 +2868,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
/* check scratch reg to see if port powered off during system sleep */
if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) {
- struct ktermios termios;
- unsigned int baud, quot, frac = 0;
-
- termios.c_cflag = port->cons->cflag;
- if (port->state->port.tty && termios.c_cflag == 0)
- termios.c_cflag = port->state->port.tty->termios.c_cflag;
-
- baud = uart_get_baud_rate(port, &termios, NULL,
- port->uartclk / 16 / 0xffff,
- port->uartclk / 16);
- quot = serial8250_get_divisor(up, baud, &frac);
-
- serial8250_set_divisor(port, baud, quot, frac);
- serial_port_out(port, UART_LCR, up->lcr);
- serial_port_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
-
+ serial8250_console_restore(up);
up->canary = 0;
}
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index e1de1181b322..e6f5e12a2d83 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -274,8 +274,8 @@ config SERIAL_8250_ACORN
config SERIAL_8250_FSL
bool
- depends on SERIAL_8250_CONSOLE && PPC_UDBG_16550
- default PPC
+ depends on SERIAL_8250_CONSOLE
+ default PPC || ARM || ARM64
config SERIAL_8250_DW
tristate "Support for Synopsys DesignWare 8250 quirks"
@@ -294,11 +294,12 @@ config SERIAL_8250_EM
config SERIAL_8250_RT288X
bool "Ralink RT288x/RT305x/RT3662/RT3883 serial port support"
- depends on SERIAL_8250 && (SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620)
+ depends on SERIAL_8250
+ default y if MIPS_ALCHEMY || SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620
help
- If you have a Ralink RT288x/RT305x SoC based board and want to use the
- serial port, say Y to this option. The driver can handle up to 2 serial
- ports. If unsure, say N.
+ Selecting this option will add support for the alternate register
+ layout used by Ralink RT288x/RT305x, Alchemy Au1xxx, and some others.
+ If unsure, say N.
config SERIAL_8250_OMAP
tristate "Support for OMAP internal UART (8250 based driver)"
@@ -337,7 +338,7 @@ config SERIAL_8250_FINTEK
through the PNP driver. If unsure, say N.
config SERIAL_8250_LPC18XX
- bool "NXP LPC18xx/43xx serial port support"
+ tristate "NXP LPC18xx/43xx serial port support"
depends on SERIAL_8250 && OF && (ARCH_LPC18XX || COMPILE_TEST)
default ARCH_LPC18XX
help
@@ -366,3 +367,13 @@ config SERIAL_8250_INGENIC
help
If you have a system using an Ingenic SoC and wish to make use of
its UARTs, say Y to this option. If unsure, say N.
+
+config SERIAL_8250_MID
+ tristate "Support for serial ports on Intel MID platforms"
+ depends on SERIAL_8250 && PCI
+ select HSU_DMA if SERIAL_8250_DMA
+ select HSU_DMA_PCI if X86_INTEL_MID
+ help
+ Selecting this option will enable handling of the extra features
+ present on the UART found on Intel Medfield SOC and various other
+ Intel platforms.
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 39c6d2277570..e177f8681ada 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -27,5 +27,6 @@ obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o
obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o
obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o
obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o
+obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o
CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt