diff options
Diffstat (limited to 'drivers/tty/serial/8250')
-rw-r--r-- | drivers/tty/serial/8250/8250.h | 41 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_aspeed_vuart.c | 2 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_core.c | 1 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_dma.c | 7 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_dw.c | 229 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_dwlib.c | 116 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_dwlib.h | 51 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_fintek.c | 8 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_mtk.c | 7 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_of.c | 2 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_pci.c | 480 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_port.c | 140 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_pxa.c | 1 | ||||
-rw-r--r-- | drivers/tty/serial/8250/Kconfig | 2 |
14 files changed, 784 insertions, 303 deletions
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index a8830e15a22c..696030cfcb09 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -17,6 +17,8 @@ struct uart_8250_dma { int (*tx_dma)(struct uart_8250_port *p); int (*rx_dma)(struct uart_8250_port *p); + void (*prepare_tx_dma)(struct uart_8250_port *p); + void (*prepare_rx_dma)(struct uart_8250_port *p); /* Filter function */ dma_filter_fn fn; @@ -83,6 +85,7 @@ struct serial8250_config { #define UART_CAP_MINI BIT(17) /* Mini UART on BCM283X family lacks: * STOP PARITY EPAR SPAR WLEN5 WLEN6 */ +#define UART_CAP_NOTEMT BIT(18) /* UART without interrupt on TEMT available */ #define UART_BUG_QUOT BIT(0) /* UART has buggy quot LSB */ #define UART_BUG_TXEN BIT(1) /* UART has buggy TX IIR status */ @@ -120,6 +123,28 @@ static inline void serial_out(struct uart_8250_port *up, int offset, int value) up->port.serial_out(&up->port, offset, value); } +/* + * For the 16C950 + */ +static void serial_icr_write(struct uart_8250_port *up, int offset, int value) +{ + serial_out(up, UART_SCR, offset); + serial_out(up, UART_ICR, value); +} + +static unsigned int __maybe_unused serial_icr_read(struct uart_8250_port *up, + int offset) +{ + unsigned int value; + + serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD); + serial_out(up, UART_SCR, offset); + value = serial_in(up, UART_ICR); + serial_icr_write(up, UART_ACR, up->acr); + + return value; +} + void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p); static inline int serial_dl_read(struct uart_8250_port *up) @@ -302,6 +327,22 @@ extern int serial8250_rx_dma(struct uart_8250_port *); extern void serial8250_rx_dma_flush(struct uart_8250_port *); extern int serial8250_request_dma(struct uart_8250_port *); extern void serial8250_release_dma(struct uart_8250_port *); + +static inline void serial8250_do_prepare_tx_dma(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + if (dma->prepare_tx_dma) + dma->prepare_tx_dma(p); +} + +static inline void serial8250_do_prepare_rx_dma(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + if (dma->prepare_rx_dma) + dma->prepare_rx_dma(p); +} #else static inline int serial8250_tx_dma(struct uart_8250_port *p) { diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c index 93fe10c680fb..9d2a7856784f 100644 --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -429,6 +429,8 @@ static int aspeed_vuart_probe(struct platform_device *pdev) timer_setup(&vuart->unthrottle_timer, aspeed_vuart_unthrottle_exp, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; memset(&port, 0, sizeof(port)); port.port.private_data = vuart; diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 01d30f6ed8fb..cfbd2de0ca6e 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -32,7 +32,6 @@ #include <linux/mutex.h> #include <linux/slab.h> #include <linux/uaccess.h> -#include <linux/pm_runtime.h> #include <linux/io.h> #ifdef CONFIG_SPARC #include <linux/sunserialcore.h> diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index b3c3f7e5851a..7133fceed35e 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -34,7 +34,7 @@ static void __dma_tx_complete(void *param) uart_write_wakeup(&p->port); ret = serial8250_tx_dma(p); - if (ret) + if (ret || !dma->tx_running) serial8250_set_THRI(p); spin_unlock_irqrestore(&p->port.lock, flags); @@ -80,12 +80,13 @@ int serial8250_tx_dma(struct uart_8250_port *p) if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) { /* We have been called from __dma_tx_complete() */ - serial8250_rpm_put_tx(p); return 0; } dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + serial8250_do_prepare_tx_dma(p); + desc = dmaengine_prep_slave_single(dma->txchan, dma->tx_addr + xmit->tail, dma->tx_size, DMA_MEM_TO_DEV, @@ -123,6 +124,8 @@ int serial8250_rx_dma(struct uart_8250_port *p) if (dma->rx_running) return 0; + serial8250_do_prepare_rx_dma(p); + desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, dma->rx_size, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 1769808031c5..f57bbd32ef11 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -12,13 +12,13 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/io.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/serial_8250.h> #include <linux/serial_reg.h> #include <linux/of.h> -#include <linux/of_irq.h> -#include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/workqueue.h> #include <linux/notifier.h> #include <linux/slab.h> @@ -33,30 +33,28 @@ /* Offsets for the DesignWare specific registers */ #define DW_UART_USR 0x1f /* UART Status Register */ +#define DW_UART_DMASA 0xa8 /* DMA Software Ack */ + +#define OCTEON_UART_USR 0x27 /* UART Status Register */ + +#define RZN1_UART_TDMACR 0x10c /* DMA Control Register Transmit Mode */ +#define RZN1_UART_RDMACR 0x110 /* DMA Control Register Receive Mode */ /* DesignWare specific register fields */ #define DW_UART_MCR_SIRE BIT(6) -struct dw8250_data { - struct dw8250_port_data data; +/* Renesas specific register fields */ +#define RZN1_UART_xDMACR_DMA_EN BIT(0) +#define RZN1_UART_xDMACR_1_WORD_BURST (0 << 1) +#define RZN1_UART_xDMACR_4_WORD_BURST (1 << 1) +#define RZN1_UART_xDMACR_8_WORD_BURST (3 << 1) +#define RZN1_UART_xDMACR_BLK_SZ(x) ((x) << 3) - u8 usr_reg; - int msr_mask_on; - int msr_mask_off; - struct clk *clk; - struct clk *pclk; - struct notifier_block clk_notifier; - struct work_struct clk_work; - struct reset_control *rst; - - unsigned int skip_autocfg:1; - unsigned int uart_16550_compatible:1; -}; - -static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data) -{ - return container_of(data, struct dw8250_data, data); -} +/* Quirks */ +#define DW_UART_QUIRK_OCTEON BIT(0) +#define DW_UART_QUIRK_ARMADA_38X BIT(1) +#define DW_UART_QUIRK_SKIP_SET_RATE BIT(2) +#define DW_UART_QUIRK_IS_DMA_FC BIT(3) static inline struct dw8250_data *clk_to_dw8250_data(struct notifier_block *nb) { @@ -238,6 +236,8 @@ static int dw8250_handle_irq(struct uart_port *p) struct uart_8250_port *up = up_to_u8250p(p); struct dw8250_data *d = to_dw8250_data(p->private_data); unsigned int iir = p->serial_in(p, UART_IIR); + bool rx_timeout = (iir & 0x3f) == UART_IIR_RX_TIMEOUT; + unsigned int quirks = d->pdata->quirks; unsigned int status; unsigned long flags; @@ -251,7 +251,7 @@ static int dw8250_handle_irq(struct uart_port *p) * 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)) { + if (!up->dma && rx_timeout) { spin_lock_irqsave(&p->lock, flags); status = p->serial_in(p, UART_LSR); @@ -261,12 +261,21 @@ static int dw8250_handle_irq(struct uart_port *p) spin_unlock_irqrestore(&p->lock, flags); } + /* Manually stop the Rx DMA transfer when acting as flow controller */ + if (quirks & DW_UART_QUIRK_IS_DMA_FC && up->dma && up->dma->rx_running && rx_timeout) { + status = p->serial_in(p, UART_LSR); + if (status & (UART_LSR_DR | UART_LSR_BI)) { + dw8250_writel_ext(p, RZN1_UART_RDMACR, 0); + dw8250_writel_ext(p, DW_UART_DMASA, 1); + } + } + if (serial8250_handle_irq(p, iir)) return 1; if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { /* Clear the USR */ - (void)p->serial_in(p, d->usr_reg); + (void)p->serial_in(p, d->pdata->usr_reg); return 1; } @@ -384,11 +393,48 @@ static bool dw8250_idma_filter(struct dma_chan *chan, void *param) return param == chan->device->dev; } +static u32 dw8250_rzn1_get_dmacr_burst(int max_burst) +{ + if (max_burst >= 8) + return RZN1_UART_xDMACR_8_WORD_BURST; + else if (max_burst >= 4) + return RZN1_UART_xDMACR_4_WORD_BURST; + else + return RZN1_UART_xDMACR_1_WORD_BURST; +} + +static void dw8250_prepare_tx_dma(struct uart_8250_port *p) +{ + struct uart_port *up = &p->port; + struct uart_8250_dma *dma = p->dma; + u32 val; + + dw8250_writel_ext(up, RZN1_UART_TDMACR, 0); + val = dw8250_rzn1_get_dmacr_burst(dma->txconf.dst_maxburst) | + RZN1_UART_xDMACR_BLK_SZ(dma->tx_size) | + RZN1_UART_xDMACR_DMA_EN; + dw8250_writel_ext(up, RZN1_UART_TDMACR, val); +} + +static void dw8250_prepare_rx_dma(struct uart_8250_port *p) +{ + struct uart_port *up = &p->port; + struct uart_8250_dma *dma = p->dma; + u32 val; + + dw8250_writel_ext(up, RZN1_UART_RDMACR, 0); + val = dw8250_rzn1_get_dmacr_burst(dma->rxconf.src_maxburst) | + RZN1_UART_xDMACR_BLK_SZ(dma->rx_size) | + RZN1_UART_xDMACR_DMA_EN; + dw8250_writel_ext(up, RZN1_UART_RDMACR, val); +} + static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) { struct device_node *np = p->dev->of_node; if (np) { + unsigned int quirks = data->pdata->quirks; int id; /* get index of serial line, if found in DT aliases */ @@ -396,12 +442,11 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) if (id >= 0) p->line = id; #ifdef CONFIG_64BIT - if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) { + if (quirks & DW_UART_QUIRK_OCTEON) { 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 @@ -412,10 +457,16 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) p->serial_out = dw8250_serial_out32be; } - if (of_device_is_compatible(np, "marvell,armada-38x-uart")) + if (quirks & DW_UART_QUIRK_ARMADA_38X) p->serial_out = dw8250_serial_out38x; - if (of_device_is_compatible(np, "starfive,jh7100-uart")) + if (quirks & DW_UART_QUIRK_SKIP_SET_RATE) p->set_termios = dw8250_do_set_termios; + if (quirks & DW_UART_QUIRK_IS_DMA_FC) { + data->data.dma.txconf.device_fc = 1; + data->data.dma.rxconf.device_fc = 1; + data->data.dma.prepare_tx_dma = dw8250_prepare_tx_dma; + data->data.dma.prepare_rx_dma = dw8250_prepare_rx_dma; + } } else if (acpi_dev_present("APMC0D08", NULL, -1)) { p->iotype = UPIO_MEM32; @@ -433,21 +484,30 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) } } +static void dw8250_clk_disable_unprepare(void *data) +{ + clk_disable_unprepare(data); +} + +static void dw8250_reset_control_assert(void *data) +{ + reset_control_assert(data); +} + static int dw8250_probe(struct platform_device *pdev) { struct uart_8250_port uart = {}, *up = &uart; - struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct uart_port *p = &up->port; struct device *dev = &pdev->dev; struct dw8250_data *data; + struct resource *regs; int irq; int err; u32 val; - if (!regs) { - dev_err(dev, "no registers defined\n"); - return -EINVAL; - } + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return dev_err_probe(dev, -EINVAL, "no registers defined\n"); irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -476,7 +536,7 @@ static int dw8250_probe(struct platform_device *pdev) return -ENOMEM; data->data.dma.fn = dw8250_fallback_dma_filter; - data->usr_reg = DW_UART_USR; + data->pdata = device_get_match_data(p->dev); p->private_data = &data->data; data->uart_16550_compatible = device_property_read_bool(dev, @@ -532,37 +592,41 @@ static int dw8250_probe(struct platform_device *pdev) err = clk_prepare_enable(data->clk); if (err) - dev_warn(dev, "could not enable optional baudclk: %d\n", err); + return dev_err_probe(dev, err, "could not enable optional baudclk\n"); + + err = devm_add_action_or_reset(dev, dw8250_clk_disable_unprepare, data->clk); + if (err) + return err; if (data->clk) p->uartclk = clk_get_rate(data->clk); /* If no clock rate is defined, fail. */ - if (!p->uartclk) { - dev_err(dev, "clock rate not defined\n"); - err = -EINVAL; - goto err_clk; - } + if (!p->uartclk) + return dev_err_probe(dev, -EINVAL, "clock rate not defined\n"); data->pclk = devm_clk_get_optional(dev, "apb_pclk"); - if (IS_ERR(data->pclk)) { - err = PTR_ERR(data->pclk); - goto err_clk; - } + if (IS_ERR(data->pclk)) + return PTR_ERR(data->pclk); err = clk_prepare_enable(data->pclk); - if (err) { - dev_err(dev, "could not enable apb_pclk\n"); - goto err_clk; - } + if (err) + return dev_err_probe(dev, err, "could not enable apb_pclk\n"); + + err = devm_add_action_or_reset(dev, dw8250_clk_disable_unprepare, data->pclk); + if (err) + return err; data->rst = devm_reset_control_get_optional_exclusive(dev, NULL); - if (IS_ERR(data->rst)) { - err = PTR_ERR(data->rst); - goto err_pclk; - } + if (IS_ERR(data->rst)) + return PTR_ERR(data->rst); + reset_control_deassert(data->rst); + err = devm_add_action_or_reset(dev, dw8250_reset_control_assert, data->rst); + if (err) + return err; + dw8250_quirks(p, data); /* If the Busy Functionality is not implemented, don't handle it */ @@ -580,10 +644,8 @@ static int dw8250_probe(struct platform_device *pdev) } data->data.line = serial8250_register_8250_port(up); - if (data->data.line < 0) { - err = data->data.line; - goto err_reset; - } + if (data->data.line < 0) + return data->data.line; /* * Some platforms may provide a reference clock shared between several @@ -593,9 +655,8 @@ static int dw8250_probe(struct platform_device *pdev) if (data->clk) { err = clk_notifier_register(data->clk, &data->clk_notifier); if (err) - dev_warn(p->dev, "Failed to set the clock notifier\n"); - else - queue_work(system_unbound_wq, &data->clk_work); + return dev_err_probe(dev, err, "Failed to set the clock notifier\n"); + queue_work(system_unbound_wq, &data->clk_work); } platform_set_drvdata(pdev, data); @@ -604,17 +665,6 @@ static int dw8250_probe(struct platform_device *pdev) pm_runtime_enable(dev); return 0; - -err_reset: - reset_control_assert(data->rst); - -err_pclk: - clk_disable_unprepare(data->pclk); - -err_clk: - clk_disable_unprepare(data->clk); - - return err; } static int dw8250_remove(struct platform_device *pdev) @@ -632,12 +682,6 @@ static int dw8250_remove(struct platform_device *pdev) serial8250_unregister_port(data->data.line); - reset_control_assert(data->rst); - - clk_disable_unprepare(data->pclk); - - clk_disable_unprepare(data->clk); - pm_runtime_disable(dev); pm_runtime_put_noidle(dev); @@ -693,12 +737,37 @@ static const struct dev_pm_ops dw8250_pm_ops = { SET_RUNTIME_PM_OPS(dw8250_runtime_suspend, dw8250_runtime_resume, NULL) }; +static const struct dw8250_platform_data dw8250_dw_apb = { + .usr_reg = DW_UART_USR, +}; + +static const struct dw8250_platform_data dw8250_octeon_3860_data = { + .usr_reg = OCTEON_UART_USR, + .quirks = DW_UART_QUIRK_OCTEON, +}; + +static const struct dw8250_platform_data dw8250_armada_38x_data = { + .usr_reg = DW_UART_USR, + .quirks = DW_UART_QUIRK_ARMADA_38X, +}; + +static const struct dw8250_platform_data dw8250_renesas_rzn1_data = { + .usr_reg = DW_UART_USR, + .cpr_val = 0x00012f32, + .quirks = DW_UART_QUIRK_IS_DMA_FC, +}; + +static const struct dw8250_platform_data dw8250_starfive_jh7100_data = { + .usr_reg = DW_UART_USR, + .quirks = DW_UART_QUIRK_SKIP_SET_RATE, +}; + static const struct of_device_id dw8250_of_match[] = { - { .compatible = "snps,dw-apb-uart" }, - { .compatible = "cavium,octeon-3860-uart" }, - { .compatible = "marvell,armada-38x-uart" }, - { .compatible = "renesas,rzn1-uart" }, - { .compatible = "starfive,jh7100-uart" }, + { .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb }, + { .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data }, + { .compatible = "marvell,armada-38x-uart", .data = &dw8250_armada_38x_data }, + { .compatible = "renesas,rzn1-uart", .data = &dw8250_renesas_rzn1_data }, + { .compatible = "starfive,jh7100-uart", .data = &dw8250_starfive_jh7100_data }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dw8250_of_match); diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c index 622d3b0d89e7..fbabfdd8c7b8 100644 --- a/drivers/tty/serial/8250/8250_dwlib.c +++ b/drivers/tty/serial/8250/8250_dwlib.c @@ -2,19 +2,32 @@ /* Synopsys DesignWare 8250 library. */ #include <linux/bitops.h> +#include <linux/bitfield.h> #include <linux/device.h> -#include <linux/io.h> #include <linux/kernel.h> +#include <linux/property.h> #include <linux/serial_8250.h> #include <linux/serial_core.h> #include "8250_dwlib.h" /* Offsets for the DesignWare specific registers */ +#define DW_UART_TCR 0xac /* Transceiver Control Register (RS485) */ +#define DW_UART_DE_EN 0xb0 /* Driver Output Enable Register */ +#define DW_UART_RE_EN 0xb4 /* Receiver Output Enable Register */ #define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */ #define DW_UART_CPR 0xf4 /* Component Parameter Register */ #define DW_UART_UCV 0xf8 /* UART Component Version */ +/* Transceiver Control Register bits */ +#define DW_UART_TCR_RS485_EN BIT(0) +#define DW_UART_TCR_RE_POL BIT(1) +#define DW_UART_TCR_DE_POL BIT(2) +#define DW_UART_TCR_XFER_MODE GENMASK(4, 3) +#define DW_UART_TCR_XFER_MODE_DE_DURING_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 0) +#define DW_UART_TCR_XFER_MODE_SW_DE_OR_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 1) +#define DW_UART_TCR_XFER_MODE_DE_OR_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 2) + /* Component Parameter Register bits */ #define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0) #define DW_UART_CPR_AFCE_MODE (1 << 4) @@ -32,21 +45,6 @@ /* Helper for FIFO size calculation */ #define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16) -static inline u32 dw8250_readl_ext(struct uart_port *p, int offset) -{ - if (p->iotype == UPIO_MEM32BE) - return ioread32be(p->membase + offset); - return readl(p->membase + offset); -} - -static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg) -{ - if (p->iotype == UPIO_MEM32BE) - iowrite32be(reg, p->membase + offset); - else - writel(reg, p->membase + offset); -} - /* * divisor = div(I) + div(F) * "I" means integer, "F" means fractional @@ -87,11 +85,87 @@ void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, struct } EXPORT_SYMBOL_GPL(dw8250_do_set_termios); +static int dw8250_rs485_config(struct uart_port *p, struct serial_rs485 *rs485) +{ + u32 tcr; + + tcr = dw8250_readl_ext(p, DW_UART_TCR); + tcr &= ~DW_UART_TCR_XFER_MODE; + + if (rs485->flags & SER_RS485_ENABLED) { + /* Clear unsupported flags. */ + rs485->flags &= SER_RS485_ENABLED | SER_RS485_RX_DURING_TX | + SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND; + tcr |= DW_UART_TCR_RS485_EN; + + if (rs485->flags & SER_RS485_RX_DURING_TX) { + tcr |= DW_UART_TCR_XFER_MODE_DE_DURING_RE; + } else { + /* HW does not support same DE level for tx and rx */ + if (!(rs485->flags & SER_RS485_RTS_ON_SEND) == + !(rs485->flags & SER_RS485_RTS_AFTER_SEND)) + return -EINVAL; + + tcr |= DW_UART_TCR_XFER_MODE_DE_OR_RE; + } + dw8250_writel_ext(p, DW_UART_DE_EN, 1); + dw8250_writel_ext(p, DW_UART_RE_EN, 1); + } else { + rs485->flags = 0; + + tcr &= ~DW_UART_TCR_RS485_EN; + } + + /* Reset to default polarity */ + tcr |= DW_UART_TCR_DE_POL; + tcr &= ~DW_UART_TCR_RE_POL; + + if (!(rs485->flags & SER_RS485_RTS_ON_SEND)) + tcr &= ~DW_UART_TCR_DE_POL; + if (device_property_read_bool(p->dev, "rs485-rx-active-high")) + tcr |= DW_UART_TCR_RE_POL; + + dw8250_writel_ext(p, DW_UART_TCR, tcr); + + rs485->delay_rts_before_send = 0; + rs485->delay_rts_after_send = 0; + + p->rs485 = *rs485; + + return 0; +} + +/* + * Tests if RE_EN register can have non-zero value to see if RS-485 HW support + * is present. + */ +static bool dw8250_detect_rs485_hw(struct uart_port *p) +{ + u32 reg; + + dw8250_writel_ext(p, DW_UART_RE_EN, 1); + reg = dw8250_readl_ext(p, DW_UART_RE_EN); + dw8250_writel_ext(p, DW_UART_RE_EN, 0); + return reg; +} + void dw8250_setup_port(struct uart_port *p) { + struct dw8250_port_data *pd = p->private_data; + struct dw8250_data *data = to_dw8250_data(pd); struct uart_8250_port *up = up_to_u8250p(p); u32 reg; + pd->hw_rs485_support = dw8250_detect_rs485_hw(p); + if (pd->hw_rs485_support) { + p->rs485_config = dw8250_rs485_config; + } else { + p->rs485_config = serial8250_em485_config; + up->rs485_start_tx = serial8250_em485_start_tx; + up->rs485_stop_tx = serial8250_em485_stop_tx; + } + up->capabilities |= UART_CAP_NOTEMT; + /* * If the Component Version Register returns zero, we know that * ADDITIONAL_FEATURES are not enabled. No need to go any further. @@ -108,14 +182,16 @@ void dw8250_setup_port(struct uart_port *p) dw8250_writel_ext(p, DW_UART_DLF, 0); if (reg) { - struct dw8250_port_data *d = p->private_data; - - d->dlf_size = fls(reg); + pd->dlf_size = fls(reg); p->get_divisor = dw8250_get_divisor; p->set_divisor = dw8250_set_divisor; } reg = dw8250_readl_ext(p, DW_UART_CPR); + if (!reg) { + reg = data->pdata->cpr_val; + dev_dbg(p->dev, "CPR is not available, using 0x%08x instead\n", reg); + } if (!reg) return; @@ -124,7 +200,7 @@ void dw8250_setup_port(struct uart_port *p) p->type = PORT_16550A; p->flags |= UPF_FIXED_TYPE; p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); - up->capabilities = UART_CAP_FIFO; + up->capabilities = UART_CAP_FIFO | UART_CAP_NOTEMT; } if (reg & DW_UART_CPR_AFCE_MODE) diff --git a/drivers/tty/serial/8250/8250_dwlib.h b/drivers/tty/serial/8250/8250_dwlib.h index 83d528e5cc21..055bfdc87985 100644 --- a/drivers/tty/serial/8250/8250_dwlib.h +++ b/drivers/tty/serial/8250/8250_dwlib.h @@ -1,10 +1,16 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* Synopsys DesignWare 8250 library header file. */ +#include <linux/io.h> +#include <linux/notifier.h> #include <linux/types.h> +#include <linux/workqueue.h> #include "8250.h" +struct clk; +struct reset_control; + struct dw8250_port_data { /* Port properties */ int line; @@ -14,7 +20,52 @@ struct dw8250_port_data { /* Hardware configuration */ u8 dlf_size; + + /* RS485 variables */ + bool hw_rs485_support; +}; + +struct dw8250_platform_data { + u8 usr_reg; + u32 cpr_val; + unsigned int quirks; +}; + +struct dw8250_data { + struct dw8250_port_data data; + const struct dw8250_platform_data *pdata; + + int msr_mask_on; + int msr_mask_off; + struct clk *clk; + struct clk *pclk; + struct notifier_block clk_notifier; + struct work_struct clk_work; + struct reset_control *rst; + + unsigned int skip_autocfg:1; + unsigned int uart_16550_compatible:1; }; void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, struct ktermios *old); void dw8250_setup_port(struct uart_port *p); + +static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data) +{ + return container_of(data, struct dw8250_data, data); +} + +static inline u32 dw8250_readl_ext(struct uart_port *p, int offset) +{ + if (p->iotype == UPIO_MEM32BE) + return ioread32be(p->membase + offset); + return readl(p->membase + offset); +} + +static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg) +{ + if (p->iotype == UPIO_MEM32BE) + iowrite32be(reg, p->membase + offset); + else + writel(reg, p->membase + offset); +} diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index 251f0018ae8c..dba5950b8d0e 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -200,12 +200,12 @@ static int fintek_8250_rs485_config(struct uart_port *port, if (!pdata) return -EINVAL; - /* Hardware do not support same RTS level on send and receive */ - if (!(rs485->flags & SER_RS485_RTS_ON_SEND) == - !(rs485->flags & SER_RS485_RTS_AFTER_SEND)) - return -EINVAL; if (rs485->flags & SER_RS485_ENABLED) { + /* Hardware do not support same RTS level on send and receive */ + if (!(rs485->flags & SER_RS485_RTS_ON_SEND) == + !(rs485->flags & SER_RS485_RTS_AFTER_SEND)) + return -EINVAL; memset(rs485->padding, 0, sizeof(rs485->padding)); config |= RS485_URA; } else { diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 21053db93ff1..54051ec7b499 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -54,9 +54,6 @@ #define MTK_UART_TX_TRIGGER 1 #define MTK_UART_RX_TRIGGER MTK_UART_RX_SIZE -#define MTK_UART_FEATURE_SEL 39 /* Feature Selection register */ -#define MTK_UART_FEAT_NEWRMAP BIT(0) /* Use new register map */ - #define MTK_UART_XON1 40 /* I/O: Xon character 1 */ #define MTK_UART_XOFF1 42 /* I/O: Xoff character 1 */ @@ -575,10 +572,6 @@ static int mtk8250_probe(struct platform_device *pdev) uart.dma = data->dma; #endif - /* Set AP UART new register map */ - writel(MTK_UART_FEAT_NEWRMAP, uart.port.membase + - (MTK_UART_FEATURE_SEL << uart.port.regshift)); - /* Disable Rate Fix function */ writel(0x0, uart.port.membase + (MTK_UART_RATE_FIX << uart.port.regshift)); diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index be8626234627..5a699a1aa79c 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -326,6 +326,8 @@ static const struct of_device_id of_platform_serial_table[] = { .data = (void *)PORT_ALTR_16550_F64, }, { .compatible = "altr,16550-FIFO128", .data = (void *)PORT_ALTR_16550_F128, }, + { .compatible = "fsl,16550-FIFO64", + .data = (void *)PORT_16550A_FSL64, }, { .compatible = "mediatek,mtk-btif", .data = (void *)PORT_MTK_BTIF, }, { .compatible = "mrvl,mmp-uart", diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index a293e9f107d0..a17619db7939 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -11,6 +11,7 @@ #include <linux/pci.h> #include <linux/string.h> #include <linux/kernel.h> +#include <linux/math.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/tty.h> @@ -994,41 +995,29 @@ static void pci_ite887x_exit(struct pci_dev *dev) } /* - * EndRun Technologies. - * Determine the number of ports available on the device. + * Oxford Semiconductor Inc. + * Check if an OxSemi device is part of the Tornado range of devices. */ #define PCI_VENDOR_ID_ENDRUN 0x7401 #define PCI_DEVICE_ID_ENDRUN_1588 0xe100 -static int pci_endrun_init(struct pci_dev *dev) +static bool pci_oxsemi_tornado_p(struct pci_dev *dev) { - u8 __iomem *p; - unsigned long deviceID; - unsigned int number_uarts = 0; + /* OxSemi Tornado devices are all 0xCxxx */ + if (dev->vendor == PCI_VENDOR_ID_OXSEMI && + (dev->device & 0xf000) != 0xc000) + return false; - /* EndRun device is all 0xexxx */ + /* EndRun devices are all 0xExxx */ if (dev->vendor == PCI_VENDOR_ID_ENDRUN && - (dev->device & 0xf000) != 0xe000) - return 0; - - p = pci_iomap(dev, 0, 5); - if (p == NULL) - return -ENOMEM; + (dev->device & 0xf000) != 0xe000) + return false; - deviceID = ioread32(p); - /* EndRun device */ - if (deviceID == 0x07000200) { - number_uarts = ioread8(p + 4); - pci_dbg(dev, "%d ports detected on EndRun PCI Express device\n", number_uarts); - } - pci_iounmap(dev, p); - return number_uarts; + return true; } /* - * Oxford Semiconductor Inc. - * Check that device is part of the Tornado range of devices, then determine - * the number of ports available on the device. + * Determine the number of ports available on a Tornado device. */ static int pci_oxsemi_tornado_init(struct pci_dev *dev) { @@ -1036,9 +1025,7 @@ static int pci_oxsemi_tornado_init(struct pci_dev *dev) unsigned long deviceID; unsigned int number_uarts = 0; - /* OxSemi Tornado devices are all 0xCxxx */ - if (dev->vendor == PCI_VENDOR_ID_OXSEMI && - (dev->device & 0xF000) != 0xC000) + if (!pci_oxsemi_tornado_p(dev)) return 0; p = pci_iomap(dev, 0, 5); @@ -1049,12 +1036,217 @@ static int pci_oxsemi_tornado_init(struct pci_dev *dev) /* Tornado device */ if (deviceID == 0x07000200) { number_uarts = ioread8(p + 4); - pci_dbg(dev, "%d ports detected on Oxford PCI Express device\n", number_uarts); + pci_dbg(dev, "%d ports detected on %s PCI Express device\n", + number_uarts, + dev->vendor == PCI_VENDOR_ID_ENDRUN ? + "EndRun" : "Oxford"); } pci_iounmap(dev, p); return number_uarts; } +/* Tornado-specific constants for the TCR and CPR registers; see below. */ +#define OXSEMI_TORNADO_TCR_MASK 0xf +#define OXSEMI_TORNADO_CPR_MASK 0x1ff +#define OXSEMI_TORNADO_CPR_MIN 0x008 +#define OXSEMI_TORNADO_CPR_DEF 0x10f + +/* + * Determine the oversampling rate, the clock prescaler, and the clock + * divisor for the requested baud rate. The clock rate is 62.5 MHz, + * which is four times the baud base, and the prescaler increments in + * steps of 1/8. Therefore to make calculations on integers we need + * to use a scaled clock rate, which is the baud base multiplied by 32 + * (or our assumed UART clock rate multiplied by 2). + * + * The allowed oversampling rates are from 4 up to 16 inclusive (values + * from 0 to 3 inclusive map to 16). Likewise the clock prescaler allows + * values between 1.000 and 63.875 inclusive (operation for values from + * 0.000 to 0.875 has not been specified). The clock divisor is the usual + * unsigned 16-bit integer. + * + * For the most accurate baud rate we use a table of predetermined + * oversampling rates and clock prescalers that records all possible + * products of the two parameters in the range from 4 up to 255 inclusive, + * and additionally 335 for the 1500000bps rate, with the prescaler scaled + * by 8. The table is sorted by the decreasing value of the oversampling + * rate and ties are resolved by sorting by the decreasing value of the + * product. This way preference is given to higher oversampling rates. + * + * We iterate over the table and choose the product of an oversampling + * rate and a clock prescaler that gives the lowest integer division + * result deviation, or if an exact integer divider is found we stop + * looking for it right away. We do some fixup if the resulting clock + * divisor required would be out of its unsigned 16-bit integer range. + * + * Finally we abuse the supposed fractional part returned to encode the + * 4-bit value of the oversampling rate and the 9-bit value of the clock + * prescaler which will end up in the TCR and CPR/CPR2 registers. + */ +static unsigned int pci_oxsemi_tornado_get_divisor(struct uart_port *port, + unsigned int baud, + unsigned int *frac) +{ + static u8 p[][2] = { + { 16, 14, }, { 16, 13, }, { 16, 12, }, { 16, 11, }, + { 16, 10, }, { 16, 9, }, { 16, 8, }, { 15, 17, }, + { 15, 16, }, { 15, 15, }, { 15, 14, }, { 15, 13, }, + { 15, 12, }, { 15, 11, }, { 15, 10, }, { 15, 9, }, + { 15, 8, }, { 14, 18, }, { 14, 17, }, { 14, 14, }, + { 14, 13, }, { 14, 12, }, { 14, 11, }, { 14, 10, }, + { 14, 9, }, { 14, 8, }, { 13, 19, }, { 13, 18, }, + { 13, 17, }, { 13, 13, }, { 13, 12, }, { 13, 11, }, + { 13, 10, }, { 13, 9, }, { 13, 8, }, { 12, 19, }, + { 12, 18, }, { 12, 17, }, { 12, 11, }, { 12, 9, }, + { 12, 8, }, { 11, 23, }, { 11, 22, }, { 11, 21, }, + { 11, 20, }, { 11, 19, }, { 11, 18, }, { 11, 17, }, + { 11, 11, }, { 11, 10, }, { 11, 9, }, { 11, 8, }, + { 10, 25, }, { 10, 23, }, { 10, 20, }, { 10, 19, }, + { 10, 17, }, { 10, 10, }, { 10, 9, }, { 10, 8, }, + { 9, 27, }, { 9, 23, }, { 9, 21, }, { 9, 19, }, + { 9, 18, }, { 9, 17, }, { 9, 9, }, { 9, 8, }, + { 8, 31, }, { 8, 29, }, { 8, 23, }, { 8, 19, }, + { 8, 17, }, { 8, 8, }, { 7, 35, }, { 7, 31, }, + { 7, 29, }, { 7, 25, }, { 7, 23, }, { 7, 21, }, + { 7, 19, }, { 7, 17, }, { 7, 15, }, { 7, 14, }, + { 7, 13, }, { 7, 12, }, { 7, 11, }, { 7, 10, }, + { 7, 9, }, { 7, 8, }, { 6, 41, }, { 6, 37, }, + { 6, 31, }, { 6, 29, }, { 6, 23, }, { 6, 19, }, + { 6, 17, }, { 6, 13, }, { 6, 11, }, { 6, 10, }, + { 6, 9, }, { 6, 8, }, { 5, 67, }, { 5, 47, }, + { 5, 43, }, { 5, 41, }, { 5, 37, }, { 5, 31, }, + { 5, 29, }, { 5, 25, }, { 5, 23, }, { 5, 19, }, + { 5, 17, }, { 5, 15, }, { 5, 13, }, { 5, 11, }, + { 5, 10, }, { 5, 9, }, { 5, 8, }, { 4, 61, }, + { 4, 59, }, { 4, 53, }, { 4, 47, }, { 4, 43, }, + { 4, 41, }, { 4, 37, }, { 4, 31, }, { 4, 29, }, + { 4, 23, }, { 4, 19, }, { 4, 17, }, { 4, 13, }, + { 4, 9, }, { 4, 8, }, + }; + /* Scale the quotient for comparison to get the fractional part. */ + const unsigned int quot_scale = 65536; + unsigned int sclk = port->uartclk * 2; + unsigned int sdiv = DIV_ROUND_CLOSEST(sclk, baud); + unsigned int best_squot; + unsigned int squot; + unsigned int quot; + u16 cpr; + u8 tcr; + int i; + + /* Old custom speed handling. */ + if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) { + unsigned int cust_div = port->custom_divisor; + + quot = cust_div & UART_DIV_MAX; + tcr = (cust_div >> 16) & OXSEMI_TORNADO_TCR_MASK; + cpr = (cust_div >> 20) & OXSEMI_TORNADO_CPR_MASK; + if (cpr < OXSEMI_TORNADO_CPR_MIN) + cpr = OXSEMI_TORNADO_CPR_DEF; + } else { + best_squot = quot_scale; + for (i = 0; i < ARRAY_SIZE(p); i++) { + unsigned int spre; + unsigned int srem; + u8 cp; + u8 tc; + + tc = p[i][0]; + cp = p[i][1]; + spre = tc * cp; + + srem = sdiv % spre; + if (srem > spre / 2) + srem = spre - srem; + squot = DIV_ROUND_CLOSEST(srem * quot_scale, spre); + + if (srem == 0) { + tcr = tc; + cpr = cp; + quot = sdiv / spre; + break; + } else if (squot < best_squot) { + best_squot = squot; + tcr = tc; + cpr = cp; + quot = DIV_ROUND_CLOSEST(sdiv, spre); + } + } + while (tcr <= (OXSEMI_TORNADO_TCR_MASK + 1) >> 1 && + quot % 2 == 0) { + quot >>= 1; + tcr <<= 1; + } + while (quot > UART_DIV_MAX) { + if (tcr <= (OXSEMI_TORNADO_TCR_MASK + 1) >> 1) { + quot >>= 1; + tcr <<= 1; + } else if (cpr <= OXSEMI_TORNADO_CPR_MASK >> 1) { + quot >>= 1; + cpr <<= 1; + } else { + quot = quot * cpr / OXSEMI_TORNADO_CPR_MASK; + cpr = OXSEMI_TORNADO_CPR_MASK; + } + } + } + + *frac = (cpr << 8) | (tcr & OXSEMI_TORNADO_TCR_MASK); + return quot; +} + +/* + * Set the oversampling rate in the transmitter clock cycle register (TCR), + * the clock prescaler in the clock prescaler register (CPR and CPR2), and + * the clock divisor in the divisor latch (DLL and DLM). Note that for + * backwards compatibility any write to CPR clears CPR2 and therefore CPR + * has to be written first, followed by CPR2, which occupies the location + * of CKS used with earlier UART designs. + */ +static void pci_oxsemi_tornado_set_divisor(struct uart_port *port, + unsigned int baud, + unsigned int quot, + unsigned int quot_frac) +{ + struct uart_8250_port *up = up_to_u8250p(port); + u8 cpr2 = quot_frac >> 16; + u8 cpr = quot_frac >> 8; + u8 tcr = quot_frac; + + serial_icr_write(up, UART_TCR, tcr); + serial_icr_write(up, UART_CPR, cpr); + serial_icr_write(up, UART_CKS, cpr2); + serial8250_do_set_divisor(port, baud, quot, 0); +} + +/* + * For Tornado devices we force MCR[7] set for the Divide-by-M N/8 baud rate + * generator prescaler (CPR and CPR2). Otherwise no prescaler would be used. + */ +static void pci_oxsemi_tornado_set_mctrl(struct uart_port *port, + unsigned int mctrl) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + up->mcr |= UART_MCR_CLKSEL; + serial8250_do_set_mctrl(port, mctrl); +} + +static int pci_oxsemi_tornado_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *up, int idx) +{ + struct pci_dev *dev = priv->dev; + + if (pci_oxsemi_tornado_p(dev)) { + up->port.get_divisor = pci_oxsemi_tornado_get_divisor; + up->port.set_divisor = pci_oxsemi_tornado_set_divisor; + up->port.set_mctrl = pci_oxsemi_tornado_set_mctrl; + } + + return pci_default_setup(priv, board, up, idx); +} + static int pci_asix_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) @@ -2244,7 +2436,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = { .device = PCI_ANY_ID, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .init = pci_endrun_init, + .init = pci_oxsemi_tornado_init, .setup = pci_default_setup, }, /* @@ -2256,7 +2448,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .init = pci_oxsemi_tornado_init, - .setup = pci_default_setup, + .setup = pci_oxsemi_tornado_setup, }, { .vendor = PCI_VENDOR_ID_MAINPINE, @@ -2264,7 +2456,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .init = pci_oxsemi_tornado_init, - .setup = pci_default_setup, + .setup = pci_oxsemi_tornado_setup, }, { .vendor = PCI_VENDOR_ID_DIGI, @@ -2272,7 +2464,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = { .subvendor = PCI_SUBVENDOR_ID_IBM, .subdevice = PCI_ANY_ID, .init = pci_oxsemi_tornado_init, - .setup = pci_default_setup, + .setup = pci_oxsemi_tornado_setup, }, { .vendor = PCI_VENDOR_ID_INTEL, @@ -2589,7 +2781,7 @@ enum pci_board_num_t { pbn_b0_2_1843200, pbn_b0_4_1843200, - pbn_b0_1_3906250, + pbn_b0_1_15625000, pbn_b0_bt_1_115200, pbn_b0_bt_2_115200, @@ -2667,12 +2859,11 @@ enum pci_board_num_t { pbn_panacom2, pbn_panacom4, pbn_plx_romulus, - pbn_endrun_2_3906250, pbn_oxsemi, - pbn_oxsemi_1_3906250, - pbn_oxsemi_2_3906250, - pbn_oxsemi_4_3906250, - pbn_oxsemi_8_3906250, + pbn_oxsemi_1_15625000, + pbn_oxsemi_2_15625000, + pbn_oxsemi_4_15625000, + pbn_oxsemi_8_15625000, pbn_intel_i960, pbn_sgi_ioc3, pbn_computone_4, @@ -2815,10 +3006,10 @@ static struct pciserial_board pci_boards[] = { .uart_offset = 8, }, - [pbn_b0_1_3906250] = { + [pbn_b0_1_15625000] = { .flags = FL_BASE0, .num_ports = 1, - .base_baud = 3906250, + .base_baud = 15625000, .uart_offset = 8, }, @@ -3190,20 +3381,6 @@ static struct pciserial_board pci_boards[] = { }, /* - * EndRun Technologies - * Uses the size of PCI Base region 0 to - * signal now many ports are available - * 2 port 952 Uart support - */ - [pbn_endrun_2_3906250] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 3906250, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - - /* * This board uses the size of PCI Base region 0 to * signal now many ports are available */ @@ -3213,31 +3390,31 @@ static struct pciserial_board pci_boards[] = { .base_baud = 115200, .uart_offset = 8, }, - [pbn_oxsemi_1_3906250] = { + [pbn_oxsemi_1_15625000] = { .flags = FL_BASE0, .num_ports = 1, - .base_baud = 3906250, + .base_baud = 15625000, .uart_offset = 0x200, .first_offset = 0x1000, }, - [pbn_oxsemi_2_3906250] = { + [pbn_oxsemi_2_15625000] = { .flags = FL_BASE0, .num_ports = 2, - .base_baud = 3906250, + .base_baud = 15625000, .uart_offset = 0x200, .first_offset = 0x1000, }, - [pbn_oxsemi_4_3906250] = { + [pbn_oxsemi_4_15625000] = { .flags = FL_BASE0, .num_ports = 4, - .base_baud = 3906250, + .base_baud = 15625000, .uart_offset = 0x200, .first_offset = 0x1000, }, - [pbn_oxsemi_8_3906250] = { + [pbn_oxsemi_8_15625000] = { .flags = FL_BASE0, .num_ports = 8, - .base_baud = 3906250, + .base_baud = 15625000, .uart_offset = 0x200, .first_offset = 0x1000, }, @@ -3518,6 +3695,12 @@ static struct pciserial_board pci_boards[] = { }, }; +#define REPORT_CONFIG(option) \ + (IS_ENABLED(CONFIG_##option) ? 0 : (kernel_ulong_t)&#option) +#define REPORT_8250_CONFIG(option) \ + (IS_ENABLED(CONFIG_SERIAL_8250_##option) ? \ + 0 : (kernel_ulong_t)&"SERIAL_8250_"#option) + static const struct pci_device_id blacklist[] = { /* softmodems */ { PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */ @@ -3525,40 +3708,43 @@ static const struct pci_device_id blacklist[] = { { PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */ /* multi-io cards handled by parport_serial */ - { PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */ - { PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */ - { PCI_DEVICE(0x1c00, 0x3250), }, /* WCH CH382 2S1P */ + /* WCH CH353 2S1P */ + { PCI_DEVICE(0x4348, 0x7053), 0, 0, REPORT_CONFIG(PARPORT_SERIAL), }, + /* WCH CH353 1S1P */ + { PCI_DEVICE(0x4348, 0x5053), 0, 0, REPORT_CONFIG(PARPORT_SERIAL), }, + /* WCH CH382 2S1P */ + { PCI_DEVICE(0x1c00, 0x3250), 0, 0, REPORT_CONFIG(PARPORT_SERIAL), }, /* Intel platforms with MID UART */ - { PCI_VDEVICE(INTEL, 0x081b), }, - { PCI_VDEVICE(INTEL, 0x081c), }, - { PCI_VDEVICE(INTEL, 0x081d), }, - { PCI_VDEVICE(INTEL, 0x1191), }, - { PCI_VDEVICE(INTEL, 0x18d8), }, - { PCI_VDEVICE(INTEL, 0x19d8), }, + { PCI_VDEVICE(INTEL, 0x081b), REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x081c), REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x081d), REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x1191), REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x18d8), REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x19d8), REPORT_8250_CONFIG(MID), }, /* Intel platforms with DesignWare UART */ - { PCI_VDEVICE(INTEL, 0x0936), }, - { PCI_VDEVICE(INTEL, 0x0f0a), }, - { PCI_VDEVICE(INTEL, 0x0f0c), }, - { PCI_VDEVICE(INTEL, 0x228a), }, - { PCI_VDEVICE(INTEL, 0x228c), }, - { PCI_VDEVICE(INTEL, 0x4b96), }, - { PCI_VDEVICE(INTEL, 0x4b97), }, - { PCI_VDEVICE(INTEL, 0x4b98), }, - { PCI_VDEVICE(INTEL, 0x4b99), }, - { PCI_VDEVICE(INTEL, 0x4b9a), }, - { PCI_VDEVICE(INTEL, 0x4b9b), }, - { PCI_VDEVICE(INTEL, 0x9ce3), }, - { PCI_VDEVICE(INTEL, 0x9ce4), }, + { PCI_VDEVICE(INTEL, 0x0936), REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x0f0a), REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x0f0c), REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x228a), REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x228c), REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b96), REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b97), REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b98), REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b99), REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b9a), REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b9b), REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x9ce3), REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x9ce4), REPORT_8250_CONFIG(LPSS), }, /* Exar devices */ - { PCI_VDEVICE(EXAR, PCI_ANY_ID), }, - { PCI_VDEVICE(COMMTECH, PCI_ANY_ID), }, + { PCI_VDEVICE(EXAR, PCI_ANY_ID), REPORT_8250_CONFIG(EXAR), }, + { PCI_VDEVICE(COMMTECH, PCI_ANY_ID), REPORT_8250_CONFIG(EXAR), }, /* Pericom devices */ - { PCI_VDEVICE(PERICOM, PCI_ANY_ID), }, - { PCI_VDEVICE(ACCESSIO, PCI_ANY_ID), }, + { PCI_VDEVICE(PERICOM, PCI_ANY_ID), REPORT_8250_CONFIG(PERICOM), }, + { PCI_VDEVICE(ACCESSIO, PCI_ANY_ID), REPORT_8250_CONFIG(PERICOM), }, /* End of the black list */ { } @@ -3840,8 +4026,12 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) board = &pci_boards[ent->driver_data]; exclude = pci_match_id(blacklist, dev); - if (exclude) + if (exclude) { + if (exclude->driver_data) + pci_warn(dev, "ignoring port, enable %s to handle\n", + (const char *)exclude->driver_data); return -ENODEV; + } rc = pcim_enable_device(dev); pci_save_state(dev); @@ -4110,13 +4300,6 @@ static const struct pci_device_id serial_pci_tbl[] = { 0x10b5, 0x106a, 0, 0, pbn_plx_romulus }, /* - * EndRun Technologies. PCI express device range. - * EndRun PTP/1588 has 2 Native UARTs. - */ - { PCI_VENDOR_ID_ENDRUN, PCI_DEVICE_ID_ENDRUN_1588, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_endrun_2_3906250 }, - /* * Quatech cards. These actually have configurable clocks but for * now we just use the default. * @@ -4225,158 +4408,165 @@ static const struct pci_device_id serial_pci_tbl[] = { */ { PCI_VENDOR_ID_OXSEMI, 0xc101, /* OXPCIe952 1 Legacy UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_3906250 }, + pbn_b0_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc105, /* OXPCIe952 1 Legacy UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_3906250 }, + pbn_b0_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc11b, /* OXPCIe952 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc11f, /* OXPCIe952 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc120, /* OXPCIe952 1 Legacy UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_3906250 }, + pbn_b0_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc124, /* OXPCIe952 1 Legacy UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_3906250 }, + pbn_b0_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc138, /* OXPCIe952 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc13d, /* OXPCIe952 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc140, /* OXPCIe952 1 Legacy UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_3906250 }, + pbn_b0_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc141, /* OXPCIe952 1 Legacy UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_3906250 }, + pbn_b0_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc144, /* OXPCIe952 1 Legacy UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_3906250 }, + pbn_b0_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc145, /* OXPCIe952 1 Legacy UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_3906250 }, + pbn_b0_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc158, /* OXPCIe952 2 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_3906250 }, + pbn_oxsemi_2_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc15d, /* OXPCIe952 2 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_3906250 }, + pbn_oxsemi_2_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc208, /* OXPCIe954 4 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_4_3906250 }, + pbn_oxsemi_4_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc20d, /* OXPCIe954 4 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_4_3906250 }, + pbn_oxsemi_4_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc308, /* OXPCIe958 8 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_8_3906250 }, + pbn_oxsemi_8_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc30d, /* OXPCIe958 8 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_8_3906250 }, + pbn_oxsemi_8_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc40b, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc40f, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc41b, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc41f, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc42b, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc42f, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc43b, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc43f, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc44b, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc44f, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc45b, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc45f, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc46b, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc46f, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc47b, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc47f, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc48b, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc48f, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc49b, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc49f, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc4ab, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc4af, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc4bb, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc4bf, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc4cb, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_OXSEMI, 0xc4cf, /* OXPCIe200 1 Native UART */ PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, /* * Mainpine Inc. IQ Express "Rev3" utilizing OxSemi Tornado */ { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 1 Port V.34 Super-G3 Fax */ PCI_VENDOR_ID_MAINPINE, 0x4001, 0, 0, - pbn_oxsemi_1_3906250 }, + pbn_oxsemi_1_15625000 }, { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 2 Port V.34 Super-G3 Fax */ PCI_VENDOR_ID_MAINPINE, 0x4002, 0, 0, - pbn_oxsemi_2_3906250 }, + pbn_oxsemi_2_15625000 }, { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 4 Port V.34 Super-G3 Fax */ PCI_VENDOR_ID_MAINPINE, 0x4004, 0, 0, - pbn_oxsemi_4_3906250 }, + pbn_oxsemi_4_15625000 }, { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 8 Port V.34 Super-G3 Fax */ PCI_VENDOR_ID_MAINPINE, 0x4008, 0, 0, - pbn_oxsemi_8_3906250 }, + pbn_oxsemi_8_15625000 }, /* * Digi/IBM PCIe 2-port Async EIA-232 Adapter utilizing OxSemi Tornado */ { PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_2_OX_IBM, PCI_SUBVENDOR_ID_IBM, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_3906250 }, + pbn_oxsemi_2_15625000 }, + /* + * EndRun Technologies. PCI express device range. + * EndRun PTP/1588 has 2 Native UARTs utilizing OxSemi 952. + */ + { PCI_VENDOR_ID_ENDRUN, PCI_DEVICE_ID_ENDRUN_1588, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_15625000 }, /* * SBS Technologies, Inc. P-Octal and PMC-OCTPRO cards, diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 1fbd5bf264be..78b6dedc43e6 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -263,7 +263,7 @@ static const struct serial8250_config uart_config[] = { .tx_loadsz = 63, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | UART_FCR7_64BYTE, - .flags = UART_CAP_FIFO, + .flags = UART_CAP_FIFO | UART_CAP_NOTEMT, }, [PORT_RT2880] = { .name = "Palmchip BK-3103", @@ -538,27 +538,6 @@ serial_port_out_sync(struct uart_port *p, int offset, int value) } /* - * For the 16C950 - */ -static void serial_icr_write(struct uart_8250_port *up, int offset, int value) -{ - serial_out(up, UART_SCR, offset); - serial_out(up, UART_ICR, value); -} - -static unsigned int serial_icr_read(struct uart_8250_port *up, int offset) -{ - unsigned int value; - - serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD); - serial_out(up, UART_SCR, offset); - value = serial_in(up, UART_ICR); - serial_icr_write(up, UART_ACR, up->acr); - - return value; -} - -/* * FIFO support. */ static void serial8250_clear_fifos(struct uart_8250_port *p) @@ -1504,18 +1483,19 @@ static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec) hrtimer_start(hrt, ms_to_ktime(msec), HRTIMER_MODE_REL); } -static void __stop_tx_rs485(struct uart_8250_port *p) +static void __stop_tx_rs485(struct uart_8250_port *p, u64 stop_delay) { struct uart_8250_em485 *em485 = p->em485; + stop_delay += (u64)p->port.rs485.delay_rts_after_send * NSEC_PER_MSEC; + /* * rs485_stop_tx() is going to set RTS according to config * AND flush RX FIFO if required. */ - if (p->port.rs485.delay_rts_after_send > 0) { + if (stop_delay > 0) { em485->active_timer = &em485->stop_tx_timer; - start_hrtimer_ms(&em485->stop_tx_timer, - p->port.rs485.delay_rts_after_send); + hrtimer_start(&em485->stop_tx_timer, ns_to_ktime(stop_delay), HRTIMER_MODE_REL); } else { p->rs485_stop_tx(p); em485->active_timer = NULL; @@ -1535,16 +1515,32 @@ static inline void __stop_tx(struct uart_8250_port *p) if (em485) { unsigned char lsr = serial_in(p, UART_LSR); + u64 stop_delay = 0; + + if (!(lsr & UART_LSR_THRE)) + return; /* * To provide required timeing and allow FIFO transfer, * __stop_tx_rs485() must be called only when both FIFO and - * shift register are empty. It is for device driver to enable - * interrupt on TEMT. + * shift register are empty. The device driver should either + * enable interrupt on TEMT or set UART_CAP_NOTEMT that will + * enlarge stop_tx_timer by the tx time of one frame to cover + * for emptying of the shift register. */ - if ((lsr & BOTH_EMPTY) != BOTH_EMPTY) - return; + if (!(lsr & UART_LSR_TEMT)) { + if (!(p->capabilities & UART_CAP_NOTEMT)) + return; + /* + * RTS might get deasserted too early with the normal + * frame timing formula. It seems to suggest THRE might + * get asserted already during tx of the stop bit + * rather than after it is fully sent. + * Roughly estimate 1 extra bit here with / 7. + */ + stop_delay = p->port.frame_time + DIV_ROUND_UP(p->port.frame_time, 7); + } - __stop_tx_rs485(p); + __stop_tx_rs485(p, stop_delay); } __do_stop_tx(p); } @@ -1948,9 +1944,12 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) status = serial8250_rx_chars(up, status); } serial8250_modem_status(up); - if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE) && - (up->ier & UART_IER_THRI)) - serial8250_tx_chars(up); + if ((status & UART_LSR_THRE) && (up->ier & UART_IER_THRI)) { + if (!up->dma || up->dma->tx_err) + serial8250_tx_chars(up); + else + __stop_tx(up); + } uart_unlock_and_check_sysrq_irqrestore(port, flags); @@ -2077,10 +2076,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) serial8250_rpm_put(up); } -/* - * Wait for transmitter & holding register to empty - */ -static void wait_for_xmitr(struct uart_8250_port *up, int bits) +static void wait_for_lsr(struct uart_8250_port *up, int bits) { unsigned int status, tmout = 10000; @@ -2097,6 +2093,16 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits) udelay(1); touch_nmi_watchdog(); } +} + +/* + * Wait for transmitter & holding register to empty + */ +static void wait_for_xmitr(struct uart_8250_port *up, int bits) +{ + unsigned int tmout; + + wait_for_lsr(up, bits); /* Wait up to 1s for flow control if necessary */ if (up->port.flags & UPF_CONS_FLOW) { @@ -2614,10 +2620,8 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, } if (!(c_cflag & PARODD)) cval |= UART_LCR_EPAR; -#ifdef CMSPAR if (c_cflag & CMSPAR) cval |= UART_LCR_SPAR; -#endif return cval; } @@ -3333,6 +3337,35 @@ static void serial8250_console_restore(struct uart_8250_port *up) } /* + * Print a string to the serial port using the device FIFO + * + * It sends fifosize bytes and then waits for the fifo + * to get empty. + */ +static void serial8250_console_fifo_write(struct uart_8250_port *up, + const char *s, unsigned int count) +{ + int i; + const char *end = s + count; + unsigned int fifosize = up->tx_loadsz; + bool cr_sent = false; + + while (s != end) { + wait_for_lsr(up, UART_LSR_THRE); + + for (i = 0; i < fifosize && s != end; ++i) { + if (*s == '\n' && !cr_sent) { + serial_out(up, UART_TX, '\r'); + cr_sent = true; + } else { + serial_out(up, UART_TX, *s++); + cr_sent = false; + } + } + } +} + +/* * Print a string to the serial port trying not to disturb * any possible real use of the port... * @@ -3347,7 +3380,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, struct uart_8250_em485 *em485 = up->em485; struct uart_port *port = &up->port; unsigned long flags; - unsigned int ier; + unsigned int ier, use_fifo; int locked = 1; touch_nmi_watchdog(); @@ -3379,7 +3412,30 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, mdelay(port->rs485.delay_rts_before_send); } - uart_console_write(port, s, count, serial8250_console_putchar); + use_fifo = (up->capabilities & UART_CAP_FIFO) && + /* + * BCM283x requires to check the fifo + * after each byte. + */ + !(up->capabilities & UART_CAP_MINI) && + /* + * tx_loadsz contains the transmit fifo size + */ + up->tx_loadsz > 1 && + (up->fcr & UART_FCR_ENABLE_FIFO) && + port->state && + test_bit(TTY_PORT_INITIALIZED, &port->state->port.iflags) && + /* + * After we put a data in the fifo, the controller will send + * it regardless of the CTS state. Therefore, only use fifo + * if we don't use control flow. + */ + !(up->port.flags & UPF_CONS_FLOW); + + if (likely(use_fifo)) + serial8250_console_fifo_write(up, s, count); + else + uart_console_write(port, s, count, serial8250_console_putchar); /* * Finally, wait for transmitter to become empty diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c index 33ca98bfa5b3..795e55142d4c 100644 --- a/drivers/tty/serial/8250/8250_pxa.c +++ b/drivers/tty/serial/8250/8250_pxa.c @@ -22,7 +22,6 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/clk.h> -#include <linux/pm_runtime.h> #include "8250.h" diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index cd93ea6eed65..fdb6c4188695 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -380,7 +380,7 @@ config SERIAL_8250_DW config SERIAL_8250_EM tristate "Support for Emma Mobile integrated serial port" depends on SERIAL_8250 && HAVE_CLK - depends on (ARM && ARCH_RENESAS) || COMPILE_TEST + depends on ARCH_RENESAS || COMPILE_TEST help Selecting this option will add support for the integrated serial port hardware found on the Emma Mobile line of processors. |