diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-01-20 14:01:31 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-01-20 14:01:31 -0300 |
commit | b4b7ff4b08e691656c9d77c758fc355833128ac0 (patch) | |
tree | 82fcb00e6b918026dc9f2d1f05ed8eee83874cc0 /drivers/tty/serial/8250 | |
parent | 35acfa0fc609f2a2cd95cef4a6a9c3a5c38f1778 (diff) |
Linux-libre 4.4-gnupck-4.4-gnu
Diffstat (limited to 'drivers/tty/serial/8250')
-rw-r--r-- | drivers/tty/serial/8250/8250_core.c | 26 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_dma.c | 6 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_dw.c | 292 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_early.c | 4 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_fsl.c | 1 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_ingenic.c | 88 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_mid.c | 326 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_omap.c | 8 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_pci.c | 229 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_port.c | 86 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_uniphier.c | 8 | ||||
-rw-r--r-- | drivers/tty/serial/8250/Kconfig | 26 | ||||
-rw-r--r-- | drivers/tty/serial/8250/Makefile | 1 |
13 files changed, 654 insertions, 447 deletions
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index d4adaf380..58dbff93a 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 e508939da..78259d3c6 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 06324f17a..a5d319e4a 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 @@ -224,10 +227,6 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, if (IS_ERR(d->clk) || !old) goto out; - /* Not requesting clock rates below 1.8432Mhz */ - if (baud < 115200) - baud = 115200; - clk_disable_unprepare(d->clk); rate = clk_round_rate(d->clk, baud * 16); ret = clk_set_rate(d->clk, rate); @@ -244,24 +243,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 +325,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 +332,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 +430,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 +460,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 4a2c3892c..e2919b4d1 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_fsl.c b/drivers/tty/serial/8250/8250_fsl.c index c0533a57e..910bfee5a 100644 --- a/drivers/tty/serial/8250/8250_fsl.c +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -60,3 +60,4 @@ int fsl8250_handle_irq(struct uart_port *port) spin_unlock_irqrestore(&up->port.lock, flags); return 1; } +EXPORT_SYMBOL_GPL(fsl8250_handle_irq); diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c index 7c1e4be48..49394b4c5 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 000000000..88531a36b --- /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 826c5c4a2..a2c0734c7 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 68042dd1c..4097f3f65 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 8bbb51fff..5a85bbaa2 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/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index d11621e2c..245edbb68 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -115,12 +115,16 @@ static void uniphier_serial_out(struct uart_port *p, int offset, int value) */ static int uniphier_serial_dl_read(struct uart_8250_port *up) { - return readl(up->port.membase + UNIPHIER_UART_DLR); + int offset = UNIPHIER_UART_DLR << up->port.regshift; + + return readl(up->port.membase + offset); } static void uniphier_serial_dl_write(struct uart_8250_port *up, int value) { - writel(value, up->port.membase + UNIPHIER_UART_DLR); + int offset = UNIPHIER_UART_DLR << up->port.regshift; + + writel(value, up->port.membase + offset); } static int uniphier_of_serial_setup(struct device *dev, struct uart_port *port, diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index e1de1181b..6412f1455 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,14 @@ 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 + select RATIONAL + 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 39c6d2277..e177f8681 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 |