From 863981e96738983919de841ec669e157e6bdaeb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Fabian=20Silva=20Delgado?= Date: Sun, 11 Sep 2016 04:34:46 -0300 Subject: Linux-libre 4.7.1-gnu --- drivers/tty/serial/8250/8250.h | 15 +- drivers/tty/serial/8250/8250_core.c | 3 + drivers/tty/serial/8250/8250_dma.c | 68 ++- drivers/tty/serial/8250/8250_dw.c | 8 +- drivers/tty/serial/8250/8250_fintek.c | 118 +--- drivers/tty/serial/8250/8250_mid.c | 9 +- drivers/tty/serial/8250/8250_of.c | 2 +- drivers/tty/serial/8250/8250_omap.c | 93 ++-- drivers/tty/serial/8250/8250_pci.c | 20 +- drivers/tty/serial/8250/8250_port.c | 38 +- drivers/tty/serial/8250/8250_uniphier.c | 2 +- drivers/tty/serial/8250/Kconfig | 24 +- drivers/tty/serial/8250/Makefile | 2 +- drivers/tty/serial/Kconfig | 41 +- drivers/tty/serial/Makefile | 2 + drivers/tty/serial/amba-pl011.c | 3 +- drivers/tty/serial/crisv10.c | 30 +- drivers/tty/serial/ifx6x60.c | 2 +- drivers/tty/serial/imx.c | 174 ++++-- drivers/tty/serial/max310x.c | 12 +- drivers/tty/serial/meson_uart.c | 42 +- drivers/tty/serial/mps2-uart.c | 625 +++++++++++++++++++++ drivers/tty/serial/msm_serial.c | 101 ++-- drivers/tty/serial/mvebu-uart.c | 31 +- drivers/tty/serial/mxs-auart.c | 644 ++++++++++++++++----- drivers/tty/serial/pic32_uart.c | 960 ++++++++++++++++++++++++++++++++ drivers/tty/serial/pic32_uart.h | 126 +++++ drivers/tty/serial/sc16is7xx.c | 29 +- drivers/tty/serial/serial-tegra.c | 2 - drivers/tty/serial/serial_core.c | 430 +++++++++----- drivers/tty/serial/serial_mctrl_gpio.c | 7 +- drivers/tty/serial/serial_mctrl_gpio.h | 6 +- drivers/tty/serial/sirfsoc_uart.c | 39 +- drivers/tty/serial/sprd_serial.c | 2 +- drivers/tty/serial/uartlite.c | 12 +- 35 files changed, 3026 insertions(+), 696 deletions(-) create mode 100644 drivers/tty/serial/mps2-uart.c create mode 100644 drivers/tty/serial/pic32_uart.c create mode 100644 drivers/tty/serial/pic32_uart.h (limited to 'drivers/tty/serial') diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 047a7ba67..215a99237 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -17,7 +17,7 @@ struct uart_8250_dma { int (*tx_dma)(struct uart_8250_port *p); - int (*rx_dma)(struct uart_8250_port *p, unsigned int iir); + int (*rx_dma)(struct uart_8250_port *p); /* Filter function */ dma_filter_fn fn; @@ -84,7 +84,6 @@ struct serial8250_config { #define UART_BUG_THRE (1 << 3) /* UART has buggy THRE reassertion */ #define UART_BUG_PARITY (1 << 4) /* UART mishandles parity if FIFO enabled */ -#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) #ifdef CONFIG_SERIAL_8250_SHARE_IRQ #define SERIAL8250_SHARE_IRQS 1 @@ -151,6 +150,12 @@ static inline int serial8250_pnp_init(void) { return 0; } static inline void serial8250_pnp_exit(void) { } #endif +#ifdef CONFIG_SERIAL_8250_FINTEK +int fintek_8250_probe(struct uart_8250_port *uart); +#else +static inline int fintek_8250_probe(struct uart_8250_port *uart) { return 0; } +#endif + #ifdef CONFIG_ARCH_OMAP1 static inline int is_omap1_8250(struct uart_8250_port *pt) { @@ -190,7 +195,8 @@ static inline int is_omap1510_8250(struct uart_8250_port *pt) #ifdef CONFIG_SERIAL_8250_DMA extern int serial8250_tx_dma(struct uart_8250_port *); -extern int serial8250_rx_dma(struct uart_8250_port *, unsigned int iir); +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 *); #else @@ -198,10 +204,11 @@ static inline int serial8250_tx_dma(struct uart_8250_port *p) { return -1; } -static inline int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) +static inline int serial8250_rx_dma(struct uart_8250_port *p) { return -1; } +static inline void serial8250_rx_dma_flush(struct uart_8250_port *p) { } static inline int serial8250_request_dma(struct uart_8250_port *p) { return -1; diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 2f4f5ee65..0fbd7c033 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -830,6 +830,7 @@ static int serial8250_probe(struct platform_device *dev) uart.port.handle_irq = p->handle_irq; uart.port.handle_break = p->handle_break; uart.port.set_termios = p->set_termios; + uart.port.get_mctrl = p->get_mctrl; uart.port.pm = p->pm; uart.port.dev = &dev->dev; uart.port.irqflags |= irqflag; @@ -1022,6 +1023,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up) /* Possibly override set_termios call */ if (up->port.set_termios) uart->port.set_termios = up->port.set_termios; + if (up->port.get_mctrl) + uart->port.get_mctrl = up->port.get_mctrl; if (up->port.set_mctrl) uart->port.set_mctrl = up->port.set_mctrl; if (up->port.startup) diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 78259d3c6..7f33d1c8d 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -110,30 +110,11 @@ err: return ret; } -int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) +int serial8250_rx_dma(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; struct dma_async_tx_descriptor *desc; - switch (iir & 0x3f) { - case UART_IIR_RLSI: - /* 8250_core handles errors and break interrupts */ - return -EIO; - case UART_IIR_RX_TIMEOUT: - /* - * If RCVR FIFO trigger level was not reached, complete the - * transfer and let 8250_core copy the remaining data. - */ - if (dma->rx_running) { - dmaengine_pause(dma->rxchan); - __dma_rx_complete(p); - dmaengine_terminate_all(dma->rxchan); - } - return -ETIMEDOUT; - default: - break; - } - if (dma->rx_running) return 0; @@ -154,10 +135,23 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) return 0; } +void serial8250_rx_dma_flush(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + if (dma->rx_running) { + dmaengine_pause(dma->rxchan); + __dma_rx_complete(p); + dmaengine_terminate_all(dma->rxchan); + } +} + int serial8250_request_dma(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; dma_cap_mask_t mask; + struct dma_slave_caps caps; + int ret; /* Default slave configuration parameters */ dma->rxconf.direction = DMA_DEV_TO_MEM; @@ -178,6 +172,16 @@ int serial8250_request_dma(struct uart_8250_port *p) if (!dma->rxchan) return -ENODEV; + /* 8250 rx dma requires dmaengine driver to support pause/terminate */ + ret = dma_get_slave_caps(dma->rxchan, &caps); + if (ret) + goto release_rx; + if (!caps.cmd_pause || !caps.cmd_terminate || + caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR) { + ret = -EINVAL; + goto release_rx; + } + dmaengine_slave_config(dma->rxchan, &dma->rxconf); /* Get a channel for TX */ @@ -185,8 +189,17 @@ int serial8250_request_dma(struct uart_8250_port *p) dma->fn, dma->tx_param, p->port.dev, "tx"); if (!dma->txchan) { - dma_release_channel(dma->rxchan); - return -ENODEV; + ret = -ENODEV; + goto release_rx; + } + + /* 8250 tx dma requires dmaengine driver to support terminate */ + ret = dma_get_slave_caps(dma->txchan, &caps); + if (ret) + goto err; + if (!caps.cmd_terminate) { + ret = -EINVAL; + goto err; } dmaengine_slave_config(dma->txchan, &dma->txconf); @@ -197,8 +210,10 @@ int serial8250_request_dma(struct uart_8250_port *p) dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size, &dma->rx_addr, GFP_KERNEL); - if (!dma->rx_buf) + if (!dma->rx_buf) { + ret = -ENOMEM; goto err; + } /* TX buffer */ dma->tx_addr = dma_map_single(dma->txchan->device->dev, @@ -208,6 +223,7 @@ int serial8250_request_dma(struct uart_8250_port *p) if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) { dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf, dma->rx_addr); + ret = -ENOMEM; goto err; } @@ -215,10 +231,10 @@ int serial8250_request_dma(struct uart_8250_port *p) return 0; err: - dma_release_channel(dma->rxchan); dma_release_channel(dma->txchan); - - return -ENOMEM; +release_rx: + dma_release_channel(dma->rxchan); + return ret; } EXPORT_SYMBOL_GPL(serial8250_request_dma); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index a3fb95d85..e19969614 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -104,15 +104,16 @@ static void dw8250_check_lcr(struct uart_port *p, int value) dw8250_force_idle(p); #ifdef CONFIG_64BIT - __raw_writeq(value & 0xff, offset); -#else + if (p->type == PORT_OCTEON) + __raw_writeq(value & 0xff, offset); + else +#endif if (p->iotype == UPIO_MEM32) writel(value, offset); else if (p->iotype == UPIO_MEM32BE) iowrite32be(value, offset); else writeb(value, offset); -#endif } /* * FIXME: this deadlocks if port->lock is already held @@ -617,6 +618,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = { { "8086228A", 0 }, { "APMC0D08", 0}, { "AMD0020", 0 }, + { "AMDI0020", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match); diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index 89474399a..870981dd9 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -1,9 +1,7 @@ /* * Probe for F81216A LPC to 4 UART * - * Based on drivers/tty/serial/8250_pnp.c, by Russell King, et al - * - * Copyright (C) 2014 Ricardo Ribalda, Qtechnology A/S + * Copyright (C) 2014-2016 Ricardo Ribalda, Qtechnology A/S * * * This program is free software; you can redistribute it and/or modify @@ -38,19 +36,15 @@ #define RXW4C_IRA BIT(3) #define TXW4C_IRA BIT(2) -#define DRIVER_NAME "8250_fintek" - struct fintek_8250 { u16 base_port; u8 index; u8 key; - long line; }; static int fintek_8250_enter_key(u16 base_port, u8 key) { - - if (!request_muxed_region(base_port, 2, DRIVER_NAME)) + if (!request_muxed_region(base_port, 2, "8250_fintek")) return -EBUSY; outb(key, base_port + ADDR_PORT); @@ -138,7 +132,7 @@ static int fintek_8250_rs485_config(struct uart_port *port, return 0; } -static int fintek_8250_base_port(u16 io_address, u8 *key, u8 *index) +static int find_base_port(struct fintek_8250 *pdata, u16 io_address) { static const u16 addr[] = {0x4e, 0x2e}; static const u8 keys[] = {0x77, 0xa0, 0x87, 0x67}; @@ -168,10 +162,13 @@ static int fintek_8250_base_port(u16 io_address, u8 *key, u8 *index) continue; fintek_8250_exit_key(addr[i]); - *key = keys[j]; - *index = k; - return addr[i]; + pdata->key = keys[j]; + pdata->base_port = addr[i]; + pdata->index = k; + + return 0; } + fintek_8250_exit_key(addr[i]); } } @@ -179,104 +176,21 @@ static int fintek_8250_base_port(u16 io_address, u8 *key, u8 *index) return -ENODEV; } -static int -fintek_8250_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) +int fintek_8250_probe(struct uart_8250_port *uart) { - struct uart_8250_port uart; struct fintek_8250 *pdata; - int base_port; - u8 key; - u8 index; - - if (!pnp_port_valid(dev, 0)) - return -ENODEV; + struct fintek_8250 probe_data; - base_port = fintek_8250_base_port(pnp_port_start(dev, 0), &key, &index); - if (base_port < 0) + if (find_base_port(&probe_data, uart->port.iobase)) return -ENODEV; - memset(&uart, 0, sizeof(uart)); - - pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL); + pdata = devm_kzalloc(uart->port.dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; - uart.port.private_data = pdata; - if (!pnp_irq_valid(dev, 0)) - return -ENODEV; - uart.port.irq = pnp_irq(dev, 0); - uart.port.iobase = pnp_port_start(dev, 0); - uart.port.iotype = UPIO_PORT; - uart.port.rs485_config = fintek_8250_rs485_config; - - uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; - if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE) - uart.port.flags |= UPF_SHARE_IRQ; - uart.port.uartclk = 1843200; - uart.port.dev = &dev->dev; - - pdata->key = key; - pdata->base_port = base_port; - pdata->index = index; - pdata->line = serial8250_register_8250_port(&uart); - if (pdata->line < 0) - return -ENODEV; + memcpy(pdata, &probe_data, sizeof(probe_data)); + uart->port.rs485_config = fintek_8250_rs485_config; + uart->port.private_data = pdata; - pnp_set_drvdata(dev, pdata); return 0; } - -static void fintek_8250_remove(struct pnp_dev *dev) -{ - struct fintek_8250 *pdata = pnp_get_drvdata(dev); - - if (pdata) - serial8250_unregister_port(pdata->line); -} - -#ifdef CONFIG_PM -static int fintek_8250_suspend(struct pnp_dev *dev, pm_message_t state) -{ - struct fintek_8250 *pdata = pnp_get_drvdata(dev); - - if (!pdata) - return -ENODEV; - serial8250_suspend_port(pdata->line); - return 0; -} - -static int fintek_8250_resume(struct pnp_dev *dev) -{ - struct fintek_8250 *pdata = pnp_get_drvdata(dev); - - if (!pdata) - return -ENODEV; - serial8250_resume_port(pdata->line); - return 0; -} -#else -#define fintek_8250_suspend NULL -#define fintek_8250_resume NULL -#endif /* CONFIG_PM */ - -static const struct pnp_device_id fintek_dev_table[] = { - /* Qtechnology Panel PC / IO1000 */ - { "PNP0501"}, - {} -}; - -MODULE_DEVICE_TABLE(pnp, fintek_dev_table); - -static struct pnp_driver fintek_8250_driver = { - .name = DRIVER_NAME, - .probe = fintek_8250_probe, - .remove = fintek_8250_remove, - .suspend = fintek_8250_suspend, - .resume = fintek_8250_resume, - .id_table = fintek_dev_table, -}; - -module_pnp_driver(fintek_8250_driver); -MODULE_DESCRIPTION("Fintek F812164 module"); -MODULE_AUTHOR("Ricardo Ribalda "); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index ed489880e..86379a79a 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -9,9 +9,10 @@ * published by the Free Software Foundation. */ -#include +#include #include #include +#include #include #include @@ -79,7 +80,11 @@ 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 */ + /* + * Device 0000:00:04.0 is not a real HSU port. It provides a global + * register set for all HSU ports, although it has the same PCI ID. + * Skip it here. + */ if (index-- == 0) return -ENODEV; diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index c7ed3d2bc..38963d7bc 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -29,7 +29,7 @@ struct of_serial_info { }; #ifdef CONFIG_ARCH_TEGRA -void tegra_serial_handle_break(struct uart_port *p) +static void tegra_serial_handle_break(struct uart_port *p) { unsigned int status, tmout = 10000; diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 6f760510e..2c44c792d 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -115,6 +115,12 @@ struct omap8250_priv { bool rx_dma_broken; }; +#ifdef CONFIG_SERIAL_8250_DMA +static void omap_8250_rx_dma_flush(struct uart_8250_port *p); +#else +static inline void omap_8250_rx_dma_flush(struct uart_8250_port *p) { } +#endif + static u32 uart_read(struct uart_8250_port *up, u32 reg) { return readl(up->port.membase + (reg << up->port.regshift)); @@ -635,7 +641,7 @@ static int omap_8250_startup(struct uart_port *port) serial_out(up, UART_OMAP_WER, priv->wer); if (up->dma) - up->dma->rx_dma(up, 0); + up->dma->rx_dma(up); pm_runtime_mark_last_busy(port->dev); pm_runtime_put_autosuspend(port->dev); @@ -654,7 +660,7 @@ static void omap_8250_shutdown(struct uart_port *port) flush_work(&priv->qos_work); if (up->dma) - up->dma->rx_dma(up, UART_IIR_RX_TIMEOUT); + omap_8250_rx_dma_flush(up); pm_runtime_get_sync(port->dev); @@ -742,9 +748,9 @@ static void omap_8250_unthrottle(struct uart_port *port) } #ifdef CONFIG_SERIAL_8250_DMA -static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir); +static int omap_8250_rx_dma(struct uart_8250_port *p); -static void __dma_rx_do_complete(struct uart_8250_port *p, bool error) +static void __dma_rx_do_complete(struct uart_8250_port *p) { struct omap8250_priv *priv = p->port.private_data; struct uart_8250_dma *dma = p->dma; @@ -754,9 +760,6 @@ static void __dma_rx_do_complete(struct uart_8250_port *p, bool error) unsigned long flags; int ret; - dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr, - dma->rx_size, DMA_FROM_DEVICE); - spin_lock_irqsave(&priv->rx_dma_lock, flags); if (!dma->rx_running) @@ -764,7 +767,6 @@ static void __dma_rx_do_complete(struct uart_8250_port *p, bool error) dma->rx_running = 0; dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); - dmaengine_terminate_all(dma->rxchan); count = dma->rx_size - state.residue; @@ -775,15 +777,13 @@ static void __dma_rx_do_complete(struct uart_8250_port *p, bool error) unlock: spin_unlock_irqrestore(&priv->rx_dma_lock, flags); - if (!error) - omap_8250_rx_dma(p, 0); - tty_flip_buffer_push(tty_port); } static void __dma_rx_complete(void *param) { - __dma_rx_do_complete(param, false); + __dma_rx_do_complete(param); + omap_8250_rx_dma(param); } static void omap_8250_rx_dma_flush(struct uart_8250_port *p) @@ -806,10 +806,11 @@ static void omap_8250_rx_dma_flush(struct uart_8250_port *p) spin_unlock_irqrestore(&priv->rx_dma_lock, flags); - __dma_rx_do_complete(p, true); + __dma_rx_do_complete(p); + dmaengine_terminate_all(dma->rxchan); } -static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) +static int omap_8250_rx_dma(struct uart_8250_port *p) { struct omap8250_priv *priv = p->port.private_data; struct uart_8250_dma *dma = p->dma; @@ -817,35 +818,6 @@ static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) struct dma_async_tx_descriptor *desc; unsigned long flags; - switch (iir & 0x3f) { - case UART_IIR_RLSI: - /* 8250_core handles errors and break interrupts */ - omap_8250_rx_dma_flush(p); - return -EIO; - case UART_IIR_RX_TIMEOUT: - /* - * If RCVR FIFO trigger level was not reached, complete the - * transfer and let 8250_core copy the remaining data. - */ - omap_8250_rx_dma_flush(p); - return -ETIMEDOUT; - case UART_IIR_RDI: - /* - * The OMAP UART is a special BEAST. If we receive RDI we _have_ - * a DMA transfer programmed but it didn't work. One reason is - * that we were too slow and there were too many bytes in the - * FIFO, the UART counted wrong and never kicked the DMA engine - * to do anything. That means once we receive RDI on OMAP then - * the DMA won't do anything soon so we have to cancel the DMA - * transfer and purge the FIFO manually. - */ - omap_8250_rx_dma_flush(p); - return -ETIMEDOUT; - - default: - break; - } - if (priv->rx_dma_broken) return -EINVAL; @@ -868,9 +840,6 @@ static int omap_8250_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); out: spin_unlock_irqrestore(&priv->rx_dma_lock, flags); @@ -1022,6 +991,18 @@ err: return ret; } +static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) +{ + switch (iir & 0x3f) { + case UART_IIR_RLSI: + case UART_IIR_RX_TIMEOUT: + case UART_IIR_RDI: + omap_8250_rx_dma_flush(up); + return true; + } + return omap_8250_rx_dma(up); +} + /* * This is mostly serial8250_handle_irq(). We have a slightly different DMA * hoook for RX/TX and need different logic for them in the ISR. Therefore we @@ -1033,7 +1014,6 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) unsigned char status; unsigned long flags; u8 iir; - int dma_err = 0; serial8250_rpm_get(up); @@ -1048,11 +1028,9 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) status = serial_port_in(port, UART_LSR); if (status & (UART_LSR_DR | UART_LSR_BI)) { - - dma_err = omap_8250_rx_dma(up, iir); - if (dma_err) { + if (handle_rx_dma(up, iir)) { status = serial8250_rx_chars(up, status); - omap_8250_rx_dma(up, 0); + omap_8250_rx_dma(up); } } serial8250_modem_status(up); @@ -1066,8 +1044,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) * try again due to an earlier failer which * might have been resolved by now. */ - dma_err = omap_8250_tx_dma(up); - if (dma_err) + if (omap_8250_tx_dma(up)) serial8250_tx_chars(up); } } @@ -1084,7 +1061,7 @@ static bool the_no_dma_filter_fn(struct dma_chan *chan, void *param) #else -static inline int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) +static inline int omap_8250_rx_dma(struct uart_8250_port *p) { return -EINVAL; } @@ -1395,7 +1372,7 @@ static int omap8250_runtime_suspend(struct device *dev) } if (up->dma && up->dma->rxchan) - omap_8250_rx_dma(up, UART_IIR_RX_TIMEOUT); + omap_8250_rx_dma_flush(up); priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; schedule_work(&priv->qos_work); @@ -1407,20 +1384,18 @@ static int omap8250_runtime_resume(struct device *dev) { struct omap8250_priv *priv = dev_get_drvdata(dev); struct uart_8250_port *up; - int loss_cntx; /* In case runtime-pm tries this before we are setup */ if (!priv) return 0; up = serial8250_get_port(priv->line); - loss_cntx = omap8250_lost_context(up); - if (loss_cntx) + if (omap8250_lost_context(up)) omap8250_restore_regs(up); if (up->dma && up->dma->rxchan) - omap_8250_rx_dma(up, 0); + omap_8250_rx_dma(up); priv->latency = priv->calc_latency; schedule_work(&priv->qos_work); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 4eedd1da3..8dd250fbd 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1416,6 +1416,17 @@ static bool byt_dma_filter(struct dma_chan *chan, void *param) return true; } +static unsigned int +byt_get_mctrl(struct uart_port *port) +{ + unsigned int ret = serial8250_do_get_mctrl(port); + + /* Force DCD and DSR signals to permanently be reported as active. */ + ret |= TIOCM_CAR | TIOCM_DSR; + + return ret; +} + static int byt_serial_setup(struct serial_private *priv, const struct pciserial_board *board, @@ -1457,13 +1468,13 @@ byt_serial_setup(struct serial_private *priv, return -EINVAL; } - rx_param->src_master = 1; - rx_param->dst_master = 0; + rx_param->m_master = 0; + rx_param->p_master = 1; dma->rxconf.src_maxburst = 16; - tx_param->src_master = 1; - tx_param->dst_master = 0; + tx_param->m_master = 0; + tx_param->p_master = 1; dma->txconf.dst_maxburst = 16; @@ -1480,6 +1491,7 @@ byt_serial_setup(struct serial_private *priv, port->port.type = PORT_16550A; port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); port->port.set_termios = byt_set_termios; + port->port.get_mctrl = byt_get_mctrl; port->port.fifosize = 64; port->tx_loadsz = 64; port->dma = dma; diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 00ad2637b..d4036038a 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1315,6 +1315,13 @@ static void autoconfig(struct uart_8250_port *up) out_lock: spin_unlock_irqrestore(&port->lock, flags); + + /* + * Check if the device is a Fintek F81216A + */ + if (port->type == PORT_16550A) + fintek_8250_probe(up); + if (up->capabilities != old_capabilities) { pr_warn("ttyS%d: detected caps %08x should be %08x\n", serial_index(port), old_capabilities, @@ -1788,6 +1795,18 @@ unsigned int serial8250_modem_status(struct uart_8250_port *up) } EXPORT_SYMBOL_GPL(serial8250_modem_status); +static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) +{ + switch (iir & 0x3f) { + case UART_IIR_RX_TIMEOUT: + serial8250_rx_dma_flush(up); + /* fall-through */ + case UART_IIR_RLSI: + return true; + } + return up->dma->rx_dma(up); +} + /* * This handles the interrupt from one port. */ @@ -1796,7 +1815,6 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) unsigned char status; unsigned long flags; struct uart_8250_port *up = up_to_u8250p(port); - int dma_err = 0; if (iir & UART_IIR_NO_INT) return 0; @@ -1808,15 +1826,11 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) DEBUG_INTR("status = %x...", status); if (status & (UART_LSR_DR | UART_LSR_BI)) { - if (up->dma) - dma_err = up->dma->rx_dma(up, iir); - - if (!up->dma || dma_err) + if (!up->dma || handle_rx_dma(up, iir)) status = serial8250_rx_chars(up, status); } serial8250_modem_status(up); - if ((!up->dma || (up->dma && up->dma->tx_err)) && - (status & UART_LSR_THRE)) + if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE)) serial8250_tx_chars(up); spin_unlock_irqrestore(&port->lock, flags); @@ -1882,7 +1896,7 @@ static unsigned int serial8250_tx_empty(struct uart_port *port) return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0; } -static unsigned int serial8250_get_mctrl(struct uart_port *port) +unsigned int serial8250_do_get_mctrl(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); unsigned int status; @@ -1903,6 +1917,14 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port) ret |= TIOCM_CTS; return ret; } +EXPORT_SYMBOL_GPL(serial8250_do_get_mctrl); + +static unsigned int serial8250_get_mctrl(struct uart_port *port) +{ + if (port->get_mctrl) + return port->get_mctrl(port); + return serial8250_do_get_mctrl(port); +} void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl) { diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 1b7bd2655..efd1f9c04 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -209,7 +209,7 @@ static int uniphier_uart_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(dev, "failed to get IRQ number"); + dev_err(dev, "failed to get IRQ number\n"); return irq; } diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 4d7cb9c04..e46761d20 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -57,6 +57,18 @@ config SERIAL_8250_PNP This builds standard PNP serial support. You may be able to disable this feature if you only need legacy serial support. +config SERIAL_8250_FINTEK + bool "Support for Fintek F81216A LPC to 4 UART RS485 API" + depends on SERIAL_8250 + ---help--- + Selecting this option will add support for the RS485 capabilities + of the Fintek F81216A LPC to 4 UART. + + If this option is not selected the device will be configured as a + standard 16550A serial port. + + If unsure, say N. + config SERIAL_8250_CONSOLE bool "Console on 8250/16550 and compatible serial port" depends on SERIAL_8250=y @@ -358,14 +370,6 @@ config SERIAL_8250_OMAP_TTYO_FIXUP not booting kernel because the serial console remains silent in case they forgot to update the command line. -config SERIAL_8250_FINTEK - tristate "Support for Fintek F81216A LPC to 4 UART" - depends on SERIAL_8250 && PNP - help - Selecting this option will add support for the Fintek F81216A - LPC to 4 UART. This device has some RS485 functionality not available - through the PNP driver. If unsure, say N. - config SERIAL_8250_LPC18XX tristate "NXP LPC18xx/43xx serial port support" depends on SERIAL_8250 && OF && (ARCH_LPC18XX || COMPILE_TEST) @@ -398,8 +402,10 @@ config SERIAL_8250_INGENIC its UARTs, say Y to this option. If unsure, say N. config SERIAL_8250_MID - tristate "Support for serial ports on Intel MID platforms" + tristate "Support for serial ports on Intel MID platforms" if EXPERT + default SERIAL_8250 depends on SERIAL_8250 && PCI + depends on X86 || COMPILE_TEST select HSU_DMA if SERIAL_8250_DMA select HSU_DMA_PCI if (HSU_DMA && X86_INTEL_MID) select RATIONAL diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index c9a2d6ed8..367d403d2 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_SERIAL_8250) += 8250.o 8250_base.o 8250-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o 8250_base-y := 8250_port.o 8250_base-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o +8250_base-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o @@ -23,7 +24,6 @@ obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o -obj-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 13d4ed6ca..7e3a58c8b 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -213,6 +213,7 @@ config SERIAL_MESON_CONSOLE bool "Support for console on meson" depends on SERIAL_MESON=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON help Say Y here if you wish to use a Amlogic MesonX UART as the system console (the system console is the device which @@ -900,6 +901,27 @@ config SERIAL_SGI_L1_CONSOLE controller serial port as your console (you want this!), say Y. Otherwise, say N. +config SERIAL_PIC32 + tristate "Microchip PIC32 serial support" + depends on MACH_PIC32 + select SERIAL_CORE + help + If you have a PIC32, this driver supports the serial ports. + + Say Y or M to use PIC32 serial ports, otherwise say N. Note that + to use a serial port as a console, this must be included in kernel and + not as a module. + +config SERIAL_PIC32_CONSOLE + bool "PIC32 serial console support" + depends on SERIAL_PIC32 + select SERIAL_CORE_CONSOLE + help + If you have a PIC32, this driver supports the putting a console on one + of the serial ports. + + Say Y to use the PIC32 console, otherwise say N. + config SERIAL_MPC52xx tristate "Freescale MPC52xx/MPC512x family PSC serial support" depends on PPC_MPC52xx || PPC_MPC512x @@ -1383,11 +1405,12 @@ config SERIAL_PCH_UART_CONSOLE config SERIAL_MXS_AUART tristate "MXS AUART support" depends on HAS_DMA - depends on ARCH_MXS || COMPILE_TEST + depends on ARCH_MXS || MACH_ASM9260 || COMPILE_TEST select SERIAL_CORE select SERIAL_MCTRL_GPIO if GPIOLIB help - This driver supports the MXS Application UART (AUART) port. + This driver supports the MXS and Alphascale ASM9260 Application + UART (AUART) port. config SERIAL_MXS_AUART_CONSOLE bool "MXS AUART console support" @@ -1446,6 +1469,19 @@ config SERIAL_EFM32_UART This driver support the USART and UART ports on Energy Micro's efm32 SoCs. +config SERIAL_MPS2_UART_CONSOLE + bool "MPS2 UART console support" + depends on SERIAL_MPS2_UART + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + +config SERIAL_MPS2_UART + bool "MPS2 UART port" + depends on ARM || COMPILE_TEST + select SERIAL_CORE + help + This driver support the UART ports on ARM MPS2. + config SERIAL_EFM32_UART_CONSOLE bool "EFM32 UART/USART console support" depends on SERIAL_EFM32_UART=y @@ -1604,6 +1640,7 @@ config SERIAL_STM32_CONSOLE config SERIAL_MVEBU_UART bool "Marvell EBU serial port support" + depends on ARCH_MVEBU || COMPILE_TEST select SERIAL_CORE help This driver is for Marvell EBU SoC's UART. If you have a machine diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 8c261adac..1278d376d 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -91,6 +91,8 @@ obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o obj-$(CONFIG_SERIAL_STM32) += stm32-usart.o obj-$(CONFIG_SERIAL_MVEBU_UART) += mvebu-uart.o +obj-$(CONFIG_SERIAL_PIC32) += pic32_uart.o +obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 7c198e0a3..1b7331e40 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -121,6 +121,7 @@ static struct vendor_data vendor_arm = { static struct vendor_data vendor_sbsa = { .reg_offset = pl011_std_offsets, + .access_32b = true, .oversampling = false, .dma_threshold = false, .cts_event_workaround = false, @@ -2359,7 +2360,7 @@ static int pl011_probe_dt_alias(int index, struct device *dev) return ret; ret = of_alias_get_id(np, "serial"); - if (IS_ERR_VALUE(ret)) { + if (ret < 0) { seen_dev_without_alias = true; ret = index; } else { diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index c0172bf54..315c84979 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -2599,7 +2599,7 @@ startup(struct e100_serial * info) /* if it was already initialized, skip this */ - if (info->port.flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(&info->port)) { local_irq_restore(flags); free_page(xmit_page); return 0; @@ -2703,7 +2703,7 @@ startup(struct e100_serial * info) e100_rts(info, 1); e100_dtr(info, 1); - info->port.flags |= ASYNC_INITIALIZED; + tty_port_set_initialized(&info->port, 1); local_irq_restore(flags); return 0; @@ -2745,7 +2745,7 @@ shutdown(struct e100_serial * info) info->tr_running = 0; } - if (!(info->port.flags & ASYNC_INITIALIZED)) + if (!tty_port_initialized(&info->port)) return; #ifdef SERIAL_DEBUG_OPEN @@ -2776,7 +2776,7 @@ shutdown(struct e100_serial * info) if (info->port.tty) set_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->port.flags &= ~ASYNC_INITIALIZED; + tty_port_set_initialized(&info->port, 0); local_irq_restore(flags); } @@ -3273,9 +3273,9 @@ set_serial_info(struct e100_serial *info, info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: - if (info->port.flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(&info->port)) change_speed(info); - } else + else retval = startup(info); return retval; } @@ -3445,7 +3445,7 @@ rs_ioctl(struct tty_struct *tty, if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) + if (tty_io_error(tty)) return -EIO; } @@ -3628,7 +3628,7 @@ rs_close(struct tty_struct *tty, struct file * filp) e100_disable_rx(info); e100_disable_rx_irq(info); - if (info->port.flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(&info->port)) { /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially @@ -3648,8 +3648,8 @@ rs_close(struct tty_struct *tty, struct file * filp) schedule_timeout_interruptible(info->port.close_delay); wake_up_interruptible(&info->port.open_wait); } - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; local_irq_restore(flags); + tty_port_set_active(&info->port, 0); /* port closed */ @@ -3732,7 +3732,7 @@ rs_hangup(struct tty_struct *tty) shutdown(info); info->event = 0; info->port.count = 0; - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; + tty_port_set_active(&info->port, 0); info->port.tty = NULL; wake_up_interruptible(&info->port.open_wait); } @@ -3755,9 +3755,8 @@ block_til_ready(struct tty_struct *tty, struct file * filp, * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - info->port.flags |= ASYNC_NORMAL_ACTIVE; + if ((filp->f_flags & O_NONBLOCK) || tty_io_error(tty)) { + tty_port_set_active(&info->port, 1); return 0; } @@ -3788,8 +3787,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, e100_dtr(info, 1); local_irq_restore(flags); set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(info->port.flags & ASYNC_INITIALIZED)) { + if (tty_hung_up_p(filp) || !tty_port_initialized(&info->port)) { #ifdef SERIAL_DO_RESTART if (info->port.flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; @@ -3826,7 +3824,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, #endif if (retval) return retval; - info->port.flags |= ASYNC_NORMAL_ACTIVE; + tty_port_set_active(&info->port, 1); return 0; } diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 2085a6cfa..d38634624 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -651,7 +651,7 @@ static void ifx_spi_complete(void *ctx) struct ifx_spi_device *ifx_dev = ctx; int length; int actual_length; - unsigned char more; + unsigned char more = 0; unsigned char cts; int local_write_pending = 0; int queue_length; diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 231e7d5ca..0df2b1c09 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -114,6 +114,7 @@ #define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */ #define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */ #define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */ +#define UCR3_DTRDEN (1<<3) /* Data Terminal Ready Delta Enable. */ #define IMX21_UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select */ #define UCR3_INVT (1<<1) /* Inverted Infrared transmission */ #define UCR3_BPEN (1<<0) /* Preset registers enable */ @@ -142,7 +143,7 @@ #define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */ #define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */ #define USR1_AGTIM (1<<8) /* Ageing timer interrupt flag */ -#define USR1_TIMEOUT (1<<7) /* Receive timeout interrupt status */ +#define USR1_DTRD (1<<7) /* DTR Delta */ #define USR1_RXDS (1<<6) /* Receiver idle interrupt flag */ #define USR1_AIRINT (1<<5) /* Async IR wake interrupt flag */ #define USR1_AWAKE (1<<4) /* Aysnc wake interrupt flag */ @@ -361,6 +362,7 @@ static void imx_stop_tx(struct uart_port *port) imx_port_rts_inactive(sport, &temp); else imx_port_rts_active(sport, &temp); + temp |= UCR2_RXEN; writel(temp, port->membase + UCR2); temp = readl(port->membase + UCR4); @@ -568,6 +570,8 @@ static void imx_start_tx(struct uart_port *port) imx_port_rts_inactive(sport, &temp); else imx_port_rts_active(sport, &temp); + if (!(port->rs485.flags & SER_RS485_RX_DURING_TX)) + temp &= ~UCR2_RXEN; writel(temp, port->membase + UCR2); /* enable transmitter and shifter empty irq */ @@ -729,11 +733,61 @@ static void imx_dma_rxint(struct imx_port *sport) spin_unlock_irqrestore(&sport->port.lock, flags); } +/* + * We have a modem side uart, so the meanings of RTS and CTS are inverted. + */ +static unsigned int imx_get_hwmctrl(struct imx_port *sport) +{ + unsigned int tmp = TIOCM_DSR; + unsigned usr1 = readl(sport->port.membase + USR1); + + if (usr1 & USR1_RTSS) + tmp |= TIOCM_CTS; + + /* in DCE mode DCDIN is always 0 */ + if (!(usr1 & USR2_DCDIN)) + tmp |= TIOCM_CAR; + + if (sport->dte_mode) + if (!(readl(sport->port.membase + USR2) & USR2_RIIN)) + tmp |= TIOCM_RI; + + return tmp; +} + +/* + * Handle any change of modem status signal since we were last called. + */ +static void imx_mctrl_check(struct imx_port *sport) +{ + unsigned int status, changed; + + status = imx_get_hwmctrl(sport); + changed = status ^ sport->old_status; + + if (changed == 0) + return; + + sport->old_status = status; + + if (changed & TIOCM_RI && status & TIOCM_RI) + sport->port.icount.rng++; + if (changed & TIOCM_DSR) + sport->port.icount.dsr++; + if (changed & TIOCM_CAR) + uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); + if (changed & TIOCM_CTS) + uart_handle_cts_change(&sport->port, status & TIOCM_CTS); + + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); +} + static irqreturn_t imx_int(int irq, void *dev_id) { struct imx_port *sport = dev_id; unsigned int sts; unsigned int sts2; + irqreturn_t ret = IRQ_NONE; sts = readl(sport->port.membase + USR1); sts2 = readl(sport->port.membase + USR2); @@ -743,26 +797,47 @@ static irqreturn_t imx_int(int irq, void *dev_id) imx_dma_rxint(sport); else imx_rxint(irq, dev_id); + ret = IRQ_HANDLED; } if ((sts & USR1_TRDY && readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) || (sts2 & USR2_TXDC && - readl(sport->port.membase + UCR4) & UCR4_TCEN)) + readl(sport->port.membase + UCR4) & UCR4_TCEN)) { imx_txint(irq, dev_id); + ret = IRQ_HANDLED; + } + + if (sts & USR1_DTRD) { + unsigned long flags; + + if (sts & USR1_DTRD) + writel(USR1_DTRD, sport->port.membase + USR1); + + spin_lock_irqsave(&sport->port.lock, flags); + imx_mctrl_check(sport); + spin_unlock_irqrestore(&sport->port.lock, flags); - if (sts & USR1_RTSD) + ret = IRQ_HANDLED; + } + + if (sts & USR1_RTSD) { imx_rtsint(irq, dev_id); + ret = IRQ_HANDLED; + } - if (sts & USR1_AWAKE) + if (sts & USR1_AWAKE) { writel(USR1_AWAKE, sport->port.membase + USR1); + ret = IRQ_HANDLED; + } if (sts2 & USR2_ORE) { sport->port.icount.overrun++; writel(USR2_ORE, sport->port.membase + USR2); + ret = IRQ_HANDLED; } - return IRQ_HANDLED; + return ret; } /* @@ -782,28 +857,6 @@ static unsigned int imx_tx_empty(struct uart_port *port) return ret; } -/* - * We have a modem side uart, so the meanings of RTS and CTS are inverted. - */ -static unsigned int imx_get_hwmctrl(struct imx_port *sport) -{ - unsigned int tmp = TIOCM_DSR; - unsigned usr1 = readl(sport->port.membase + USR1); - - if (usr1 & USR1_RTSS) - tmp |= TIOCM_CTS; - - /* in DCE mode DCDIN is always 0 */ - if (!(usr1 & USR2_DCDIN)) - tmp |= TIOCM_CAR; - - /* in DCE mode RIIN is always 0 */ - if (readl(sport->port.membase + USR2) & USR2_RIIN) - tmp |= TIOCM_RI; - - return tmp; -} - static unsigned int imx_get_mctrl(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; @@ -860,33 +913,6 @@ static void imx_break_ctl(struct uart_port *port, int break_state) spin_unlock_irqrestore(&sport->port.lock, flags); } -/* - * Handle any change of modem status signal since we were last called. - */ -static void imx_mctrl_check(struct imx_port *sport) -{ - unsigned int status, changed; - - status = imx_get_hwmctrl(sport); - changed = status ^ sport->old_status; - - if (changed == 0) - return; - - sport->old_status = status; - - if (changed & TIOCM_RI) - sport->port.icount.rng++; - if (changed & TIOCM_DSR) - sport->port.icount.dsr++; - if (changed & TIOCM_CAR) - uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); - if (changed & TIOCM_CTS) - uart_handle_cts_change(&sport->port, status & TIOCM_CTS); - - wake_up_interruptible(&sport->port.state->port.delta_msr_wait); -} - /* * This is our per-port timeout handler, for checking the * modem status signals. @@ -1193,7 +1219,7 @@ static int imx_startup(struct uart_port *port) /* * Finally, clear and enable interrupts */ - writel(USR1_RTSD, sport->port.membase + USR1); + writel(USR1_RTSD | USR1_DTRD, sport->port.membase + USR1); writel(USR2_ORE, sport->port.membase + USR2); if (sport->dma_is_inited && !sport->dma_is_enabled) @@ -1212,11 +1238,32 @@ static int imx_startup(struct uart_port *port) temp |= (UCR2_RXEN | UCR2_TXEN); if (!sport->have_rtscts) temp |= UCR2_IRTS; + /* + * make sure the edge sensitive RTS-irq is disabled, + * we're using RTSD instead. + */ + if (!is_imx1_uart(sport)) + temp &= ~UCR2_RTSEN; writel(temp, sport->port.membase + UCR2); if (!is_imx1_uart(sport)) { temp = readl(sport->port.membase + UCR3); - temp |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP; + + /* + * The effect of RI and DCD differs depending on the UFCR_DCEDTE + * bit. In DCE mode they control the outputs, in DTE mode they + * enable the respective irqs. At least the DCD irq cannot be + * cleared on i.MX25 at least, so it's not usable and must be + * disabled. I don't have test hardware to check if RI has the + * same problem but I consider this likely so it's disabled for + * now, too. + */ + temp |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | + UCR3_DTRDEN | UCR3_RI | UCR3_DCD; + + if (sport->dte_mode) + temp &= ~(UCR3_RI | UCR3_DCD); + writel(temp, sport->port.membase + UCR3); } @@ -1610,19 +1657,17 @@ static int imx_rs485_config(struct uart_port *port, struct serial_rs485 *rs485conf) { struct imx_port *sport = (struct imx_port *)port; + unsigned long temp; /* unimplemented */ rs485conf->delay_rts_before_send = 0; rs485conf->delay_rts_after_send = 0; - rs485conf->flags |= SER_RS485_RX_DURING_TX; /* RTS is required to control the transmitter */ if (!sport->have_rtscts) rs485conf->flags &= ~SER_RS485_ENABLED; if (rs485conf->flags & SER_RS485_ENABLED) { - unsigned long temp; - /* disable transmitter */ temp = readl(sport->port.membase + UCR2); if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND) @@ -1632,6 +1677,14 @@ static int imx_rs485_config(struct uart_port *port, writel(temp, sport->port.membase + UCR2); } + /* Make sure Rx is enabled in case Tx is active with Rx disabled */ + if (!(rs485conf->flags & SER_RS485_ENABLED) || + rs485conf->flags & SER_RS485_RX_DURING_TX) { + temp = readl(sport->port.membase + UCR2); + temp |= UCR2_RXEN; + writel(temp, sport->port.membase + UCR2); + } + port->rs485 = *rs485conf; return 0; @@ -1927,7 +1980,8 @@ static int serial_imx_probe_dt(struct imx_port *sport, } sport->port.line = ret; - if (of_get_property(np, "fsl,uart-has-rtscts", NULL)) + if (of_get_property(np, "uart-has-rtscts", NULL) || + of_get_property(np, "fsl,uart-has-rtscts", NULL) /* deprecated */) sport->have_rtscts = 1; if (of_get_property(np, "fsl,dte-mode", NULL)) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 3f98165b4..3f6e0ab72 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -1036,7 +1036,7 @@ static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume); static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset) { unsigned int val; - struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct max310x_port *s = gpiochip_get_data(chip); struct uart_port *port = &s->p[offset / 4].port; val = max310x_port_read(port, MAX310X_GPIODATA_REG); @@ -1046,7 +1046,7 @@ static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset) static void max310x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { - struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct max310x_port *s = gpiochip_get_data(chip); struct uart_port *port = &s->p[offset / 4].port; max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4), @@ -1055,7 +1055,7 @@ static void max310x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { - struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct max310x_port *s = gpiochip_get_data(chip); struct uart_port *port = &s->p[offset / 4].port; max310x_port_update(port, MAX310X_GPIOCFG_REG, 1 << (offset % 4), 0); @@ -1066,7 +1066,7 @@ static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned offset) static int max310x_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { - struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct max310x_port *s = gpiochip_get_data(chip); struct uart_port *port = &s->p[offset / 4].port; max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4), @@ -1183,7 +1183,7 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, s->gpio.base = -1; s->gpio.ngpio = devtype->nr * 4; s->gpio.can_sleep = 1; - ret = gpiochip_add(&s->gpio); + ret = gpiochip_add_data(&s->gpio, s); if (ret) goto out_uart; #endif diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 024445aa0..6aea0f4a9 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -481,18 +481,13 @@ static void meson_console_putchar(struct uart_port *port, int ch) writel(ch, port->membase + AML_UART_WFIFO); } -static void meson_serial_console_write(struct console *co, const char *s, - u_int count) +static void meson_serial_port_write(struct uart_port *port, const char *s, + u_int count) { - struct uart_port *port; unsigned long flags; int locked; u32 val, tmp; - port = meson_ports[co->index]; - if (!port) - return; - local_irq_save(flags); if (port->sysrq) { locked = 0; @@ -516,6 +511,18 @@ static void meson_serial_console_write(struct console *co, const char *s, local_irq_restore(flags); } +static void meson_serial_console_write(struct console *co, const char *s, + u_int count) +{ + struct uart_port *port; + + port = meson_ports[co->index]; + if (!port) + return; + + meson_serial_port_write(port, s, count); +} + static int meson_serial_console_setup(struct console *co, char *options) { struct uart_port *port; @@ -554,6 +561,27 @@ static int __init meson_serial_console_init(void) } console_initcall(meson_serial_console_init); +static void meson_serial_early_console_write(struct console *co, + const char *s, + u_int count) +{ + struct earlycon_device *dev = co->data; + + meson_serial_port_write(&dev->port, s, count); +} + +static int __init +meson_serial_early_console_setup(struct earlycon_device *device, const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = meson_serial_early_console_write; + return 0; +} +OF_EARLYCON_DECLARE(meson, "amlogic,meson-uart", + meson_serial_early_console_setup); + #define MESON_SERIAL_CONSOLE (&meson_serial_console) #else #define MESON_SERIAL_CONSOLE NULL diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c new file mode 100644 index 000000000..da9e27d3c --- /dev/null +++ b/drivers/tty/serial/mps2-uart.c @@ -0,0 +1,625 @@ +/* + * Copyright (C) 2015 ARM Limited + * + * Author: Vladimir Murzin + * + * 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. + * + * TODO: support for SysRq + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SERIAL_NAME "ttyMPS" +#define DRIVER_NAME "mps2-uart" +#define MAKE_NAME(x) (DRIVER_NAME # x) + +#define UARTn_DATA 0x00 + +#define UARTn_STATE 0x04 +#define UARTn_STATE_TX_FULL BIT(0) +#define UARTn_STATE_RX_FULL BIT(1) +#define UARTn_STATE_TX_OVERRUN BIT(2) +#define UARTn_STATE_RX_OVERRUN BIT(3) + +#define UARTn_CTRL 0x08 +#define UARTn_CTRL_TX_ENABLE BIT(0) +#define UARTn_CTRL_RX_ENABLE BIT(1) +#define UARTn_CTRL_TX_INT_ENABLE BIT(2) +#define UARTn_CTRL_RX_INT_ENABLE BIT(3) +#define UARTn_CTRL_TX_OVERRUN_INT_ENABLE BIT(4) +#define UARTn_CTRL_RX_OVERRUN_INT_ENABLE BIT(5) + +#define UARTn_INT 0x0c +#define UARTn_INT_TX BIT(0) +#define UARTn_INT_RX BIT(1) +#define UARTn_INT_TX_OVERRUN BIT(2) +#define UARTn_INT_RX_OVERRUN BIT(3) + +#define UARTn_BAUDDIV 0x10 +#define UARTn_BAUDDIV_MASK GENMASK(20, 0) + +/* + * Helpers to make typical enable/disable operations more readable. + */ +#define UARTn_CTRL_TX_GRP (UARTn_CTRL_TX_ENABLE |\ + UARTn_CTRL_TX_INT_ENABLE |\ + UARTn_CTRL_TX_OVERRUN_INT_ENABLE) + +#define UARTn_CTRL_RX_GRP (UARTn_CTRL_RX_ENABLE |\ + UARTn_CTRL_RX_INT_ENABLE |\ + UARTn_CTRL_RX_OVERRUN_INT_ENABLE) + +#define MPS2_MAX_PORTS 3 + +struct mps2_uart_port { + struct uart_port port; + struct clk *clk; + unsigned int tx_irq; + unsigned int rx_irq; +}; + +static inline struct mps2_uart_port *to_mps2_port(struct uart_port *port) +{ + return container_of(port, struct mps2_uart_port, port); +} + +static void mps2_uart_write8(struct uart_port *port, u8 val, unsigned int off) +{ + struct mps2_uart_port *mps_port = to_mps2_port(port); + + writeb(val, mps_port->port.membase + off); +} + +static u8 mps2_uart_read8(struct uart_port *port, unsigned int off) +{ + struct mps2_uart_port *mps_port = to_mps2_port(port); + + return readb(mps_port->port.membase + off); +} + +static void mps2_uart_write32(struct uart_port *port, u32 val, unsigned int off) +{ + struct mps2_uart_port *mps_port = to_mps2_port(port); + + writel_relaxed(val, mps_port->port.membase + off); +} + +static unsigned int mps2_uart_tx_empty(struct uart_port *port) +{ + u8 status = mps2_uart_read8(port, UARTn_STATE); + + return (status & UARTn_STATE_TX_FULL) ? 0 : TIOCSER_TEMT; +} + +static void mps2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static unsigned int mps2_uart_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR; +} + +static void mps2_uart_stop_tx(struct uart_port *port) +{ + u8 control = mps2_uart_read8(port, UARTn_CTRL); + + control &= ~UARTn_CTRL_TX_INT_ENABLE; + + mps2_uart_write8(port, control, UARTn_CTRL); +} + +static void mps2_uart_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + while (!(mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)) { + if (port->x_char) { + mps2_uart_write8(port, port->x_char, UARTn_DATA); + port->x_char = 0; + port->icount.tx++; + continue; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + break; + + mps2_uart_write8(port, xmit->buf[xmit->tail], UARTn_DATA); + xmit->tail = (xmit->tail + 1) % UART_XMIT_SIZE; + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + mps2_uart_stop_tx(port); +} + +static void mps2_uart_start_tx(struct uart_port *port) +{ + u8 control = mps2_uart_read8(port, UARTn_CTRL); + + control |= UARTn_CTRL_TX_INT_ENABLE; + + mps2_uart_write8(port, control, UARTn_CTRL); + + /* + * We've just unmasked the TX IRQ and now slow-starting via + * polling; if there is enough data to fill up the internal + * write buffer in one go, the TX IRQ should assert, at which + * point we switch to fully interrupt-driven TX. + */ + + mps2_uart_tx_chars(port); +} + +static void mps2_uart_stop_rx(struct uart_port *port) +{ + u8 control = mps2_uart_read8(port, UARTn_CTRL); + + control &= ~UARTn_CTRL_RX_GRP; + + mps2_uart_write8(port, control, UARTn_CTRL); +} + +static void mps2_uart_break_ctl(struct uart_port *port, int ctl) +{ +} + +static void mps2_uart_rx_chars(struct uart_port *port) +{ + struct tty_port *tport = &port->state->port; + + while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_RX_FULL) { + u8 rxdata = mps2_uart_read8(port, UARTn_DATA); + + port->icount.rx++; + tty_insert_flip_char(&port->state->port, rxdata, TTY_NORMAL); + } + + tty_flip_buffer_push(tport); +} + +static irqreturn_t mps2_uart_rxirq(int irq, void *data) +{ + struct uart_port *port = data; + u8 irqflag = mps2_uart_read8(port, UARTn_INT); + + if (unlikely(!(irqflag & UARTn_INT_RX))) + return IRQ_NONE; + + spin_lock(&port->lock); + + mps2_uart_write8(port, UARTn_INT_RX, UARTn_INT); + mps2_uart_rx_chars(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t mps2_uart_txirq(int irq, void *data) +{ + struct uart_port *port = data; + u8 irqflag = mps2_uart_read8(port, UARTn_INT); + + if (unlikely(!(irqflag & UARTn_INT_TX))) + return IRQ_NONE; + + spin_lock(&port->lock); + + mps2_uart_write8(port, UARTn_INT_TX, UARTn_INT); + mps2_uart_tx_chars(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t mps2_uart_oerrirq(int irq, void *data) +{ + irqreturn_t handled = IRQ_NONE; + struct uart_port *port = data; + u8 irqflag = mps2_uart_read8(port, UARTn_INT); + + spin_lock(&port->lock); + + if (irqflag & UARTn_INT_RX_OVERRUN) { + struct tty_port *tport = &port->state->port; + + mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT); + port->icount.overrun++; + tty_insert_flip_char(tport, 0, TTY_OVERRUN); + tty_flip_buffer_push(tport); + handled = IRQ_HANDLED; + } + + /* + * It's never been seen in practice and it never *should* happen since + * we check if there is enough room in TX buffer before sending data. + * So we keep this check in case something suspicious has happened. + */ + if (irqflag & UARTn_INT_TX_OVERRUN) { + mps2_uart_write8(port, UARTn_INT_TX_OVERRUN, UARTn_INT); + handled = IRQ_HANDLED; + } + + spin_unlock(&port->lock); + + return handled; +} + +static int mps2_uart_startup(struct uart_port *port) +{ + struct mps2_uart_port *mps_port = to_mps2_port(port); + u8 control = mps2_uart_read8(port, UARTn_CTRL); + int ret; + + control &= ~(UARTn_CTRL_RX_GRP | UARTn_CTRL_TX_GRP); + + mps2_uart_write8(port, control, UARTn_CTRL); + + ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0, + MAKE_NAME(-rx), mps_port); + if (ret) { + dev_err(port->dev, "failed to register rxirq (%d)\n", ret); + return ret; + } + + ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0, + MAKE_NAME(-tx), mps_port); + if (ret) { + dev_err(port->dev, "failed to register txirq (%d)\n", ret); + goto err_free_rxirq; + } + + ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED, + MAKE_NAME(-overrun), mps_port); + + if (ret) { + dev_err(port->dev, "failed to register oerrirq (%d)\n", ret); + goto err_free_txirq; + } + + control |= UARTn_CTRL_RX_GRP | UARTn_CTRL_TX_GRP; + + mps2_uart_write8(port, control, UARTn_CTRL); + + return 0; + +err_free_txirq: + free_irq(mps_port->tx_irq, mps_port); +err_free_rxirq: + free_irq(mps_port->rx_irq, mps_port); + + return ret; +} + +static void mps2_uart_shutdown(struct uart_port *port) +{ + struct mps2_uart_port *mps_port = to_mps2_port(port); + u8 control = mps2_uart_read8(port, UARTn_CTRL); + + control &= ~(UARTn_CTRL_RX_GRP | UARTn_CTRL_TX_GRP); + + mps2_uart_write8(port, control, UARTn_CTRL); + + free_irq(mps_port->rx_irq, mps_port); + free_irq(mps_port->tx_irq, mps_port); + free_irq(port->irq, mps_port); +} + +static void +mps2_uart_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, bauddiv; + + termios->c_cflag &= ~(CRTSCTS | CMSPAR); + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + termios->c_cflag &= ~PARENB; + termios->c_cflag &= ~CSTOPB; + + baud = uart_get_baud_rate(port, termios, old, + DIV_ROUND_CLOSEST(port->uartclk, UARTn_BAUDDIV_MASK), + DIV_ROUND_CLOSEST(port->uartclk, 16)); + + bauddiv = DIV_ROUND_CLOSEST(port->uartclk, baud); + + spin_lock_irqsave(&port->lock, flags); + + uart_update_timeout(port, termios->c_cflag, baud); + mps2_uart_write32(port, bauddiv, UARTn_BAUDDIV); + + spin_unlock_irqrestore(&port->lock, flags); + + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +static const char *mps2_uart_type(struct uart_port *port) +{ + return (port->type == PORT_MPS2UART) ? DRIVER_NAME : NULL; +} + +static void mps2_uart_release_port(struct uart_port *port) +{ +} + +static int mps2_uart_request_port(struct uart_port *port) +{ + return 0; +} + +static void mps2_uart_config_port(struct uart_port *port, int type) +{ + if (type & UART_CONFIG_TYPE && !mps2_uart_request_port(port)) + port->type = PORT_MPS2UART; +} + +static int mps2_uart_verify_port(struct uart_port *port, struct serial_struct *serinfo) +{ + return -EINVAL; +} + +static const struct uart_ops mps2_uart_pops = { + .tx_empty = mps2_uart_tx_empty, + .set_mctrl = mps2_uart_set_mctrl, + .get_mctrl = mps2_uart_get_mctrl, + .stop_tx = mps2_uart_stop_tx, + .start_tx = mps2_uart_start_tx, + .stop_rx = mps2_uart_stop_rx, + .break_ctl = mps2_uart_break_ctl, + .startup = mps2_uart_startup, + .shutdown = mps2_uart_shutdown, + .set_termios = mps2_uart_set_termios, + .type = mps2_uart_type, + .release_port = mps2_uart_release_port, + .request_port = mps2_uart_request_port, + .config_port = mps2_uart_config_port, + .verify_port = mps2_uart_verify_port, +}; + +static struct mps2_uart_port mps2_uart_ports[MPS2_MAX_PORTS]; + +#ifdef CONFIG_SERIAL_MPS2_UART_CONSOLE +static void mps2_uart_console_putchar(struct uart_port *port, int ch) +{ + while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL) + cpu_relax(); + + mps2_uart_write8(port, ch, UARTn_DATA); +} + +static void mps2_uart_console_write(struct console *co, const char *s, unsigned int cnt) +{ + struct uart_port *port = &mps2_uart_ports[co->index].port; + + uart_console_write(port, s, cnt, mps2_uart_console_putchar); +} + +static int mps2_uart_console_setup(struct console *co, char *options) +{ + struct mps2_uart_port *mps_port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= MPS2_MAX_PORTS) + return -ENODEV; + + mps_port = &mps2_uart_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&mps_port->port, co, baud, parity, bits, flow); +} + +static struct uart_driver mps2_uart_driver; + +static struct console mps2_uart_console = { + .name = SERIAL_NAME, + .device = uart_console_device, + .write = mps2_uart_console_write, + .setup = mps2_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &mps2_uart_driver, +}; + +#define MPS2_SERIAL_CONSOLE (&mps2_uart_console) + +static void mps2_early_putchar(struct uart_port *port, int ch) +{ + while (readb(port->membase + UARTn_STATE) & UARTn_STATE_TX_FULL) + cpu_relax(); + + writeb((unsigned char)ch, port->membase + UARTn_DATA); +} + +static void mps2_early_write(struct console *con, const char *s, unsigned int n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, mps2_early_putchar); +} + +static int __init mps2_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = mps2_early_write; + + return 0; +} + +OF_EARLYCON_DECLARE(mps2, "arm,mps2-uart", mps2_early_console_setup); + +#else +#define MPS2_SERIAL_CONSOLE NULL +#endif + +static struct uart_driver mps2_uart_driver = { + .driver_name = DRIVER_NAME, + .dev_name = SERIAL_NAME, + .nr = MPS2_MAX_PORTS, + .cons = MPS2_SERIAL_CONSOLE, +}; + +static struct mps2_uart_port *mps2_of_get_port(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int id; + + if (!np) + return NULL; + + id = of_alias_get_id(np, "serial"); + if (id < 0) + id = 0; + + if (WARN_ON(id >= MPS2_MAX_PORTS)) + return NULL; + + mps2_uart_ports[id].port.line = id; + return &mps2_uart_ports[id]; +} + +static int mps2_init_port(struct mps2_uart_port *mps_port, + struct platform_device *pdev) +{ + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mps_port->port.membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mps_port->port.membase)) + return PTR_ERR(mps_port->port.membase); + + mps_port->port.mapbase = res->start; + mps_port->port.mapsize = resource_size(res); + + mps_port->rx_irq = platform_get_irq(pdev, 0); + mps_port->tx_irq = platform_get_irq(pdev, 1); + mps_port->port.irq = platform_get_irq(pdev, 2); + + mps_port->port.iotype = UPIO_MEM; + mps_port->port.flags = UPF_BOOT_AUTOCONF; + mps_port->port.fifosize = 1; + mps_port->port.ops = &mps2_uart_pops; + mps_port->port.dev = &pdev->dev; + + mps_port->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mps_port->clk)) + return PTR_ERR(mps_port->clk); + + ret = clk_prepare_enable(mps_port->clk); + if (ret) + return ret; + + mps_port->port.uartclk = clk_get_rate(mps_port->clk); + + clk_disable_unprepare(mps_port->clk); + + return ret; +} + +static int mps2_serial_probe(struct platform_device *pdev) +{ + struct mps2_uart_port *mps_port; + int ret; + + mps_port = mps2_of_get_port(pdev); + if (!mps_port) + return -ENODEV; + + ret = mps2_init_port(mps_port, pdev); + if (ret) + return ret; + + ret = uart_add_one_port(&mps2_uart_driver, &mps_port->port); + if (ret) + return ret; + + platform_set_drvdata(pdev, mps_port); + + return 0; +} + +static int mps2_serial_remove(struct platform_device *pdev) +{ + struct mps2_uart_port *mps_port = platform_get_drvdata(pdev); + + uart_remove_one_port(&mps2_uart_driver, &mps_port->port); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id mps2_match[] = { + { .compatible = "arm,mps2-uart", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mps2_match); +#endif + +static struct platform_driver mps2_serial_driver = { + .probe = mps2_serial_probe, + .remove = mps2_serial_remove, + + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(mps2_match), + }, +}; + +static int __init mps2_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&mps2_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&mps2_serial_driver); + if (ret) + uart_unregister_driver(&mps2_uart_driver); + + return ret; +} +module_init(mps2_uart_init); + +static void __exit mps2_uart_exit(void) +{ + platform_driver_unregister(&mps2_serial_driver); + uart_unregister_driver(&mps2_uart_driver); +} +module_exit(mps2_uart_exit); + +MODULE_AUTHOR("Vladimir Murzin "); +MODULE_DESCRIPTION("MPS2 UART driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 96d3ce8dc..b7d80bd57 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -861,37 +861,72 @@ struct msm_baud_map { }; static const struct msm_baud_map * -msm_find_best_baud(struct uart_port *port, unsigned int baud) +msm_find_best_baud(struct uart_port *port, unsigned int baud, + unsigned long *rate) { - unsigned int i, divisor; - const struct msm_baud_map *entry; + struct msm_port *msm_port = UART_TO_MSM(port); + unsigned int divisor, result; + unsigned long target, old, best_rate = 0, diff, best_diff = ULONG_MAX; + const struct msm_baud_map *entry, *end, *best; static const struct msm_baud_map table[] = { - { 1536, 0x00, 1 }, - { 768, 0x11, 1 }, - { 384, 0x22, 1 }, - { 192, 0x33, 1 }, - { 96, 0x44, 1 }, - { 48, 0x55, 1 }, - { 32, 0x66, 1 }, - { 24, 0x77, 1 }, - { 16, 0x88, 1 }, - { 12, 0x99, 6 }, - { 8, 0xaa, 6 }, - { 6, 0xbb, 6 }, - { 4, 0xcc, 6 }, - { 3, 0xdd, 8 }, - { 2, 0xee, 16 }, { 1, 0xff, 31 }, - { 0, 0xff, 31 }, + { 2, 0xee, 16 }, + { 3, 0xdd, 8 }, + { 4, 0xcc, 6 }, + { 6, 0xbb, 6 }, + { 8, 0xaa, 6 }, + { 12, 0x99, 6 }, + { 16, 0x88, 1 }, + { 24, 0x77, 1 }, + { 32, 0x66, 1 }, + { 48, 0x55, 1 }, + { 96, 0x44, 1 }, + { 192, 0x33, 1 }, + { 384, 0x22, 1 }, + { 768, 0x11, 1 }, + { 1536, 0x00, 1 }, }; - divisor = uart_get_divisor(port, baud); + best = table; /* Default to smallest divider */ + target = clk_round_rate(msm_port->clk, 16 * baud); + divisor = DIV_ROUND_CLOSEST(target, 16 * baud); + + end = table + ARRAY_SIZE(table); + entry = table; + while (entry < end) { + if (entry->divisor <= divisor) { + result = target / entry->divisor / 16; + diff = abs(result - baud); + + /* Keep track of best entry */ + if (diff < best_diff) { + best_diff = diff; + best = entry; + best_rate = target; + } - for (i = 0, entry = table; i < ARRAY_SIZE(table); i++, entry++) - if (entry->divisor <= divisor) - break; + if (result == baud) + break; + } else if (entry->divisor > divisor) { + old = target; + target = clk_round_rate(msm_port->clk, old + 1); + /* + * The rate didn't get any faster so we can't do + * better at dividing it down + */ + if (target == old) + break; - return entry; /* Default to smallest divider */ + /* Start the divisor search over at this new rate */ + entry = table; + divisor = DIV_ROUND_CLOSEST(target, 16 * baud); + continue; + } + entry++; + } + + *rate = best_rate; + return best; } static int msm_set_baud_rate(struct uart_port *port, unsigned int baud, @@ -900,22 +935,20 @@ static int msm_set_baud_rate(struct uart_port *port, unsigned int baud, unsigned int rxstale, watermark, mask; struct msm_port *msm_port = UART_TO_MSM(port); const struct msm_baud_map *entry; - unsigned long flags; - - entry = msm_find_best_baud(port, baud); - - msm_write(port, entry->code, UART_CSR); - - if (baud > 460800) - port->uartclk = baud * 16; + unsigned long flags, rate; flags = *saved_flags; spin_unlock_irqrestore(&port->lock, flags); - clk_set_rate(msm_port->clk, port->uartclk); + entry = msm_find_best_baud(port, baud, &rate); + clk_set_rate(msm_port->clk, rate); + baud = rate / 16 / entry->divisor; spin_lock_irqsave(&port->lock, flags); *saved_flags = flags; + port->uartclk = rate; + + msm_write(port, entry->code, UART_CSR); /* RX stale watermark */ rxstale = entry->rxstale; @@ -1577,8 +1610,6 @@ static int msm_serial_probe(struct platform_device *pdev) msm_port->pclk = devm_clk_get(&pdev->dev, "iface"); if (IS_ERR(msm_port->pclk)) return PTR_ERR(msm_port->pclk); - - clk_set_rate(msm_port->clk, 1843200); } port->uartclk = clk_get_rate(msm_port->clk); diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index 0ff27818b..45b57c294 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -1,5 +1,7 @@ /* * *************************************************************************** +* Marvell Armada-3700 Serial Driver +* Author: Wilson Ding * Copyright (C) 2015 Marvell International Ltd. * *************************************************************************** * This program is free software: you can redistribute it and/or modify it @@ -23,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -299,6 +300,8 @@ static int mvebu_uart_startup(struct uart_port *port) static void mvebu_uart_shutdown(struct uart_port *port) { writel(0, port->membase + UART_CTRL); + + free_irq(port->irq, port); } static void mvebu_uart_set_termios(struct uart_port *port, @@ -594,30 +597,18 @@ static int mvebu_uart_probe(struct platform_device *pdev) return 0; } -static int mvebu_uart_remove(struct platform_device *pdev) -{ - struct mvebu_uart_data *data = platform_get_drvdata(pdev); - - uart_remove_one_port(&mvebu_uart_driver, data->port); - data->port->private_data = NULL; - data->port->mapbase = 0; - return 0; -} - /* Match table for of_platform binding */ static const struct of_device_id mvebu_uart_of_match[] = { { .compatible = "marvell,armada-3700-uart", }, {} }; -MODULE_DEVICE_TABLE(of, mvebu_uart_of_match); static struct platform_driver mvebu_uart_platform_driver = { .probe = mvebu_uart_probe, - .remove = mvebu_uart_remove, .driver = { - .owner = THIS_MODULE, .name = "mvebu-uart", .of_match_table = of_match_ptr(mvebu_uart_of_match), + .suppress_bind_attrs = true, }, }; @@ -635,16 +626,4 @@ static int __init mvebu_uart_init(void) return ret; } - -static void __exit mvebu_uart_exit(void) -{ - platform_driver_unregister(&mvebu_uart_platform_driver); - uart_unregister_driver(&mvebu_uart_driver); -} - arch_initcall(mvebu_uart_init); -module_exit(mvebu_uart_exit); - -MODULE_AUTHOR("Wilson Ding "); -MODULE_DESCRIPTION("Marvell Armada-3700 Serial Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index cd0414bbe..eb54e5c77 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1,17 +1,18 @@ /* - * Freescale STMP37XX/STMP378X Application UART driver + * Application UART driver for: + * Freescale STMP37XX/STMP378X + * Alphascale ASM9260 * * Author: dmitry pervushin * + * Copyright 2014 Oleksij Rempel + * Provide Alphascale ASM9260 support. * Copyright 2008-2010 Freescale Semiconductor, Inc. * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html */ #if defined(CONFIG_SERIAL_MXS_AUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) @@ -51,30 +52,16 @@ #define MXS_AUART_PORTS 5 #define MXS_AUART_FIFO_SIZE 16 +#define SET_REG 0x4 +#define CLR_REG 0x8 +#define TOG_REG 0xc + #define AUART_CTRL0 0x00000000 -#define AUART_CTRL0_SET 0x00000004 -#define AUART_CTRL0_CLR 0x00000008 -#define AUART_CTRL0_TOG 0x0000000c #define AUART_CTRL1 0x00000010 -#define AUART_CTRL1_SET 0x00000014 -#define AUART_CTRL1_CLR 0x00000018 -#define AUART_CTRL1_TOG 0x0000001c #define AUART_CTRL2 0x00000020 -#define AUART_CTRL2_SET 0x00000024 -#define AUART_CTRL2_CLR 0x00000028 -#define AUART_CTRL2_TOG 0x0000002c #define AUART_LINECTRL 0x00000030 -#define AUART_LINECTRL_SET 0x00000034 -#define AUART_LINECTRL_CLR 0x00000038 -#define AUART_LINECTRL_TOG 0x0000003c #define AUART_LINECTRL2 0x00000040 -#define AUART_LINECTRL2_SET 0x00000044 -#define AUART_LINECTRL2_CLR 0x00000048 -#define AUART_LINECTRL2_TOG 0x0000004c #define AUART_INTR 0x00000050 -#define AUART_INTR_SET 0x00000054 -#define AUART_INTR_CLR 0x00000058 -#define AUART_INTR_TOG 0x0000005c #define AUART_DATA 0x00000060 #define AUART_STAT 0x00000070 #define AUART_DEBUG 0x00000080 @@ -136,11 +123,301 @@ #define AUART_STAT_FERR (1 << 16) #define AUART_STAT_RXCOUNT_MASK 0xffff +/* + * Start of Alphascale asm9260 defines + * This list contains only differences of existing bits + * between imx2x and asm9260 + */ +#define ASM9260_HW_CTRL0 0x0000 +/* + * RW. Tell the UART to execute the RX DMA Command. The + * UART will clear this bit at the end of receive execution. + */ +#define ASM9260_BM_CTRL0_RXDMA_RUN BIT(28) +/* RW. 0 use FIFO for status register; 1 use DMA */ +#define ASM9260_BM_CTRL0_RXTO_SOURCE_STATUS BIT(25) +/* + * RW. RX TIMEOUT Enable. Valid for FIFO and DMA. + * Warning: If this bit is set to 0, the RX timeout will not affect receive DMA + * operation. If this bit is set to 1, a receive timeout will cause the receive + * DMA logic to terminate by filling the remaining DMA bytes with garbage data. + */ +#define ASM9260_BM_CTRL0_RXTO_ENABLE BIT(24) +/* + * RW. Receive Timeout Counter Value: number of 8-bit-time to wait before + * asserting timeout on the RX input. If the RXFIFO is not empty and the RX + * input is idle, then the watchdog counter will decrement each bit-time. Note + * 7-bit-time is added to the programmed value, so a value of zero will set + * the counter to 7-bit-time, a value of 0x1 gives 15-bit-time and so on. Also + * note that the counter is reloaded at the end of each frame, so if the frame + * is 10 bits long and the timeout counter value is zero, then timeout will + * occur (when FIFO is not empty) even if the RX input is not idle. The default + * value is 0x3 (31 bit-time). + */ +#define ASM9260_BM_CTRL0_RXTO_MASK (0xff << 16) +/* TIMEOUT = (100*7+1)*(1/BAUD) */ +#define ASM9260_BM_CTRL0_DEFAULT_RXTIMEOUT (20 << 16) + +/* TX ctrl register */ +#define ASM9260_HW_CTRL1 0x0010 +/* + * RW. Tell the UART to execute the TX DMA Command. The + * UART will clear this bit at the end of transmit execution. + */ +#define ASM9260_BM_CTRL1_TXDMA_RUN BIT(28) + +#define ASM9260_HW_CTRL2 0x0020 +/* + * RW. Receive Interrupt FIFO Level Select. + * The trigger points for the receive interrupt are as follows: + * ONE_EIGHTHS = 0x0 Trigger on FIFO full to at least 2 of 16 entries. + * ONE_QUARTER = 0x1 Trigger on FIFO full to at least 4 of 16 entries. + * ONE_HALF = 0x2 Trigger on FIFO full to at least 8 of 16 entries. + * THREE_QUARTERS = 0x3 Trigger on FIFO full to at least 12 of 16 entries. + * SEVEN_EIGHTHS = 0x4 Trigger on FIFO full to at least 14 of 16 entries. + */ +#define ASM9260_BM_CTRL2_RXIFLSEL (7 << 20) +#define ASM9260_BM_CTRL2_DEFAULT_RXIFLSEL (3 << 20) +/* RW. Same as RXIFLSEL */ +#define ASM9260_BM_CTRL2_TXIFLSEL (7 << 16) +#define ASM9260_BM_CTRL2_DEFAULT_TXIFLSEL (2 << 16) +/* RW. Set DTR. When this bit is 1, the output is 0. */ +#define ASM9260_BM_CTRL2_DTR BIT(10) +/* RW. Loop Back Enable */ +#define ASM9260_BM_CTRL2_LBE BIT(7) +#define ASM9260_BM_CTRL2_PORT_ENABLE BIT(0) + +#define ASM9260_HW_LINECTRL 0x0030 +/* + * RW. Stick Parity Select. When bits 1, 2, and 7 of this register are set, the + * parity bit is transmitted and checked as a 0. When bits 1 and 7 are set, + * and bit 2 is 0, the parity bit is transmitted and checked as a 1. When this + * bit is cleared stick parity is disabled. + */ +#define ASM9260_BM_LCTRL_SPS BIT(7) +/* RW. Word length */ +#define ASM9260_BM_LCTRL_WLEN (3 << 5) +#define ASM9260_BM_LCTRL_CHRL_5 (0 << 5) +#define ASM9260_BM_LCTRL_CHRL_6 (1 << 5) +#define ASM9260_BM_LCTRL_CHRL_7 (2 << 5) +#define ASM9260_BM_LCTRL_CHRL_8 (3 << 5) + +/* + * Interrupt register. + * contains the interrupt enables and the interrupt status bits + */ +#define ASM9260_HW_INTR 0x0040 +/* Tx FIFO EMPTY Raw Interrupt enable */ +#define ASM9260_BM_INTR_TFEIEN BIT(27) +/* Overrun Error Interrupt Enable. */ +#define ASM9260_BM_INTR_OEIEN BIT(26) +/* Break Error Interrupt Enable. */ +#define ASM9260_BM_INTR_BEIEN BIT(25) +/* Parity Error Interrupt Enable. */ +#define ASM9260_BM_INTR_PEIEN BIT(24) +/* Framing Error Interrupt Enable. */ +#define ASM9260_BM_INTR_FEIEN BIT(23) + +/* nUARTDSR Modem Interrupt Enable. */ +#define ASM9260_BM_INTR_DSRMIEN BIT(19) +/* nUARTDCD Modem Interrupt Enable. */ +#define ASM9260_BM_INTR_DCDMIEN BIT(18) +/* nUARTRI Modem Interrupt Enable. */ +#define ASM9260_BM_INTR_RIMIEN BIT(16) +/* Auto-Boud Timeout */ +#define ASM9260_BM_INTR_ABTO BIT(13) +#define ASM9260_BM_INTR_ABEO BIT(12) +/* Tx FIFO EMPTY Raw Interrupt state */ +#define ASM9260_BM_INTR_TFEIS BIT(11) +/* Overrun Error */ +#define ASM9260_BM_INTR_OEIS BIT(10) +/* Break Error */ +#define ASM9260_BM_INTR_BEIS BIT(9) +/* Parity Error */ +#define ASM9260_BM_INTR_PEIS BIT(8) +/* Framing Error */ +#define ASM9260_BM_INTR_FEIS BIT(7) +#define ASM9260_BM_INTR_DSRMIS BIT(3) +#define ASM9260_BM_INTR_DCDMIS BIT(2) +#define ASM9260_BM_INTR_RIMIS BIT(0) + +/* + * RW. In DMA mode, up to 4 Received/Transmit characters can be accessed at a + * time. In PIO mode, only one character can be accessed at a time. The status + * register contains the receive data flags and valid bits. + */ +#define ASM9260_HW_DATA 0x0050 + +#define ASM9260_HW_STAT 0x0060 +/* RO. If 1, UARTAPP is present in this product. */ +#define ASM9260_BM_STAT_PRESENT BIT(31) +/* RO. If 1, HISPEED is present in this product. */ +#define ASM9260_BM_STAT_HISPEED BIT(30) +/* RO. Receive FIFO Full. */ +#define ASM9260_BM_STAT_RXFULL BIT(26) + +/* RO. The UART Debug Register contains the state of the DMA signals. */ +#define ASM9260_HW_DEBUG 0x0070 +/* DMA Command Run Status */ +#define ASM9260_BM_DEBUG_TXDMARUN BIT(5) +#define ASM9260_BM_DEBUG_RXDMARUN BIT(4) +/* DMA Command End Status */ +#define ASM9260_BM_DEBUG_TXCMDEND BIT(3) +#define ASM9260_BM_DEBUG_RXCMDEND BIT(2) +/* DMA Request Status */ +#define ASM9260_BM_DEBUG_TXDMARQ BIT(1) +#define ASM9260_BM_DEBUG_RXDMARQ BIT(0) + +#define ASM9260_HW_ILPR 0x0080 + +#define ASM9260_HW_RS485CTRL 0x0090 +/* + * RW. This bit reverses the polarity of the direction control signal on the RTS + * (or DTR) pin. + * If 0, The direction control pin will be driven to logic ‘0’ when the + * transmitter has data to be sent. It will be driven to logic ‘1’ after the + * last bit of data has been transmitted. + */ +#define ASM9260_BM_RS485CTRL_ONIV BIT(5) +/* RW. Enable Auto Direction Control. */ +#define ASM9260_BM_RS485CTRL_DIR_CTRL BIT(4) +/* + * RW. If 0 and DIR_CTRL = 1, pin RTS is used for direction control. + * If 1 and DIR_CTRL = 1, pin DTR is used for direction control. + */ +#define ASM9260_BM_RS485CTRL_PINSEL BIT(3) +/* RW. Enable Auto Address Detect (AAD). */ +#define ASM9260_BM_RS485CTRL_AADEN BIT(2) +/* RW. Disable receiver. */ +#define ASM9260_BM_RS485CTRL_RXDIS BIT(1) +/* RW. Enable RS-485/EIA-485 Normal Multidrop Mode (NMM) */ +#define ASM9260_BM_RS485CTRL_RS485EN BIT(0) + +#define ASM9260_HW_RS485ADRMATCH 0x00a0 +/* Contains the address match value. */ +#define ASM9260_BM_RS485ADRMATCH_MASK (0xff << 0) + +#define ASM9260_HW_RS485DLY 0x00b0 +/* + * RW. Contains the direction control (RTS or DTR) delay value. This delay time + * is in periods of the baud clock. + */ +#define ASM9260_BM_RS485DLY_MASK (0xff << 0) + +#define ASM9260_HW_AUTOBAUD 0x00c0 +/* WO. Auto-baud time-out interrupt clear bit. */ +#define ASM9260_BM_AUTOBAUD_TO_INT_CLR BIT(9) +/* WO. End of auto-baud interrupt clear bit. */ +#define ASM9260_BM_AUTOBAUD_EO_INT_CLR BIT(8) +/* Restart in case of timeout (counter restarts at next UART Rx falling edge) */ +#define ASM9260_BM_AUTOBAUD_AUTORESTART BIT(2) +/* Auto-baud mode select bit. 0 - Mode 0, 1 - Mode 1. */ +#define ASM9260_BM_AUTOBAUD_MODE BIT(1) +/* + * Auto-baud start (auto-baud is running). Auto-baud run bit. This bit is + * automatically cleared after auto-baud completion. + */ +#define ASM9260_BM_AUTOBAUD_START BIT(0) + +#define ASM9260_HW_CTRL3 0x00d0 +#define ASM9260_BM_CTRL3_OUTCLK_DIV_MASK (0xffff << 16) +/* + * RW. Provide clk over OUTCLK pin. In case of asm9260 it can be configured on + * pins 137 and 144. + */ +#define ASM9260_BM_CTRL3_MASTERMODE BIT(6) +/* RW. Baud Rate Mode: 1 - Enable sync mode. 0 - async mode. */ +#define ASM9260_BM_CTRL3_SYNCMODE BIT(4) +/* RW. 1 - MSB bit send frist; 0 - LSB bit frist. */ +#define ASM9260_BM_CTRL3_MSBF BIT(2) +/* RW. 1 - sample rate = 8 x Baudrate; 0 - sample rate = 16 x Baudrate. */ +#define ASM9260_BM_CTRL3_BAUD8 BIT(1) +/* RW. 1 - Set word length to 9bit. 0 - use ASM9260_BM_LCTRL_WLEN */ +#define ASM9260_BM_CTRL3_9BIT BIT(0) + +#define ASM9260_HW_ISO7816_CTRL 0x00e0 +/* RW. Enable High Speed mode. */ +#define ASM9260_BM_ISO7816CTRL_HS BIT(12) +/* Disable Successive Receive NACK */ +#define ASM9260_BM_ISO7816CTRL_DS_NACK BIT(8) +#define ASM9260_BM_ISO7816CTRL_MAX_ITER_MASK (0xff << 4) +/* Receive NACK Inhibit */ +#define ASM9260_BM_ISO7816CTRL_INACK BIT(3) +#define ASM9260_BM_ISO7816CTRL_NEG_DATA BIT(2) +/* RW. 1 - ISO7816 mode; 0 - USART mode */ +#define ASM9260_BM_ISO7816CTRL_ENABLE BIT(0) + +#define ASM9260_HW_ISO7816_ERRCNT 0x00f0 +/* Parity error counter. Will be cleared after reading */ +#define ASM9260_BM_ISO7816_NB_ERRORS_MASK (0xff << 0) + +#define ASM9260_HW_ISO7816_STATUS 0x0100 +/* Max number of Repetitions Reached */ +#define ASM9260_BM_ISO7816_STAT_ITERATION BIT(0) + +/* End of Alphascale asm9260 defines */ + static struct uart_driver auart_driver; enum mxs_auart_type { IMX23_AUART, IMX28_AUART, + ASM9260_AUART, +}; + +struct vendor_data { + const u16 *reg_offset; +}; + +enum { + REG_CTRL0, + REG_CTRL1, + REG_CTRL2, + REG_LINECTRL, + REG_LINECTRL2, + REG_INTR, + REG_DATA, + REG_STAT, + REG_DEBUG, + REG_VERSION, + REG_AUTOBAUD, + + /* The size of the array - must be last */ + REG_ARRAY_SIZE, +}; + +static const u16 mxs_asm9260_offsets[REG_ARRAY_SIZE] = { + [REG_CTRL0] = ASM9260_HW_CTRL0, + [REG_CTRL1] = ASM9260_HW_CTRL1, + [REG_CTRL2] = ASM9260_HW_CTRL2, + [REG_LINECTRL] = ASM9260_HW_LINECTRL, + [REG_INTR] = ASM9260_HW_INTR, + [REG_DATA] = ASM9260_HW_DATA, + [REG_STAT] = ASM9260_HW_STAT, + [REG_DEBUG] = ASM9260_HW_DEBUG, + [REG_AUTOBAUD] = ASM9260_HW_AUTOBAUD, +}; + +static const u16 mxs_stmp37xx_offsets[REG_ARRAY_SIZE] = { + [REG_CTRL0] = AUART_CTRL0, + [REG_CTRL1] = AUART_CTRL1, + [REG_CTRL2] = AUART_CTRL2, + [REG_LINECTRL] = AUART_LINECTRL, + [REG_LINECTRL2] = AUART_LINECTRL2, + [REG_INTR] = AUART_INTR, + [REG_DATA] = AUART_DATA, + [REG_STAT] = AUART_STAT, + [REG_DEBUG] = AUART_DEBUG, + [REG_VERSION] = AUART_VERSION, + [REG_AUTOBAUD] = AUART_AUTOBAUD, +}; + +static const struct vendor_data vendor_alphascale_asm9260 = { + .reg_offset = mxs_asm9260_offsets, +}; + +static const struct vendor_data vendor_freescale_stmp37xx = { + .reg_offset = mxs_stmp37xx_offsets, }; struct mxs_auart_port { @@ -153,8 +430,10 @@ struct mxs_auart_port { unsigned long flags; unsigned int mctrl_prev; enum mxs_auart_type devtype; + const struct vendor_data *vendor; struct clk *clk; + struct clk *clk_ahb; struct device *dev; /* for DMA */ @@ -174,6 +453,7 @@ struct mxs_auart_port { static const struct platform_device_id mxs_auart_devtype[] = { { .name = "mxs-auart-imx23", .driver_data = IMX23_AUART }, { .name = "mxs-auart-imx28", .driver_data = IMX28_AUART }, + { .name = "as-auart-asm9260", .driver_data = ASM9260_AUART }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mxs_auart_devtype); @@ -185,6 +465,9 @@ static const struct of_device_id mxs_auart_dt_ids[] = { }, { .compatible = "fsl,imx23-auart", .data = &mxs_auart_devtype[IMX23_AUART] + }, { + .compatible = "alphascale,asm9260-auart", + .data = &mxs_auart_devtype[ASM9260_AUART] }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mxs_auart_dt_ids); @@ -194,11 +477,54 @@ static inline int is_imx28_auart(struct mxs_auart_port *s) return s->devtype == IMX28_AUART; } +static inline int is_asm9260_auart(struct mxs_auart_port *s) +{ + return s->devtype == ASM9260_AUART; +} + static inline bool auart_dma_enabled(struct mxs_auart_port *s) { return s->flags & MXS_AUART_DMA_ENABLED; } +static unsigned int mxs_reg_to_offset(const struct mxs_auart_port *uap, + unsigned int reg) +{ + return uap->vendor->reg_offset[reg]; +} + +static unsigned int mxs_read(const struct mxs_auart_port *uap, + unsigned int reg) +{ + void __iomem *addr = uap->port.membase + mxs_reg_to_offset(uap, reg); + + return readl_relaxed(addr); +} + +static void mxs_write(unsigned int val, struct mxs_auart_port *uap, + unsigned int reg) +{ + void __iomem *addr = uap->port.membase + mxs_reg_to_offset(uap, reg); + + writel_relaxed(val, addr); +} + +static void mxs_set(unsigned int val, struct mxs_auart_port *uap, + unsigned int reg) +{ + void __iomem *addr = uap->port.membase + mxs_reg_to_offset(uap, reg); + + writel_relaxed(val, addr + SET_REG); +} + +static void mxs_clr(unsigned int val, struct mxs_auart_port *uap, + unsigned int reg) +{ + void __iomem *addr = uap->port.membase + mxs_reg_to_offset(uap, reg); + + writel_relaxed(val, addr + CLR_REG); +} + static void mxs_auart_stop_tx(struct uart_port *u); #define to_auart_port(u) container_of(u, struct mxs_auart_port, port) @@ -295,19 +621,16 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s) } - while (!(readl(s->port.membase + AUART_STAT) & - AUART_STAT_TXFF)) { + while (!(mxs_read(s, REG_STAT) & AUART_STAT_TXFF)) { if (s->port.x_char) { s->port.icount.tx++; - writel(s->port.x_char, - s->port.membase + AUART_DATA); + mxs_write(s->port.x_char, s, REG_DATA); s->port.x_char = 0; continue; } if (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) { s->port.icount.tx++; - writel(xmit->buf[xmit->tail], - s->port.membase + AUART_DATA); + mxs_write(xmit->buf[xmit->tail], s, REG_DATA); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); } else break; @@ -316,11 +639,9 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s) uart_write_wakeup(&s->port); if (uart_circ_empty(&(s->port.state->xmit))) - writel(AUART_INTR_TXIEN, - s->port.membase + AUART_INTR_CLR); + mxs_clr(AUART_INTR_TXIEN, s, REG_INTR); else - writel(AUART_INTR_TXIEN, - s->port.membase + AUART_INTR_SET); + mxs_set(AUART_INTR_TXIEN, s, REG_INTR); if (uart_tx_stopped(&s->port)) mxs_auart_stop_tx(&s->port); @@ -332,8 +653,8 @@ static void mxs_auart_rx_char(struct mxs_auart_port *s) u32 stat; u8 c; - c = readl(s->port.membase + AUART_DATA); - stat = readl(s->port.membase + AUART_STAT); + c = mxs_read(s, REG_DATA); + stat = mxs_read(s, REG_STAT); flag = TTY_NORMAL; s->port.icount.rx++; @@ -368,7 +689,7 @@ static void mxs_auart_rx_char(struct mxs_auart_port *s) uart_insert_char(&s->port, stat, AUART_STAT_OERR, c, flag); out: - writel(stat, s->port.membase + AUART_STAT); + mxs_write(stat, s, REG_STAT); } static void mxs_auart_rx_chars(struct mxs_auart_port *s) @@ -376,13 +697,13 @@ static void mxs_auart_rx_chars(struct mxs_auart_port *s) u32 stat = 0; for (;;) { - stat = readl(s->port.membase + AUART_STAT); + stat = mxs_read(s, REG_STAT); if (stat & AUART_STAT_RXFE) break; mxs_auart_rx_char(s); } - writel(stat, s->port.membase + AUART_STAT); + mxs_write(stat, s, REG_STAT); tty_flip_buffer_push(&s->port.state->port); } @@ -418,7 +739,7 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) { struct mxs_auart_port *s = to_auart_port(u); - u32 ctrl = readl(u->membase + AUART_CTRL2); + u32 ctrl = mxs_read(s, REG_CTRL2); ctrl &= ~(AUART_CTRL2_RTSEN | AUART_CTRL2_RTS); if (mctrl & TIOCM_RTS) { @@ -428,7 +749,7 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) ctrl |= AUART_CTRL2_RTS; } - writel(ctrl, u->membase + AUART_CTRL2); + mxs_write(ctrl, s, REG_CTRL2); mctrl_gpio_set(s->gpios, mctrl); } @@ -459,7 +780,7 @@ static u32 mxs_auart_modem_status(struct mxs_auart_port *s, u32 mctrl) static u32 mxs_auart_get_mctrl(struct uart_port *u) { struct mxs_auart_port *s = to_auart_port(u); - u32 stat = readl(u->membase + AUART_STAT); + u32 stat = mxs_read(s, REG_STAT); u32 mctrl = 0; if (stat & AUART_STAT_CTS) @@ -536,14 +857,14 @@ static void dma_rx_callback(void *arg) dma_unmap_sg(s->dev, &s->rx_sgl, 1, DMA_FROM_DEVICE); - stat = readl(s->port.membase + AUART_STAT); + stat = mxs_read(s, REG_STAT); stat &= ~(AUART_STAT_OERR | AUART_STAT_BERR | AUART_STAT_PERR | AUART_STAT_FERR); count = stat & AUART_STAT_RXCOUNT_MASK; tty_insert_flip_string(port, s->rx_dma_buf, count); - writel(stat, s->port.membase + AUART_STAT); + mxs_write(stat, s, REG_STAT); tty_flip_buffer_push(port); /* start the next DMA for RX. */ @@ -606,8 +927,8 @@ static void mxs_auart_dma_exit_channel(struct mxs_auart_port *s) static void mxs_auart_dma_exit(struct mxs_auart_port *s) { - writel(AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE | AUART_CTRL2_DMAONERR, - s->port.membase + AUART_CTRL2_CLR); + mxs_clr(AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE | AUART_CTRL2_DMAONERR, + s, REG_CTRL2); mxs_auart_dma_exit_channel(s); s->flags &= ~MXS_AUART_DMA_ENABLED; @@ -666,7 +987,7 @@ static void mxs_auart_settermios(struct uart_port *u, cflag = termios->c_cflag; ctrl = AUART_LINECTRL_FEN; - ctrl2 = readl(u->membase + AUART_CTRL2); + ctrl2 = mxs_read(s, REG_CTRL2); /* byte size */ switch (cflag & CSIZE) { @@ -754,15 +1075,24 @@ static void mxs_auart_settermios(struct uart_port *u, } /* set baud rate */ - baud_min = DIV_ROUND_UP(u->uartclk * 32, AUART_LINECTRL_BAUD_DIV_MAX); - baud_max = u->uartclk * 32 / AUART_LINECTRL_BAUD_DIV_MIN; - baud = uart_get_baud_rate(u, termios, old, baud_min, baud_max); - div = u->uartclk * 32 / baud; + if (is_asm9260_auart(s)) { + baud = uart_get_baud_rate(u, termios, old, + u->uartclk * 4 / 0x3FFFFF, + u->uartclk / 16); + div = u->uartclk * 4 / baud; + } else { + baud_min = DIV_ROUND_UP(u->uartclk * 32, + AUART_LINECTRL_BAUD_DIV_MAX); + baud_max = u->uartclk * 32 / AUART_LINECTRL_BAUD_DIV_MIN; + baud = uart_get_baud_rate(u, termios, old, baud_min, baud_max); + div = u->uartclk * 32 / baud; + } + ctrl |= AUART_LINECTRL_BAUD_DIVFRAC(div & 0x3F); ctrl |= AUART_LINECTRL_BAUD_DIVINT(div >> 6); + mxs_write(ctrl, s, REG_LINECTRL); - writel(ctrl, u->membase + AUART_LINECTRL); - writel(ctrl2, u->membase + AUART_CTRL2); + mxs_write(ctrl2, s, REG_CTRL2); uart_update_timeout(u, termios->c_cflag, baud); @@ -771,8 +1101,8 @@ static void mxs_auart_settermios(struct uart_port *u, !test_and_set_bit(MXS_AUART_DMA_RX_READY, &s->flags)) { if (!mxs_auart_dma_prep_rx(s)) { /* Disable the normal RX interrupt. */ - writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN, - u->membase + AUART_INTR_CLR); + mxs_clr(AUART_INTR_RXIEN | AUART_INTR_RTIEN, + s, REG_INTR); } else { mxs_auart_dma_exit(s); dev_err(s->dev, "We can not start up the DMA.\n"); @@ -802,16 +1132,13 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) u32 istat; struct mxs_auart_port *s = context; u32 mctrl_temp = s->mctrl_prev; - u32 stat = readl(s->port.membase + AUART_STAT); + u32 stat = mxs_read(s, REG_STAT); - istat = readl(s->port.membase + AUART_INTR); + istat = mxs_read(s, REG_INTR); /* ack irq */ - writel(istat & (AUART_INTR_RTIS - | AUART_INTR_TXIS - | AUART_INTR_RXIS - | AUART_INTR_CTSMIS), - s->port.membase + AUART_INTR_CLR); + mxs_clr(istat & (AUART_INTR_RTIS | AUART_INTR_TXIS | AUART_INTR_RXIS + | AUART_INTR_CTSMIS), s, REG_INTR); /* * Dealing with GPIO interrupt @@ -827,8 +1154,7 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) if (CTS_AT_AUART() && s->ms_irq_enabled) uart_handle_cts_change(&s->port, stat & AUART_STAT_CTS); - writel(AUART_INTR_CTSMIS, - s->port.membase + AUART_INTR_CLR); + mxs_clr(AUART_INTR_CTSMIS, s, REG_INTR); istat &= ~AUART_INTR_CTSMIS; } @@ -846,44 +1172,44 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) return IRQ_HANDLED; } -static void mxs_auart_reset_deassert(struct uart_port *u) +static void mxs_auart_reset_deassert(struct mxs_auart_port *s) { int i; unsigned int reg; - writel(AUART_CTRL0_SFTRST, u->membase + AUART_CTRL0_CLR); + mxs_clr(AUART_CTRL0_SFTRST, s, REG_CTRL0); for (i = 0; i < 10000; i++) { - reg = readl(u->membase + AUART_CTRL0); + reg = mxs_read(s, REG_CTRL0); if (!(reg & AUART_CTRL0_SFTRST)) break; udelay(3); } - writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR); + mxs_clr(AUART_CTRL0_CLKGATE, s, REG_CTRL0); } -static void mxs_auart_reset_assert(struct uart_port *u) +static void mxs_auart_reset_assert(struct mxs_auart_port *s) { int i; u32 reg; - reg = readl(u->membase + AUART_CTRL0); + reg = mxs_read(s, REG_CTRL0); /* if already in reset state, keep it untouched */ if (reg & AUART_CTRL0_SFTRST) return; - writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR); - writel(AUART_CTRL0_SFTRST, u->membase + AUART_CTRL0_SET); + mxs_clr(AUART_CTRL0_CLKGATE, s, REG_CTRL0); + mxs_set(AUART_CTRL0_SFTRST, s, REG_CTRL0); for (i = 0; i < 1000; i++) { - reg = readl(u->membase + AUART_CTRL0); + reg = mxs_read(s, REG_CTRL0); /* reset is finished when the clock is gated */ if (reg & AUART_CTRL0_CLKGATE) return; udelay(10); } - dev_err(u->dev, "Failed to reset the unit."); + dev_err(s->dev, "Failed to reset the unit."); } static int mxs_auart_startup(struct uart_port *u) @@ -896,17 +1222,17 @@ static int mxs_auart_startup(struct uart_port *u) return ret; if (uart_console(u)) { - writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR); + mxs_clr(AUART_CTRL0_CLKGATE, s, REG_CTRL0); } else { /* reset the unit to a well known state */ - mxs_auart_reset_assert(u); - mxs_auart_reset_deassert(u); + mxs_auart_reset_assert(s); + mxs_auart_reset_deassert(s); } - writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_SET); + mxs_set(AUART_CTRL2_UARTEN, s, REG_CTRL2); - writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN, - u->membase + AUART_INTR); + mxs_write(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN, + s, REG_INTR); /* Reset FIFO size (it could have changed if DMA was enabled) */ u->fifosize = MXS_AUART_FIFO_SIZE; @@ -915,7 +1241,7 @@ static int mxs_auart_startup(struct uart_port *u) * Enable fifo so all four bytes of a DMA word are written to * output (otherwise, only the LSB is written, ie. 1 in 4 bytes) */ - writel(AUART_LINECTRL_FEN, u->membase + AUART_LINECTRL_SET); + mxs_set(AUART_LINECTRL_FEN, s, REG_LINECTRL); /* get initial status of modem lines */ mctrl_gpio_get(s->gpios, &s->mctrl_prev); @@ -934,12 +1260,13 @@ static void mxs_auart_shutdown(struct uart_port *u) mxs_auart_dma_exit(s); if (uart_console(u)) { - writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_CLR); - writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN, - u->membase + AUART_INTR_CLR); - writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_SET); + mxs_clr(AUART_CTRL2_UARTEN, s, REG_CTRL2); + + mxs_clr(AUART_INTR_RXIEN | AUART_INTR_RTIEN | + AUART_INTR_CTSMIEN, s, REG_INTR); + mxs_set(AUART_CTRL0_CLKGATE, s, REG_CTRL0); } else { - mxs_auart_reset_assert(u); + mxs_auart_reset_assert(s); } clk_disable_unprepare(s->clk); @@ -947,7 +1274,9 @@ static void mxs_auart_shutdown(struct uart_port *u) static unsigned int mxs_auart_tx_empty(struct uart_port *u) { - if ((readl(u->membase + AUART_STAT) & + struct mxs_auart_port *s = to_auart_port(u); + + if ((mxs_read(s, REG_STAT) & (AUART_STAT_TXFE | AUART_STAT_BUSY)) == AUART_STAT_TXFE) return TIOCSER_TEMT; @@ -959,29 +1288,33 @@ static void mxs_auart_start_tx(struct uart_port *u) struct mxs_auart_port *s = to_auart_port(u); /* enable transmitter */ - writel(AUART_CTRL2_TXE, u->membase + AUART_CTRL2_SET); + mxs_set(AUART_CTRL2_TXE, s, REG_CTRL2); mxs_auart_tx_chars(s); } static void mxs_auart_stop_tx(struct uart_port *u) { - writel(AUART_CTRL2_TXE, u->membase + AUART_CTRL2_CLR); + struct mxs_auart_port *s = to_auart_port(u); + + mxs_clr(AUART_CTRL2_TXE, s, REG_CTRL2); } static void mxs_auart_stop_rx(struct uart_port *u) { - writel(AUART_CTRL2_RXE, u->membase + AUART_CTRL2_CLR); + struct mxs_auart_port *s = to_auart_port(u); + + mxs_clr(AUART_CTRL2_RXE, s, REG_CTRL2); } static void mxs_auart_break_ctl(struct uart_port *u, int ctl) { + struct mxs_auart_port *s = to_auart_port(u); + if (ctl) - writel(AUART_LINECTRL_BRK, - u->membase + AUART_LINECTRL_SET); + mxs_set(AUART_LINECTRL_BRK, s, REG_LINECTRL); else - writel(AUART_LINECTRL_BRK, - u->membase + AUART_LINECTRL_CLR); + mxs_clr(AUART_LINECTRL_BRK, s, REG_LINECTRL); } static struct uart_ops mxs_auart_ops = { @@ -1009,15 +1342,16 @@ static struct mxs_auart_port *auart_port[MXS_AUART_PORTS]; #ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE static void mxs_auart_console_putchar(struct uart_port *port, int ch) { + struct mxs_auart_port *s = to_auart_port(port); unsigned int to = 1000; - while (readl(port->membase + AUART_STAT) & AUART_STAT_TXFF) { + while (mxs_read(s, REG_STAT) & AUART_STAT_TXFF) { if (!to--) break; udelay(1); } - writel(ch, port->membase + AUART_DATA); + mxs_write(ch, s, REG_DATA); } static void @@ -1037,18 +1371,16 @@ auart_console_write(struct console *co, const char *str, unsigned int count) clk_enable(s->clk); /* First save the CR then disable the interrupts */ - old_ctrl2 = readl(port->membase + AUART_CTRL2); - old_ctrl0 = readl(port->membase + AUART_CTRL0); + old_ctrl2 = mxs_read(s, REG_CTRL2); + old_ctrl0 = mxs_read(s, REG_CTRL0); - writel(AUART_CTRL0_CLKGATE, - port->membase + AUART_CTRL0_CLR); - writel(AUART_CTRL2_UARTEN | AUART_CTRL2_TXE, - port->membase + AUART_CTRL2_SET); + mxs_clr(AUART_CTRL0_CLKGATE, s, REG_CTRL0); + mxs_set(AUART_CTRL2_UARTEN | AUART_CTRL2_TXE, s, REG_CTRL2); uart_console_write(port, str, count, mxs_auart_console_putchar); /* Finally, wait for transmitter to become empty ... */ - while (readl(port->membase + AUART_STAT) & AUART_STAT_BUSY) { + while (mxs_read(s, REG_STAT) & AUART_STAT_BUSY) { udelay(1); if (!to--) break; @@ -1060,24 +1392,25 @@ auart_console_write(struct console *co, const char *str, unsigned int count) * unused, but that is better than to disable it while it is still * transmitting. */ - if (!(readl(port->membase + AUART_STAT) & AUART_STAT_BUSY)) { - writel(old_ctrl0, port->membase + AUART_CTRL0); - writel(old_ctrl2, port->membase + AUART_CTRL2); + if (!(mxs_read(s, REG_STAT) & AUART_STAT_BUSY)) { + mxs_write(old_ctrl0, s, REG_CTRL0); + mxs_write(old_ctrl2, s, REG_CTRL2); } clk_disable(s->clk); } static void __init -auart_console_get_options(struct uart_port *port, int *baud, +auart_console_get_options(struct mxs_auart_port *s, int *baud, int *parity, int *bits) { + struct uart_port *port = &s->port; unsigned int lcr_h, quot; - if (!(readl(port->membase + AUART_CTRL2) & AUART_CTRL2_UARTEN)) + if (!(mxs_read(s, REG_CTRL2) & AUART_CTRL2_UARTEN)) return; - lcr_h = readl(port->membase + AUART_LINECTRL); + lcr_h = mxs_read(s, REG_LINECTRL); *parity = 'n'; if (lcr_h & AUART_LINECTRL_PEN) { @@ -1092,12 +1425,10 @@ auart_console_get_options(struct uart_port *port, int *baud, else *bits = 8; - quot = ((readl(port->membase + AUART_LINECTRL) - & AUART_LINECTRL_BAUD_DIVINT_MASK)) - >> (AUART_LINECTRL_BAUD_DIVINT_SHIFT - 6); - quot |= ((readl(port->membase + AUART_LINECTRL) - & AUART_LINECTRL_BAUD_DIVFRAC_MASK)) - >> AUART_LINECTRL_BAUD_DIVFRAC_SHIFT; + quot = ((mxs_read(s, REG_LINECTRL) & AUART_LINECTRL_BAUD_DIVINT_MASK)) + >> (AUART_LINECTRL_BAUD_DIVINT_SHIFT - 6); + quot |= ((mxs_read(s, REG_LINECTRL) & AUART_LINECTRL_BAUD_DIVFRAC_MASK)) + >> AUART_LINECTRL_BAUD_DIVFRAC_SHIFT; if (quot == 0) quot = 1; @@ -1132,7 +1463,7 @@ auart_console_setup(struct console *co, char *options) if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else - auart_console_get_options(&s->port, &baud, &parity, &bits); + auart_console_get_options(s, &baud, &parity, &bits); ret = uart_set_options(&s->port, co, baud, parity, bits, flow); @@ -1164,6 +1495,60 @@ static struct uart_driver auart_driver = { #endif }; +static void mxs_init_regs(struct mxs_auart_port *s) +{ + if (is_asm9260_auart(s)) + s->vendor = &vendor_alphascale_asm9260; + else + s->vendor = &vendor_freescale_stmp37xx; +} + +static int mxs_get_clks(struct mxs_auart_port *s, + struct platform_device *pdev) +{ + int err; + + if (!is_asm9260_auart(s)) { + s->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(s->clk)) + return PTR_ERR(s->clk); + + return 0; + } + + s->clk = devm_clk_get(s->dev, "mod"); + if (IS_ERR(s->clk)) { + dev_err(s->dev, "Failed to get \"mod\" clk\n"); + return PTR_ERR(s->clk); + } + + s->clk_ahb = devm_clk_get(s->dev, "ahb"); + if (IS_ERR(s->clk_ahb)) { + dev_err(s->dev, "Failed to get \"ahb\" clk\n"); + return PTR_ERR(s->clk_ahb); + } + + err = clk_prepare_enable(s->clk_ahb); + if (err) { + dev_err(s->dev, "Failed to enable ahb_clk!\n"); + return err; + } + + err = clk_set_rate(s->clk, clk_get_rate(s->clk_ahb)); + if (err) { + dev_err(s->dev, "Failed to set rate!\n"); + return err; + } + + err = clk_prepare_enable(s->clk); + if (err) { + dev_err(s->dev, "Failed to enable clk!\n"); + return err; + } + + return 0; +} + /* * This function returns 1 if pdev isn't a device instatiated by dt, 0 if it * could successfully get all information from dt or a negative errno. @@ -1185,7 +1570,8 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, } s->port.line = ret; - if (of_get_property(np, "fsl,uart-has-rtscts", NULL)) + if (of_get_property(np, "uart-has-rtscts", NULL) || + of_get_property(np, "fsl,uart-has-rtscts", NULL) /* deprecated */) set_bit(MXS_AUART_RTSCTS, &s->flags); return 0; @@ -1269,6 +1655,9 @@ static int mxs_auart_probe(struct platform_device *pdev) if (!s) return -ENOMEM; + s->port.dev = &pdev->dev; + s->dev = &pdev->dev; + ret = serial_mxs_probe_dt(s, pdev); if (ret > 0) s->port.line = pdev->id < 0 ? 0 : pdev->id; @@ -1280,15 +1669,14 @@ static int mxs_auart_probe(struct platform_device *pdev) s->devtype = pdev->id_entry->driver_data; } - s->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(s->clk)) - return PTR_ERR(s->clk); + ret = mxs_get_clks(s, pdev); + if (ret) + return ret; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) return -ENXIO; - s->port.mapbase = r->start; s->port.membase = ioremap(r->start, resource_size(r)); s->port.ops = &mxs_auart_ops; @@ -1296,7 +1684,8 @@ static int mxs_auart_probe(struct platform_device *pdev) s->port.fifosize = MXS_AUART_FIFO_SIZE; s->port.uartclk = clk_get_rate(s->clk); s->port.type = PORT_IMX; - s->port.dev = s->dev = &pdev->dev; + + mxs_init_regs(s); s->mctrl_prev = 0; @@ -1327,16 +1716,21 @@ static int mxs_auart_probe(struct platform_device *pdev) auart_port[s->port.line] = s; - mxs_auart_reset_deassert(&s->port); + mxs_auart_reset_deassert(s); ret = uart_add_one_port(&auart_driver, &s->port); if (ret) goto out_free_gpio_irq; - version = readl(s->port.membase + AUART_VERSION); - dev_info(&pdev->dev, "Found APPUART %d.%d.%d\n", - (version >> 24) & 0xff, - (version >> 16) & 0xff, version & 0xffff); + /* ASM9260 don't have version reg */ + if (is_asm9260_auart(s)) { + dev_info(&pdev->dev, "Found APPUART ASM9260\n"); + } else { + version = mxs_read(s, REG_VERSION); + dev_info(&pdev->dev, "Found APPUART %d.%d.%d\n", + (version >> 24) & 0xff, + (version >> 16) & 0xff, version & 0xffff); + } return 0; diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c new file mode 100644 index 000000000..62a43bf56 --- /dev/null +++ b/drivers/tty/serial/pic32_uart.c @@ -0,0 +1,960 @@ +/* + * PIC32 Integrated Serial Driver. + * + * Copyright (C) 2015 Microchip Technology, Inc. + * + * Authors: + * Sorin-Andrei Pistirica + * + * Licensed under GPLv2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "pic32_uart.h" + +/* UART name and device definitions */ +#define PIC32_DEV_NAME "pic32-uart" +#define PIC32_MAX_UARTS 6 +#define PIC32_SDEV_NAME "ttyPIC" + +/* pic32_sport pointer for console use */ +static struct pic32_sport *pic32_sports[PIC32_MAX_UARTS]; + +static inline void pic32_wait_deplete_txbuf(struct pic32_sport *sport) +{ + /* wait for tx empty, otherwise chars will be lost or corrupted */ + while (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_TRMT)) + udelay(1); +} + +static inline int pic32_enable_clock(struct pic32_sport *sport) +{ + int ret = clk_prepare_enable(sport->clk); + + if (ret) + return ret; + + sport->ref_clk++; + return 0; +} + +static inline void pic32_disable_clock(struct pic32_sport *sport) +{ + sport->ref_clk--; + clk_disable_unprepare(sport->clk); +} + +/* serial core request to check if uart tx buffer is empty */ +static unsigned int pic32_uart_tx_empty(struct uart_port *port) +{ + struct pic32_sport *sport = to_pic32_sport(port); + u32 val = pic32_uart_readl(sport, PIC32_UART_STA); + + return (val & PIC32_UART_STA_TRMT) ? 1 : 0; +} + +/* serial core request to set UART outputs */ +static void pic32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct pic32_sport *sport = to_pic32_sport(port); + + /* set loopback mode */ + if (mctrl & TIOCM_LOOP) + pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE), + PIC32_UART_MODE_LPBK); + else + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE), + PIC32_UART_MODE_LPBK); +} + +/* get the state of CTS input pin for this port */ +static unsigned int get_cts_state(struct pic32_sport *sport) +{ + /* read and invert UxCTS */ + if (gpio_is_valid(sport->cts_gpio)) + return !gpio_get_value(sport->cts_gpio); + + return 1; +} + +/* serial core request to return the state of misc UART input pins */ +static unsigned int pic32_uart_get_mctrl(struct uart_port *port) +{ + struct pic32_sport *sport = to_pic32_sport(port); + unsigned int mctrl = 0; + + if (!sport->hw_flow_ctrl) + mctrl |= TIOCM_CTS; + else if (get_cts_state(sport)) + mctrl |= TIOCM_CTS; + + /* DSR and CD are not supported in PIC32, so return 1 + * RI is not supported in PIC32, so return 0 + */ + mctrl |= TIOCM_CD; + mctrl |= TIOCM_DSR; + + return mctrl; +} + +/* stop tx and start tx are not called in pairs, therefore a flag indicates + * the status of irq to control the irq-depth. + */ +static inline void pic32_uart_irqtxen(struct pic32_sport *sport, u8 en) +{ + if (en && !tx_irq_enabled(sport)) { + enable_irq(sport->irq_tx); + tx_irq_enabled(sport) = 1; + } else if (!en && tx_irq_enabled(sport)) { + /* use disable_irq_nosync() and not disable_irq() to avoid self + * imposed deadlock by not waiting for irq handler to end, + * since this callback is called from interrupt context. + */ + disable_irq_nosync(sport->irq_tx); + tx_irq_enabled(sport) = 0; + } +} + +/* serial core request to disable tx ASAP (used for flow control) */ +static void pic32_uart_stop_tx(struct uart_port *port) +{ + struct pic32_sport *sport = to_pic32_sport(port); + + if (!(pic32_uart_readl(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON)) + return; + + if (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN)) + return; + + /* wait for tx empty */ + pic32_wait_deplete_txbuf(sport); + + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA), + PIC32_UART_STA_UTXEN); + pic32_uart_irqtxen(sport, 0); +} + +/* serial core request to (re)enable tx */ +static void pic32_uart_start_tx(struct uart_port *port) +{ + struct pic32_sport *sport = to_pic32_sport(port); + + pic32_uart_irqtxen(sport, 1); + pic32_uart_writel(sport, PIC32_SET(PIC32_UART_STA), + PIC32_UART_STA_UTXEN); +} + +/* serial core request to stop rx, called before port shutdown */ +static void pic32_uart_stop_rx(struct uart_port *port) +{ + struct pic32_sport *sport = to_pic32_sport(port); + + /* disable rx interrupts */ + disable_irq(sport->irq_rx); + + /* receiver Enable bit OFF */ + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA), + PIC32_UART_STA_URXEN); +} + +/* serial core request to start/stop emitting break char */ +static void pic32_uart_break_ctl(struct uart_port *port, int ctl) +{ + struct pic32_sport *sport = to_pic32_sport(port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + if (ctl) + pic32_uart_writel(sport, PIC32_SET(PIC32_UART_STA), + PIC32_UART_STA_UTXBRK); + else + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA), + PIC32_UART_STA_UTXBRK); + + spin_unlock_irqrestore(&port->lock, flags); +} + +/* get port type in string format */ +static const char *pic32_uart_type(struct uart_port *port) +{ + return (port->type == PORT_PIC32) ? PIC32_DEV_NAME : NULL; +} + +/* read all chars in rx fifo and send them to core */ +static void pic32_uart_do_rx(struct uart_port *port) +{ + struct pic32_sport *sport = to_pic32_sport(port); + struct tty_port *tty; + unsigned int max_count; + + /* limit number of char read in interrupt, should not be + * higher than fifo size anyway since we're much faster than + * serial port + */ + max_count = PIC32_UART_RX_FIFO_DEPTH; + + spin_lock(&port->lock); + + tty = &port->state->port; + + do { + u32 sta_reg, c; + char flag; + + /* get overrun/fifo empty information from status register */ + sta_reg = pic32_uart_readl(sport, PIC32_UART_STA); + if (unlikely(sta_reg & PIC32_UART_STA_OERR)) { + + /* fifo reset is required to clear interrupt */ + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA), + PIC32_UART_STA_OERR); + + port->icount.overrun++; + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + + /* Can at least one more character can be read? */ + if (!(sta_reg & PIC32_UART_STA_URXDA)) + break; + + /* read the character and increment the rx counter */ + c = pic32_uart_readl(sport, PIC32_UART_RX); + + port->icount.rx++; + flag = TTY_NORMAL; + c &= 0xff; + + if (unlikely((sta_reg & PIC32_UART_STA_PERR) || + (sta_reg & PIC32_UART_STA_FERR))) { + + /* do stats first */ + if (sta_reg & PIC32_UART_STA_PERR) + port->icount.parity++; + if (sta_reg & PIC32_UART_STA_FERR) + port->icount.frame++; + + /* update flag wrt read_status_mask */ + sta_reg &= port->read_status_mask; + + if (sta_reg & PIC32_UART_STA_FERR) + flag = TTY_FRAME; + if (sta_reg & PIC32_UART_STA_PERR) + flag = TTY_PARITY; + } + + if (uart_handle_sysrq_char(port, c)) + continue; + + if ((sta_reg & port->ignore_status_mask) == 0) + tty_insert_flip_char(tty, c, flag); + + } while (--max_count); + + spin_unlock(&port->lock); + + tty_flip_buffer_push(tty); +} + +/* fill tx fifo with chars to send, stop when fifo is about to be full + * or when all chars have been sent. + */ +static void pic32_uart_do_tx(struct uart_port *port) +{ + struct pic32_sport *sport = to_pic32_sport(port); + struct circ_buf *xmit = &port->state->xmit; + unsigned int max_count = PIC32_UART_TX_FIFO_DEPTH; + + if (port->x_char) { + pic32_uart_writel(sport, PIC32_UART_TX, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_tx_stopped(port)) { + pic32_uart_stop_tx(port); + return; + } + + if (uart_circ_empty(xmit)) + goto txq_empty; + + /* keep stuffing chars into uart tx buffer + * 1) until uart fifo is full + * or + * 2) until the circ buffer is empty + * (all chars have been sent) + * or + * 3) until the max count is reached + * (prevents lingering here for too long in certain cases) + */ + while (!(PIC32_UART_STA_UTXBF & + pic32_uart_readl(sport, PIC32_UART_STA))) { + unsigned int c = xmit->buf[xmit->tail]; + + pic32_uart_writel(sport, PIC32_UART_TX, c); + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + if (--max_count == 0) + break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + goto txq_empty; + + return; + +txq_empty: + pic32_uart_irqtxen(sport, 0); +} + +/* RX interrupt handler */ +static irqreturn_t pic32_uart_rx_interrupt(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + + pic32_uart_do_rx(port); + + return IRQ_HANDLED; +} + +/* TX interrupt handler */ +static irqreturn_t pic32_uart_tx_interrupt(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + pic32_uart_do_tx(port); + spin_unlock_irqrestore(&port->lock, flags); + + return IRQ_HANDLED; +} + +/* FAULT interrupt handler */ +static irqreturn_t pic32_uart_fault_interrupt(int irq, void *dev_id) +{ + /* do nothing: pic32_uart_do_rx() handles faults. */ + return IRQ_HANDLED; +} + +/* enable rx & tx operation on uart */ +static void pic32_uart_en_and_unmask(struct uart_port *port) +{ + struct pic32_sport *sport = to_pic32_sport(port); + + pic32_uart_writel(sport, PIC32_SET(PIC32_UART_STA), + PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN); + pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE), + PIC32_UART_MODE_ON); +} + +/* disable rx & tx operation on uart */ +static void pic32_uart_dsbl_and_mask(struct uart_port *port) +{ + struct pic32_sport *sport = to_pic32_sport(port); + + /* wait for tx empty, otherwise chars will be lost or corrupted */ + pic32_wait_deplete_txbuf(sport); + + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA), + PIC32_UART_STA_UTXEN | PIC32_UART_STA_URXEN); + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE), + PIC32_UART_MODE_ON); +} + +/* serial core request to initialize uart and start rx operation */ +static int pic32_uart_startup(struct uart_port *port) +{ + struct pic32_sport *sport = to_pic32_sport(port); + u32 dflt_baud = (port->uartclk / PIC32_UART_DFLT_BRATE / 16) - 1; + unsigned long flags; + int ret; + + local_irq_save(flags); + + ret = pic32_enable_clock(sport); + if (ret) { + local_irq_restore(flags); + goto out_done; + } + + /* clear status and mode registers */ + pic32_uart_writel(sport, PIC32_UART_MODE, 0); + pic32_uart_writel(sport, PIC32_UART_STA, 0); + + /* disable uart and mask all interrupts */ + pic32_uart_dsbl_and_mask(port); + + /* set default baud */ + pic32_uart_writel(sport, PIC32_UART_BRG, dflt_baud); + + local_irq_restore(flags); + + /* Each UART of a PIC32 has three interrupts therefore, + * we setup driver to register the 3 irqs for the device. + * + * For each irq request_irq() is called with interrupt disabled. + * And the irq is enabled as soon as we are ready to handle them. + */ + tx_irq_enabled(sport) = 0; + + sport->irq_fault_name = kasprintf(GFP_KERNEL, "%s%d-fault", + pic32_uart_type(port), + sport->idx); + if (!sport->irq_fault_name) { + dev_err(port->dev, "%s: kasprintf err!", __func__); + ret = -ENOMEM; + goto out_done; + } + irq_set_status_flags(sport->irq_fault, IRQ_NOAUTOEN); + ret = request_irq(sport->irq_fault, pic32_uart_fault_interrupt, + sport->irqflags_fault, sport->irq_fault_name, port); + if (ret) { + dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n", + __func__, sport->irq_fault, ret, + pic32_uart_type(port)); + goto out_f; + } + + sport->irq_rx_name = kasprintf(GFP_KERNEL, "%s%d-rx", + pic32_uart_type(port), + sport->idx); + if (!sport->irq_rx_name) { + dev_err(port->dev, "%s: kasprintf err!", __func__); + kfree(sport->irq_fault_name); + ret = -ENOMEM; + goto out_f; + } + irq_set_status_flags(sport->irq_rx, IRQ_NOAUTOEN); + ret = request_irq(sport->irq_rx, pic32_uart_rx_interrupt, + sport->irqflags_rx, sport->irq_rx_name, port); + if (ret) { + dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n", + __func__, sport->irq_rx, ret, + pic32_uart_type(port)); + goto out_r; + } + + sport->irq_tx_name = kasprintf(GFP_KERNEL, "%s%d-tx", + pic32_uart_type(port), + sport->idx); + if (!sport->irq_tx_name) { + dev_err(port->dev, "%s: kasprintf err!", __func__); + ret = -ENOMEM; + goto out_r; + } + irq_set_status_flags(sport->irq_tx, IRQ_NOAUTOEN); + ret = request_irq(sport->irq_tx, pic32_uart_tx_interrupt, + sport->irqflags_tx, sport->irq_tx_name, port); + if (ret) { + dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n", + __func__, sport->irq_tx, ret, + pic32_uart_type(port)); + goto out_t; + } + + local_irq_save(flags); + + /* set rx interrupt on first receive */ + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA), + PIC32_UART_STA_URXISEL1 | PIC32_UART_STA_URXISEL0); + + /* set interrupt on empty */ + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA), + PIC32_UART_STA_UTXISEL1); + + /* enable all interrupts and eanable uart */ + pic32_uart_en_and_unmask(port); + + enable_irq(sport->irq_rx); + + return 0; + +out_t: + kfree(sport->irq_tx_name); + free_irq(sport->irq_tx, sport); +out_r: + kfree(sport->irq_rx_name); + free_irq(sport->irq_rx, sport); +out_f: + kfree(sport->irq_fault_name); + free_irq(sport->irq_fault, sport); +out_done: + return ret; +} + +/* serial core request to flush & disable uart */ +static void pic32_uart_shutdown(struct uart_port *port) +{ + struct pic32_sport *sport = to_pic32_sport(port); + unsigned long flags; + + /* disable uart */ + spin_lock_irqsave(&port->lock, flags); + pic32_uart_dsbl_and_mask(port); + spin_unlock_irqrestore(&port->lock, flags); + pic32_disable_clock(sport); + + /* free all 3 interrupts for this UART */ + free_irq(sport->irq_fault, port); + free_irq(sport->irq_tx, port); + free_irq(sport->irq_rx, port); +} + +/* serial core request to change current uart setting */ +static void pic32_uart_set_termios(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) +{ + struct pic32_sport *sport = to_pic32_sport(port); + unsigned int baud; + unsigned int quot; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* disable uart and mask all interrupts while changing speed */ + pic32_uart_dsbl_and_mask(port); + + /* stop bit options */ + if (new->c_cflag & CSTOPB) + pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE), + PIC32_UART_MODE_STSEL); + else + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE), + PIC32_UART_MODE_STSEL); + + /* parity options */ + if (new->c_cflag & PARENB) { + if (new->c_cflag & PARODD) { + pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE), + PIC32_UART_MODE_PDSEL1); + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE), + PIC32_UART_MODE_PDSEL0); + } else { + pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE), + PIC32_UART_MODE_PDSEL0); + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE), + PIC32_UART_MODE_PDSEL1); + } + } else { + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE), + PIC32_UART_MODE_PDSEL1 | + PIC32_UART_MODE_PDSEL0); + } + /* if hw flow ctrl, then the pins must be specified in device tree */ + if ((new->c_cflag & CRTSCTS) && sport->hw_flow_ctrl) { + /* enable hardware flow control */ + pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE), + PIC32_UART_MODE_UEN1); + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE), + PIC32_UART_MODE_UEN0); + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE), + PIC32_UART_MODE_RTSMD); + } else { + /* disable hardware flow control */ + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE), + PIC32_UART_MODE_UEN1); + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE), + PIC32_UART_MODE_UEN0); + pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_MODE), + PIC32_UART_MODE_RTSMD); + } + + /* Always 8-bit */ + new->c_cflag |= CS8; + + /* Mark/Space parity is not supported */ + new->c_cflag &= ~CMSPAR; + + /* update baud */ + baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16); + quot = uart_get_divisor(port, baud) - 1; + pic32_uart_writel(sport, PIC32_UART_BRG, quot); + uart_update_timeout(port, new->c_cflag, baud); + + if (tty_termios_baud_rate(new)) + tty_termios_encode_baud_rate(new, baud, baud); + + /* enable uart */ + pic32_uart_en_and_unmask(port); + + spin_unlock_irqrestore(&port->lock, flags); +} + +/* serial core request to claim uart iomem */ +static int pic32_uart_request_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *res_mem; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res_mem)) + return -EINVAL; + + if (!request_mem_region(port->mapbase, resource_size(res_mem), + "pic32_uart_mem")) + return -EBUSY; + + port->membase = devm_ioremap_nocache(port->dev, port->mapbase, + resource_size(res_mem)); + if (!port->membase) { + dev_err(port->dev, "Unable to map registers\n"); + release_mem_region(port->mapbase, resource_size(res_mem)); + return -ENOMEM; + } + + return 0; +} + +/* serial core request to release uart iomem */ +static void pic32_uart_release_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *res_mem; + unsigned int res_size; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res_mem)) + return; + res_size = resource_size(res_mem); + + release_mem_region(port->mapbase, res_size); +} + +/* serial core request to do any port required auto-configuration */ +static void pic32_uart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + if (pic32_uart_request_port(port)) + return; + port->type = PORT_PIC32; + } +} + +/* serial core request to check that port information in serinfo are suitable */ +static int pic32_uart_verify_port(struct uart_port *port, + struct serial_struct *serinfo) +{ + if (port->type != PORT_PIC32) + return -EINVAL; + if (port->irq != serinfo->irq) + return -EINVAL; + if (port->iotype != serinfo->io_type) + return -EINVAL; + if (port->mapbase != (unsigned long)serinfo->iomem_base) + return -EINVAL; + + return 0; +} + +/* serial core callbacks */ +static const struct uart_ops pic32_uart_ops = { + .tx_empty = pic32_uart_tx_empty, + .get_mctrl = pic32_uart_get_mctrl, + .set_mctrl = pic32_uart_set_mctrl, + .start_tx = pic32_uart_start_tx, + .stop_tx = pic32_uart_stop_tx, + .stop_rx = pic32_uart_stop_rx, + .break_ctl = pic32_uart_break_ctl, + .startup = pic32_uart_startup, + .shutdown = pic32_uart_shutdown, + .set_termios = pic32_uart_set_termios, + .type = pic32_uart_type, + .release_port = pic32_uart_release_port, + .request_port = pic32_uart_request_port, + .config_port = pic32_uart_config_port, + .verify_port = pic32_uart_verify_port, +}; + +#ifdef CONFIG_SERIAL_PIC32_CONSOLE +/* output given char */ +static void pic32_console_putchar(struct uart_port *port, int ch) +{ + struct pic32_sport *sport = to_pic32_sport(port); + + if (!(pic32_uart_readl(sport, PIC32_UART_MODE) & PIC32_UART_MODE_ON)) + return; + + if (!(pic32_uart_readl(sport, PIC32_UART_STA) & PIC32_UART_STA_UTXEN)) + return; + + /* wait for tx empty */ + pic32_wait_deplete_txbuf(sport); + + pic32_uart_writel(sport, PIC32_UART_TX, ch & 0xff); +} + +/* console core request to output given string */ +static void pic32_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct pic32_sport *sport = pic32_sports[co->index]; + struct uart_port *port = pic32_get_port(sport); + + /* call uart helper to deal with \r\n */ + uart_console_write(port, s, count, pic32_console_putchar); +} + +/* console core request to setup given console, find matching uart + * port and setup it. + */ +static int pic32_console_setup(struct console *co, char *options) +{ + struct pic32_sport *sport; + struct uart_port *port = NULL; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret = 0; + + if (unlikely(co->index < 0 || co->index >= PIC32_MAX_UARTS)) + return -ENODEV; + + sport = pic32_sports[co->index]; + if (!sport) + return -ENODEV; + port = pic32_get_port(sport); + + ret = pic32_enable_clock(sport); + if (ret) + return ret; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver pic32_uart_driver; +static struct console pic32_console = { + .name = PIC32_SDEV_NAME, + .write = pic32_console_write, + .device = uart_console_device, + .setup = pic32_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &pic32_uart_driver, +}; +#define PIC32_SCONSOLE (&pic32_console) + +static int __init pic32_console_init(void) +{ + register_console(&pic32_console); + return 0; +} +console_initcall(pic32_console_init); + +static inline bool is_pic32_console_port(struct uart_port *port) +{ + return port->cons && port->cons->index == port->line; +} + +/* + * Late console initialization. + */ +static int __init pic32_late_console_init(void) +{ + if (!(pic32_console.flags & CON_ENABLED)) + register_console(&pic32_console); + + return 0; +} + +core_initcall(pic32_late_console_init); + +#else +#define PIC32_SCONSOLE NULL +#endif + +static struct uart_driver pic32_uart_driver = { + .owner = THIS_MODULE, + .driver_name = PIC32_DEV_NAME, + .dev_name = PIC32_SDEV_NAME, + .nr = PIC32_MAX_UARTS, + .cons = PIC32_SCONSOLE, +}; + +static int pic32_uart_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct pic32_sport *sport; + int uart_idx = 0; + struct resource *res_mem; + struct uart_port *port; + int ret; + + uart_idx = of_alias_get_id(np, "serial"); + if (uart_idx < 0 || uart_idx >= PIC32_MAX_UARTS) + return -EINVAL; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) + return -EINVAL; + + sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); + if (!sport) + return -ENOMEM; + + sport->idx = uart_idx; + sport->irq_fault = irq_of_parse_and_map(np, 0); + sport->irqflags_fault = IRQF_NO_THREAD; + sport->irq_rx = irq_of_parse_and_map(np, 1); + sport->irqflags_rx = IRQF_NO_THREAD; + sport->irq_tx = irq_of_parse_and_map(np, 2); + sport->irqflags_tx = IRQF_NO_THREAD; + sport->clk = devm_clk_get(&pdev->dev, NULL); + sport->cts_gpio = -EINVAL; + sport->dev = &pdev->dev; + + /* Hardware flow control: gpios + * !Note: Basically, CTS is needed for reading the status. + */ + sport->hw_flow_ctrl = false; + sport->cts_gpio = of_get_named_gpio(np, "cts-gpios", 0); + if (gpio_is_valid(sport->cts_gpio)) { + sport->hw_flow_ctrl = true; + + ret = devm_gpio_request(sport->dev, + sport->cts_gpio, "CTS"); + if (ret) { + dev_err(&pdev->dev, + "error requesting CTS GPIO\n"); + goto err; + } + + ret = gpio_direction_input(sport->cts_gpio); + if (ret) { + dev_err(&pdev->dev, "error setting CTS GPIO\n"); + goto err; + } + } + + pic32_sports[uart_idx] = sport; + port = &sport->port; + memset(port, 0, sizeof(*port)); + port->iotype = UPIO_MEM; + port->mapbase = res_mem->start; + port->ops = &pic32_uart_ops; + port->flags = UPF_BOOT_AUTOCONF; + port->dev = &pdev->dev; + port->fifosize = PIC32_UART_TX_FIFO_DEPTH; + port->uartclk = clk_get_rate(sport->clk); + port->line = uart_idx; + + ret = uart_add_one_port(&pic32_uart_driver, port); + if (ret) { + port->membase = NULL; + dev_err(port->dev, "%s: uart add port error!\n", __func__); + goto err; + } + +#ifdef CONFIG_SERIAL_PIC32_CONSOLE + if (is_pic32_console_port(port) && + (pic32_console.flags & CON_ENABLED)) { + /* The peripheral clock has been enabled by console_setup, + * so disable it till the port is used. + */ + pic32_disable_clock(sport); + } +#endif + + platform_set_drvdata(pdev, port); + + dev_info(&pdev->dev, "%s: uart(%d) driver initialized.\n", + __func__, uart_idx); + + return 0; +err: + /* automatic unroll of sport and gpios */ + return ret; +} + +static int pic32_uart_remove(struct platform_device *pdev) +{ + struct uart_port *port = platform_get_drvdata(pdev); + struct pic32_sport *sport = to_pic32_sport(port); + + uart_remove_one_port(&pic32_uart_driver, port); + pic32_disable_clock(sport); + platform_set_drvdata(pdev, NULL); + pic32_sports[sport->idx] = NULL; + + /* automatic unroll of sport and gpios */ + return 0; +} + +static const struct of_device_id pic32_serial_dt_ids[] = { + { .compatible = "microchip,pic32mzda-uart" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pic32_serial_dt_ids); + +static struct platform_driver pic32_uart_platform_driver = { + .probe = pic32_uart_probe, + .remove = pic32_uart_remove, + .driver = { + .name = PIC32_DEV_NAME, + .of_match_table = of_match_ptr(pic32_serial_dt_ids), + }, +}; + +static int __init pic32_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&pic32_uart_driver); + if (ret) { + pr_err("failed to register %s:%d\n", + pic32_uart_driver.driver_name, ret); + return ret; + } + + ret = platform_driver_register(&pic32_uart_platform_driver); + if (ret) { + pr_err("fail to register pic32 uart\n"); + uart_unregister_driver(&pic32_uart_driver); + } + + return ret; +} +arch_initcall(pic32_uart_init); + +static void __exit pic32_uart_exit(void) +{ +#ifdef CONFIG_SERIAL_PIC32_CONSOLE + unregister_console(&pic32_console); +#endif + platform_driver_unregister(&pic32_uart_platform_driver); + uart_unregister_driver(&pic32_uart_driver); +} +module_exit(pic32_uart_exit); + +MODULE_AUTHOR("Sorin-Andrei Pistirica "); +MODULE_DESCRIPTION("Microchip PIC32 integrated serial port driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/pic32_uart.h b/drivers/tty/serial/pic32_uart.h new file mode 100644 index 000000000..ec379da55 --- /dev/null +++ b/drivers/tty/serial/pic32_uart.h @@ -0,0 +1,126 @@ +/* + * PIC32 Integrated Serial Driver. + * + * Copyright (C) 2015 Microchip Technology, Inc. + * + * Authors: + * Sorin-Andrei Pistirica + * + * Licensed under GPLv2 or later. + */ +#ifndef __DT_PIC32_UART_H__ +#define __DT_PIC32_UART_H__ + +#define PIC32_UART_DFLT_BRATE (9600) +#define PIC32_UART_TX_FIFO_DEPTH (8) +#define PIC32_UART_RX_FIFO_DEPTH (8) + +#define PIC32_UART_MODE 0x00 +#define PIC32_UART_STA 0x10 +#define PIC32_UART_TX 0x20 +#define PIC32_UART_RX 0x30 +#define PIC32_UART_BRG 0x40 + +struct pic32_console_opt { + int baud; + int parity; + int bits; + int flow; +}; + +/* struct pic32_sport - pic32 serial port descriptor + * @port: uart port descriptor + * @idx: port index + * @irq_fault: virtual fault interrupt number + * @irqflags_fault: flags related to fault irq + * @irq_fault_name: irq fault name + * @irq_rx: virtual rx interrupt number + * @irqflags_rx: flags related to rx irq + * @irq_rx_name: irq rx name + * @irq_tx: virtual tx interrupt number + * @irqflags_tx: : flags related to tx irq + * @irq_tx_name: irq tx name + * @cts_gpio: clear to send gpio + * @dev: device descriptor + **/ +struct pic32_sport { + struct uart_port port; + struct pic32_console_opt opt; + int idx; + + int irq_fault; + int irqflags_fault; + const char *irq_fault_name; + int irq_rx; + int irqflags_rx; + const char *irq_rx_name; + int irq_tx; + int irqflags_tx; + const char *irq_tx_name; + u8 enable_tx_irq; + + bool hw_flow_ctrl; + int cts_gpio; + + int ref_clk; + struct clk *clk; + + struct device *dev; +}; +#define to_pic32_sport(c) container_of(c, struct pic32_sport, port) +#define pic32_get_port(sport) (&sport->port) +#define pic32_get_opt(sport) (&sport->opt) +#define tx_irq_enabled(sport) (sport->enable_tx_irq) + +static inline void pic32_uart_writel(struct pic32_sport *sport, + u32 reg, u32 val) +{ + struct uart_port *port = pic32_get_port(sport); + + __raw_writel(val, port->membase + reg); +} + +static inline u32 pic32_uart_readl(struct pic32_sport *sport, u32 reg) +{ + struct uart_port *port = pic32_get_port(sport); + + return __raw_readl(port->membase + reg); +} + +/* pic32 uart mode register bits */ +#define PIC32_UART_MODE_ON BIT(15) +#define PIC32_UART_MODE_FRZ BIT(14) +#define PIC32_UART_MODE_SIDL BIT(13) +#define PIC32_UART_MODE_IREN BIT(12) +#define PIC32_UART_MODE_RTSMD BIT(11) +#define PIC32_UART_MODE_RESV1 BIT(10) +#define PIC32_UART_MODE_UEN1 BIT(9) +#define PIC32_UART_MODE_UEN0 BIT(8) +#define PIC32_UART_MODE_WAKE BIT(7) +#define PIC32_UART_MODE_LPBK BIT(6) +#define PIC32_UART_MODE_ABAUD BIT(5) +#define PIC32_UART_MODE_RXINV BIT(4) +#define PIC32_UART_MODE_BRGH BIT(3) +#define PIC32_UART_MODE_PDSEL1 BIT(2) +#define PIC32_UART_MODE_PDSEL0 BIT(1) +#define PIC32_UART_MODE_STSEL BIT(0) + +/* pic32 uart status register bits */ +#define PIC32_UART_STA_UTXISEL1 BIT(15) +#define PIC32_UART_STA_UTXISEL0 BIT(14) +#define PIC32_UART_STA_UTXINV BIT(13) +#define PIC32_UART_STA_URXEN BIT(12) +#define PIC32_UART_STA_UTXBRK BIT(11) +#define PIC32_UART_STA_UTXEN BIT(10) +#define PIC32_UART_STA_UTXBF BIT(9) +#define PIC32_UART_STA_TRMT BIT(8) +#define PIC32_UART_STA_URXISEL1 BIT(7) +#define PIC32_UART_STA_URXISEL0 BIT(6) +#define PIC32_UART_STA_ADDEN BIT(5) +#define PIC32_UART_STA_RIDLE BIT(4) +#define PIC32_UART_STA_PERR BIT(3) +#define PIC32_UART_STA_FERR BIT(2) +#define PIC32_UART_STA_OERR BIT(1) +#define PIC32_UART_STA_URXDA BIT(0) + +#endif /* __DT_PIC32_UART_H__ */ diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 025a42644..f36e6df2f 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -666,7 +666,7 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) struct uart_port *port = &s->p[portno].port; do { - unsigned int iir, msr, rxlen; + unsigned int iir, rxlen; iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG); if (iir & SC16IS7XX_IIR_NO_INT_BIT) @@ -683,12 +683,6 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) if (rxlen) sc16is7xx_handle_rx(port, rxlen, iir); break; - - case SC16IS7XX_IIR_CTSRTS_SRC: - msr = sc16is7xx_port_read(port, SC16IS7XX_MSR_REG); - uart_handle_cts_change(port, - !!(msr & SC16IS7XX_MSR_DCTS_BIT)); - break; case SC16IS7XX_IIR_THRI_SRC: sc16is7xx_handle_tx(port); break; @@ -1014,9 +1008,8 @@ static int sc16is7xx_startup(struct uart_port *port) SC16IS7XX_EFCR_TXDISABLE_BIT, 0); - /* Enable RX, TX, CTS change interrupts */ - val = SC16IS7XX_IER_RDI_BIT | SC16IS7XX_IER_THRI_BIT | - SC16IS7XX_IER_CTSI_BIT; + /* Enable RX, TX interrupts */ + val = SC16IS7XX_IER_RDI_BIT | SC16IS7XX_IER_THRI_BIT; sc16is7xx_port_write(port, SC16IS7XX_IER_REG, val); return 0; @@ -1104,8 +1097,7 @@ static const struct uart_ops sc16is7xx_ops = { static int sc16is7xx_gpio_get(struct gpio_chip *chip, unsigned offset) { unsigned int val; - struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, - gpio); + struct sc16is7xx_port *s = gpiochip_get_data(chip); struct uart_port *port = &s->p[0].port; val = sc16is7xx_port_read(port, SC16IS7XX_IOSTATE_REG); @@ -1115,8 +1107,7 @@ static int sc16is7xx_gpio_get(struct gpio_chip *chip, unsigned offset) static void sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned offset, int val) { - struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, - gpio); + struct sc16is7xx_port *s = gpiochip_get_data(chip); struct uart_port *port = &s->p[0].port; sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset), @@ -1126,8 +1117,7 @@ static void sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned offset, int val) static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { - struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, - gpio); + struct sc16is7xx_port *s = gpiochip_get_data(chip); struct uart_port *port = &s->p[0].port; sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset), 0); @@ -1138,8 +1128,7 @@ static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip, static int sc16is7xx_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int val) { - struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, - gpio); + struct sc16is7xx_port *s = gpiochip_get_data(chip); struct uart_port *port = &s->p[0].port; sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset), @@ -1210,7 +1199,7 @@ static int sc16is7xx_probe(struct device *dev, s->gpio.base = -1; s->gpio.ngpio = devtype->nr_gpio; s->gpio.can_sleep = 1; - ret = gpiochip_add(&s->gpio); + ret = gpiochip_add_data(&s->gpio, s); if (ret) goto out_thread; } diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 1d6fc60ed..1dba6719d 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -206,10 +206,8 @@ static void set_dtr(struct tegra_uart_port *tup, bool active) static void tegra_uart_set_mctrl(struct uart_port *u, unsigned int mctrl) { struct tegra_uart_port *tup = to_tegra_uport(u); - unsigned long mcr; int dtr_enable; - mcr = tup->mcr_shadow; tup->rts_active = !!(mctrl & TIOCM_RTS); set_rts(tup, tup->rts_active); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index a126a603b..a333c59cb 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -64,6 +64,41 @@ static int uart_dcd_enabled(struct uart_port *uport) return !!(uport->status & UPSTAT_DCD_ENABLE); } +static inline struct uart_port *uart_port_ref(struct uart_state *state) +{ + if (atomic_add_unless(&state->refcount, 1, 0)) + return state->uart_port; + return NULL; +} + +static inline void uart_port_deref(struct uart_port *uport) +{ + if (uport && atomic_dec_and_test(&uport->state->refcount)) + wake_up(&uport->state->remove_wait); +} + +#define uart_port_lock(state, flags) \ + ({ \ + struct uart_port *__uport = uart_port_ref(state); \ + if (__uport) \ + spin_lock_irqsave(&__uport->lock, flags); \ + __uport; \ + }) + +#define uart_port_unlock(uport, flags) \ + ({ \ + struct uart_port *__uport = uport; \ + if (__uport) \ + spin_unlock_irqrestore(&__uport->lock, flags); \ + uart_port_deref(__uport); \ + }) + +static inline struct uart_port *uart_port_check(struct uart_state *state) +{ + lockdep_assert_held(&state->port.mutex); + return state->uart_port; +} + /* * This routine is used by the interrupt handler to schedule processing in * the software interrupt portion of the driver. @@ -82,12 +117,13 @@ void uart_write_wakeup(struct uart_port *port) static void uart_stop(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; + struct uart_port *port; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); - port->ops->stop_tx(port); - spin_unlock_irqrestore(&port->lock, flags); + port = uart_port_lock(state, flags); + if (port) + port->ops->stop_tx(port); + uart_port_unlock(port, flags); } static void __uart_start(struct tty_struct *tty) @@ -95,19 +131,19 @@ static void __uart_start(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct uart_port *port = state->uart_port; - if (!uart_tx_stopped(port)) + if (port && !uart_tx_stopped(port)) port->ops->start_tx(port); } static void uart_start(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; + struct uart_port *port; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + port = uart_port_lock(state, flags); __uart_start(tty); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock(port, flags); } static void @@ -134,7 +170,7 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, int init_hw) { - struct uart_port *uport = state->uart_port; + struct uart_port *uport = uart_port_check(state); unsigned long page; int retval = 0; @@ -196,7 +232,7 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, struct tty_port *port = &state->port; int retval; - if (port->flags & ASYNC_INITIALIZED) + if (tty_port_initialized(port)) return 0; /* @@ -207,7 +243,7 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, retval = uart_port_startup(tty, state, init_hw); if (!retval) { - set_bit(ASYNCB_INITIALIZED, &port->flags); + tty_port_set_initialized(port, 1); clear_bit(TTY_IO_ERROR, &tty->flags); } else if (retval > 0) retval = 0; @@ -219,10 +255,12 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. Calls to * uart_shutdown are serialised by the per-port semaphore. + * + * uport == NULL if uart_port has already been removed */ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) { - struct uart_port *uport = state->uart_port; + struct uart_port *uport = uart_port_check(state); struct tty_port *port = &state->port; /* @@ -231,11 +269,13 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) if (tty) set_bit(TTY_IO_ERROR, &tty->flags); - if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) { + if (tty_port_initialized(port)) { + tty_port_set_initialized(port, 0); + /* * Turn off DTR and RTS early. */ - if (uart_console(uport) && tty) + if (uport && uart_console(uport) && tty) uport->cons->cflag = tty->termios.c_cflag; if (!tty || C_HUPCL(tty)) @@ -249,7 +289,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) * a DCD drop (hangup) at just the right time. Clear suspended bit so * we don't try to resume a port that has been shutdown. */ - clear_bit(ASYNCB_SUSPENDED, &port->flags); + tty_port_set_suspended(port, 0); /* * Free the transmit buffer page. @@ -441,7 +481,7 @@ EXPORT_SYMBOL(uart_get_divisor); static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, struct ktermios *old_termios) { - struct uart_port *uport = state->uart_port; + struct uart_port *uport = uart_port_check(state); struct ktermios *termios; int hw_stopped; @@ -486,7 +526,7 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, static int uart_put_char(struct tty_struct *tty, unsigned char c) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; + struct uart_port *port; struct circ_buf *circ; unsigned long flags; int ret = 0; @@ -495,13 +535,13 @@ static int uart_put_char(struct tty_struct *tty, unsigned char c) if (!circ->buf) return 0; - spin_lock_irqsave(&port->lock, flags); - if (uart_circ_chars_free(circ) != 0) { + port = uart_port_lock(state, flags); + if (port && uart_circ_chars_free(circ) != 0) { circ->buf[circ->head] = c; circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); ret = 1; } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock(port, flags); return ret; } @@ -528,14 +568,12 @@ static int uart_write(struct tty_struct *tty, return -EL3HLT; } - port = state->uart_port; circ = &state->xmit; - if (!circ->buf) return 0; - spin_lock_irqsave(&port->lock, flags); - while (1) { + port = uart_port_lock(state, flags); + while (port) { c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); if (count < c) c = count; @@ -549,32 +587,33 @@ static int uart_write(struct tty_struct *tty, } __uart_start(tty); - spin_unlock_irqrestore(&port->lock, flags); - + uart_port_unlock(port, flags); return ret; } static int uart_write_room(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; + struct uart_port *port; unsigned long flags; int ret; - spin_lock_irqsave(&state->uart_port->lock, flags); + port = uart_port_lock(state, flags); ret = uart_circ_chars_free(&state->xmit); - spin_unlock_irqrestore(&state->uart_port->lock, flags); + uart_port_unlock(port, flags); return ret; } static int uart_chars_in_buffer(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; + struct uart_port *port; unsigned long flags; int ret; - spin_lock_irqsave(&state->uart_port->lock, flags); + port = uart_port_lock(state, flags); ret = uart_circ_chars_pending(&state->xmit); - spin_unlock_irqrestore(&state->uart_port->lock, flags); + uart_port_unlock(port, flags); return ret; } @@ -593,14 +632,15 @@ static void uart_flush_buffer(struct tty_struct *tty) return; } - port = state->uart_port; pr_debug("uart_flush_buffer(%d) called\n", tty->index); - spin_lock_irqsave(&port->lock, flags); + port = uart_port_lock(state, flags); + if (!port) + return; uart_circ_clear(&state->xmit); if (port->ops->flush_buffer) port->ops->flush_buffer(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock(port, flags); tty_wakeup(tty); } @@ -611,9 +651,13 @@ static void uart_flush_buffer(struct tty_struct *tty) static void uart_send_xchar(struct tty_struct *tty, char ch) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; + struct uart_port *port; unsigned long flags; + port = uart_port_ref(state); + if (!port) + return; + if (port->ops->send_xchar) port->ops->send_xchar(port, ch); else { @@ -623,14 +667,19 @@ static void uart_send_xchar(struct tty_struct *tty, char ch) port->ops->start_tx(port); spin_unlock_irqrestore(&port->lock, flags); } + uart_port_deref(port); } static void uart_throttle(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; + struct uart_port *port; upstat_t mask = 0; + port = uart_port_ref(state); + if (!port) + return; + if (I_IXOFF(tty)) mask |= UPSTAT_AUTOXOFF; if (C_CRTSCTS(tty)) @@ -646,14 +695,20 @@ static void uart_throttle(struct tty_struct *tty) if (mask & UPSTAT_AUTOXOFF) uart_send_xchar(tty, STOP_CHAR(tty)); + + uart_port_deref(port); } static void uart_unthrottle(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; + struct uart_port *port; upstat_t mask = 0; + port = uart_port_ref(state); + if (!port) + return; + if (I_IXOFF(tty)) mask |= UPSTAT_AUTOXOFF; if (C_CRTSCTS(tty)) @@ -669,12 +724,15 @@ static void uart_unthrottle(struct tty_struct *tty) if (mask & UPSTAT_AUTOXOFF) uart_send_xchar(tty, START_CHAR(tty)); + + uart_port_deref(port); } -static void uart_get_info(struct tty_port *port, struct serial_struct *retinfo) +static int uart_get_info(struct tty_port *port, struct serial_struct *retinfo) { struct uart_state *state = container_of(port, struct uart_state, port); - struct uart_port *uport = state->uart_port; + struct uart_port *uport; + int ret = -ENODEV; memset(retinfo, 0, sizeof(*retinfo)); @@ -683,6 +741,10 @@ static void uart_get_info(struct tty_port *port, struct serial_struct *retinfo) * occur as we go */ mutex_lock(&port->mutex); + uport = uart_port_check(state); + if (!uport) + goto out; + retinfo->type = uport->type; retinfo->line = uport->line; retinfo->port = uport->iobase; @@ -701,7 +763,11 @@ static void uart_get_info(struct tty_port *port, struct serial_struct *retinfo) retinfo->io_type = uport->iotype; retinfo->iomem_reg_shift = uport->regshift; retinfo->iomem_base = (void *)(unsigned long)uport->mapbase; + + ret = 0; +out: mutex_unlock(&port->mutex); + return ret; } static int uart_get_info_user(struct tty_port *port, @@ -709,7 +775,8 @@ static int uart_get_info_user(struct tty_port *port, { struct serial_struct tmp; - uart_get_info(port, &tmp); + if (uart_get_info(port, &tmp) < 0) + return -EIO; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; @@ -720,13 +787,16 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, struct uart_state *state, struct serial_struct *new_info) { - struct uart_port *uport = state->uart_port; + struct uart_port *uport = uart_port_check(state); unsigned long new_port; unsigned int change_irq, change_port, closing_wait; unsigned int old_custom_divisor, close_delay; upf_t old_flags, new_flags; int retval = 0; + if (!uport) + return -EIO; + new_port = new_info->port; if (HIGH_BITS_OFFSET) new_port += (unsigned long) new_info->port_high << HIGH_BITS_OFFSET; @@ -886,7 +956,7 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, retval = 0; if (uport->type == PORT_UNKNOWN) goto exit; - if (port->flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(port)) { if (((old_flags ^ uport->flags) & UPF_SPD_MASK) || old_custom_divisor != uport->custom_divisor) { /* @@ -936,13 +1006,11 @@ static int uart_set_info_user(struct tty_struct *tty, struct uart_state *state, * @tty: tty associated with the UART * @state: UART being queried * @value: returned modem value - * - * Note: uart_ioctl protects us against hangups. */ static int uart_get_lsr_info(struct tty_struct *tty, struct uart_state *state, unsigned int __user *value) { - struct uart_port *uport = state->uart_port; + struct uart_port *uport = uart_port_check(state); unsigned int result; result = uport->ops->tx_empty(uport); @@ -965,18 +1033,22 @@ static int uart_tiocmget(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; - struct uart_port *uport = state->uart_port; + struct uart_port *uport; int result = -EIO; mutex_lock(&port->mutex); - if (!(tty->flags & (1 << TTY_IO_ERROR))) { + uport = uart_port_check(state); + if (!uport) + goto out; + + if (!tty_io_error(tty)) { result = uport->mctrl; spin_lock_irq(&uport->lock); result |= uport->ops->get_mctrl(uport); spin_unlock_irq(&uport->lock); } +out: mutex_unlock(&port->mutex); - return result; } @@ -984,15 +1056,20 @@ static int uart_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct uart_state *state = tty->driver_data; - struct uart_port *uport = state->uart_port; struct tty_port *port = &state->port; + struct uart_port *uport; int ret = -EIO; mutex_lock(&port->mutex); - if (!(tty->flags & (1 << TTY_IO_ERROR))) { + uport = uart_port_check(state); + if (!uport) + goto out; + + if (!tty_io_error(tty)) { uart_update_mctrl(uport, set, clear); ret = 0; } +out: mutex_unlock(&port->mutex); return ret; } @@ -1001,21 +1078,26 @@ static int uart_break_ctl(struct tty_struct *tty, int break_state) { struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; - struct uart_port *uport = state->uart_port; + struct uart_port *uport; + int ret = -EIO; mutex_lock(&port->mutex); + uport = uart_port_check(state); + if (!uport) + goto out; if (uport->type != PORT_UNKNOWN) uport->ops->break_ctl(uport, break_state); - + ret = 0; +out: mutex_unlock(&port->mutex); - return 0; + return ret; } static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state) { - struct uart_port *uport = state->uart_port; struct tty_port *port = &state->port; + struct uart_port *uport; int flags, ret; if (!capable(CAP_SYS_ADMIN)) @@ -1029,6 +1111,12 @@ static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state) if (mutex_lock_interruptible(&port->mutex)) return -ERESTARTSYS; + uport = uart_port_check(state); + if (!uport) { + ret = -EIO; + goto out; + } + ret = -EBUSY; if (tty_port_users(port) == 1) { uart_shutdown(tty, state); @@ -1052,6 +1140,7 @@ static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state) ret = uart_startup(tty, state, 1); } +out: mutex_unlock(&port->mutex); return ret; } @@ -1074,10 +1163,9 @@ static void uart_enable_ms(struct uart_port *uport) * FIXME: This wants extracting into a common all driver implementation * of TIOCMWAIT using tty_port. */ -static int -uart_wait_modem_status(struct uart_state *state, unsigned long arg) +static int uart_wait_modem_status(struct uart_state *state, unsigned long arg) { - struct uart_port *uport = state->uart_port; + struct uart_port *uport; struct tty_port *port = &state->port; DECLARE_WAITQUEUE(wait, current); struct uart_icount cprev, cnow; @@ -1086,6 +1174,9 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg) /* * note the counters on entry */ + uport = uart_port_ref(state); + if (!uport) + return -EIO; spin_lock_irq(&uport->lock); memcpy(&cprev, &uport->icount, sizeof(struct uart_icount)); uart_enable_ms(uport); @@ -1119,6 +1210,7 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg) } __set_current_state(TASK_RUNNING); remove_wait_queue(&port->delta_msr_wait, &wait); + uart_port_deref(uport); return ret; } @@ -1134,11 +1226,15 @@ static int uart_get_icount(struct tty_struct *tty, { struct uart_state *state = tty->driver_data; struct uart_icount cnow; - struct uart_port *uport = state->uart_port; + struct uart_port *uport; + uport = uart_port_ref(state); + if (!uport) + return -EIO; spin_lock_irq(&uport->lock); memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); spin_unlock_irq(&uport->lock); + uart_port_deref(uport); icount->cts = cnow.cts; icount->dsr = cnow.dsr; @@ -1200,11 +1296,11 @@ static int uart_set_rs485_config(struct uart_port *port, * Called via sys_ioctl. We can use spin_lock_irq() here. */ static int -uart_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) +uart_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; + struct uart_port *uport; void __user *uarg = (void __user *)arg; int ret = -ENOIOCTLCMD; @@ -1238,7 +1334,7 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, if (ret != -ENOIOCTLCMD) goto out; - if (tty->flags & (1 << TTY_IO_ERROR)) { + if (tty_io_error(tty)) { ret = -EIO; goto out; } @@ -1256,8 +1352,9 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, goto out; mutex_lock(&port->mutex); + uport = uart_port_check(state); - if (tty->flags & (1 << TTY_IO_ERROR)) { + if (!uport || tty_io_error(tty)) { ret = -EIO; goto out_up; } @@ -1273,19 +1370,17 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, break; case TIOCGRS485: - ret = uart_get_rs485_config(state->uart_port, uarg); + ret = uart_get_rs485_config(uport, uarg); break; case TIOCSRS485: - ret = uart_set_rs485_config(state->uart_port, uarg); + ret = uart_set_rs485_config(uport, uarg); break; - default: { - struct uart_port *uport = state->uart_port; + default: if (uport->ops->ioctl) ret = uport->ops->ioctl(uport, cmd, arg); break; } - } out_up: mutex_unlock(&port->mutex); out: @@ -1295,24 +1390,29 @@ out: static void uart_set_ldisc(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *uport = state->uart_port; + struct uart_port *uport; - if (uport->ops->set_ldisc) { - mutex_lock(&state->port.mutex); + mutex_lock(&state->port.mutex); + uport = uart_port_check(state); + if (uport && uport->ops->set_ldisc) uport->ops->set_ldisc(uport, &tty->termios); - mutex_unlock(&state->port.mutex); - } + mutex_unlock(&state->port.mutex); } static void uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct uart_state *state = tty->driver_data; - struct uart_port *uport = state->uart_port; + struct uart_port *uport; unsigned int cflag = tty->termios.c_cflag; unsigned int iflag_mask = IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK; bool sw_changed = false; + mutex_lock(&state->port.mutex); + uport = uart_port_check(state); + if (!uport) + goto out; + /* * Drivers doing software flow control also need to know * about changes to these input settings. @@ -1335,12 +1435,10 @@ static void uart_set_termios(struct tty_struct *tty, tty->termios.c_ispeed == old_termios->c_ispeed && ((tty->termios.c_iflag ^ old_termios->c_iflag) & iflag_mask) == 0 && !sw_changed) { - return; + goto out; } - mutex_lock(&state->port.mutex); uart_change_speed(tty, state, old_termios); - mutex_unlock(&state->port.mutex); /* reload cflag from termios; port driver may have overriden flags */ cflag = tty->termios.c_cflag; @@ -1350,17 +1448,18 @@ static void uart_set_termios(struct tty_struct *tty, /* Handle transition away from B0 status */ else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { unsigned int mask = TIOCM_DTR; - if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) + if (!(cflag & CRTSCTS) || !tty_throttled(tty)) mask |= TIOCM_RTS; uart_set_mctrl(uport, mask); } +out: + mutex_unlock(&state->port.mutex); } /* * Calls to uart_close() are serialised via the tty_lock in * drivers/tty/tty_io.c:tty_release() * drivers/tty/tty_io.c:do_tty_hangup() - * This runs from a workqueue and can sleep for a _short_ time only. */ static void uart_close(struct tty_struct *tty, struct file *filp) { @@ -1379,18 +1478,21 @@ static void uart_close(struct tty_struct *tty, struct file *filp) return; } - uport = state->uart_port; port = &state->port; pr_debug("uart_close(%d) called\n", tty->index); - if (!port->count || tty_port_close_start(port, tty, filp) == 0) + if (tty_port_close_start(port, tty, filp) == 0) return; + mutex_lock(&port->mutex); + uport = uart_port_check(state); + /* * At this point, we stop accepting input. To do this, we * disable the receive line status interrupts. */ - if (port->flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(port) && + !WARN(!uport, "detached port still initialized!\n")) { spin_lock_irq(&uport->lock); uport->ops->stop_rx(uport); spin_unlock_irq(&uport->lock); @@ -1402,7 +1504,6 @@ static void uart_close(struct tty_struct *tty, struct file *filp) uart_wait_until_sent(tty, uport->timeout); } - mutex_lock(&port->mutex); uart_shutdown(tty, state); tty_port_tty_set(port, NULL); @@ -1413,17 +1514,17 @@ static void uart_close(struct tty_struct *tty, struct file *filp) if (port->close_delay) msleep_interruptible(jiffies_to_msecs(port->close_delay)); spin_lock_irq(&port->lock); - } else if (!uart_console(uport)) { + } else if (uport && !uart_console(uport)) { spin_unlock_irq(&port->lock); uart_change_pm(state, UART_PM_STATE_OFF); spin_lock_irq(&port->lock); } + spin_unlock_irq(&port->lock); + tty_port_set_active(port, 0); /* * Wake up anyone trying to open this port. */ - clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); - spin_unlock_irq(&port->lock); wake_up_interruptible(&port->open_wait); mutex_unlock(&port->mutex); @@ -1435,11 +1536,14 @@ static void uart_close(struct tty_struct *tty, struct file *filp) static void uart_wait_until_sent(struct tty_struct *tty, int timeout) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; + struct uart_port *port; unsigned long char_time, expire; - if (port->type == PORT_UNKNOWN || port->fifosize == 0) + port = uart_port_ref(state); + if (!port || port->type == PORT_UNKNOWN || port->fifosize == 0) { + uart_port_deref(port); return; + } /* * Set the check interval to be 1/5 of the estimated time to @@ -1485,6 +1589,7 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) if (time_after(jiffies, expire)) break; } + uart_port_deref(port); } /* @@ -1496,20 +1601,24 @@ static void uart_hangup(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; + struct uart_port *uport; unsigned long flags; pr_debug("uart_hangup(%d)\n", tty->index); mutex_lock(&port->mutex); - if (port->flags & ASYNC_NORMAL_ACTIVE) { + uport = uart_port_check(state); + WARN(!uport, "hangup of detached port!\n"); + + if (tty_port_active(port)) { uart_flush_buffer(tty); uart_shutdown(tty, state); spin_lock_irqsave(&port->lock, flags); port->count = 0; - clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); spin_unlock_irqrestore(&port->lock, flags); + tty_port_set_active(port, 0); tty_port_tty_set(port, NULL); - if (!uart_console(state->uart_port)) + if (uport && !uart_console(uport)) uart_change_pm(state, UART_PM_STATE_OFF); wake_up_interruptible(&port->open_wait); wake_up_interruptible(&port->delta_msr_wait); @@ -1517,10 +1626,11 @@ static void uart_hangup(struct tty_struct *tty) mutex_unlock(&port->mutex); } +/* uport == NULL if uart_port has already been removed */ static void uart_port_shutdown(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); - struct uart_port *uport = state->uart_port; + struct uart_port *uport = uart_port_check(state); /* * clear delta_msr_wait queue to avoid mem leaks: we may free @@ -1534,23 +1644,36 @@ static void uart_port_shutdown(struct tty_port *port) /* * Free the IRQ and disable the port. */ - uport->ops->shutdown(uport); + if (uport) + uport->ops->shutdown(uport); /* * Ensure that the IRQ handler isn't running on another CPU. */ - synchronize_irq(uport->irq); + if (uport) + synchronize_irq(uport->irq); } static int uart_carrier_raised(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); - struct uart_port *uport = state->uart_port; + struct uart_port *uport; int mctrl; + + uport = uart_port_ref(state); + /* + * Should never observe uport == NULL since checks for hangup should + * abort the tty_port_block_til_ready() loop before checking for carrier + * raised -- but report carrier raised if it does anyway so open will + * continue and not sleep + */ + if (WARN_ON(!uport)) + return 1; spin_lock_irq(&uport->lock); uart_enable_ms(uport); mctrl = uport->ops->get_mctrl(uport); spin_unlock_irq(&uport->lock); + uart_port_deref(uport); if (mctrl & TIOCM_CAR) return 1; return 0; @@ -1559,12 +1682,18 @@ static int uart_carrier_raised(struct tty_port *port) static void uart_dtr_rts(struct tty_port *port, int onoff) { struct uart_state *state = container_of(port, struct uart_state, port); - struct uart_port *uport = state->uart_port; + struct uart_port *uport; + + uport = uart_port_ref(state); + if (!uport) + return; if (onoff) uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS); else uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); + + uart_port_deref(uport); } /* @@ -1583,6 +1712,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp) int retval, line = tty->index; struct uart_state *state = drv->state + line; struct tty_port *port = &state->port; + struct uart_port *uport; pr_debug("uart_open(%d) called\n", line); @@ -1602,15 +1732,15 @@ static int uart_open(struct tty_struct *tty, struct file *filp) goto end; } - if (!state->uart_port || state->uart_port->flags & UPF_DEAD) { + uport = uart_port_check(state); + if (!uport || uport->flags & UPF_DEAD) { retval = -ENXIO; goto err_unlock; } tty->driver_data = state; - state->uart_port->state = state; - state->port.low_latency = - (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0; + uport->state = state; + port->low_latency = (uport->flags & UPF_LOW_LATENCY) ? 1 : 0; tty_port_tty_set(port, tty); /* @@ -1649,13 +1779,15 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) struct uart_state *state = drv->state + i; struct tty_port *port = &state->port; enum uart_pm_state pm_state; - struct uart_port *uport = state->uart_port; + struct uart_port *uport; char stat_buf[32]; unsigned int status; int mmio; + mutex_lock(&port->mutex); + uport = uart_port_check(state); if (!uport) - return; + goto out; mmio = uport->iotype >= UPIO_MEM; seq_printf(m, "%d: uart:%s %s%08llX irq:%d", @@ -1667,11 +1799,10 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) if (uport->type == PORT_UNKNOWN) { seq_putc(m, '\n'); - return; + goto out; } if (capable(CAP_SYS_ADMIN)) { - mutex_lock(&port->mutex); pm_state = state->pm_state; if (pm_state != UART_PM_STATE_ON) uart_change_pm(state, UART_PM_STATE_ON); @@ -1680,7 +1811,6 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) spin_unlock_irq(&uport->lock); if (pm_state != UART_PM_STATE_ON) uart_change_pm(state, pm_state); - mutex_unlock(&port->mutex); seq_printf(m, " tx:%d rx:%d", uport->icount.tx, uport->icount.rx); @@ -1718,6 +1848,8 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) seq_putc(m, '\n'); #undef STATBIT #undef INFOBIT +out: + mutex_unlock(&port->mutex); } static int uart_proc_show(struct seq_file *m, void *v) @@ -1954,10 +2086,10 @@ EXPORT_SYMBOL_GPL(uart_set_options); static void uart_change_pm(struct uart_state *state, enum uart_pm_state pm_state) { - struct uart_port *port = state->uart_port; + struct uart_port *port = uart_port_check(state); if (state->pm_state != pm_state) { - if (port->ops->pm) + if (port && port->ops->pm) port->ops->pm(port, pm_state, state->pm_state); state->pm_state = pm_state; } @@ -2003,12 +2135,12 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) uport->suspended = 1; - if (port->flags & ASYNC_INITIALIZED) { + if (tty_port_initialized(port)) { const struct uart_ops *ops = uport->ops; int tries; - set_bit(ASYNCB_SUSPENDED, &port->flags); - clear_bit(ASYNCB_INITIALIZED, &port->flags); + tty_port_set_suspended(port, 1); + tty_port_set_initialized(port, 0); spin_lock_irq(&uport->lock); ops->stop_tx(uport); @@ -2088,7 +2220,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) console_start(uport->cons); } - if (port->flags & ASYNC_SUSPENDED) { + if (tty_port_suspended(port)) { const struct uart_ops *ops = uport->ops; int ret; @@ -2107,7 +2239,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) ops->set_mctrl(uport, uport->mctrl); ops->start_tx(uport); spin_unlock_irq(&uport->lock); - set_bit(ASYNCB_INITIALIZED, &port->flags); + tty_port_set_initialized(port, 1); } else { /* * Failed to resume - maybe hardware went away? @@ -2118,7 +2250,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) } } - clear_bit(ASYNCB_SUSPENDED, &port->flags); + tty_port_set_suspended(port, 0); } mutex_unlock(&port->mutex); @@ -2228,42 +2360,42 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options) { struct uart_driver *drv = driver->driver_state; struct uart_state *state = drv->state + line; + struct tty_port *tport; struct uart_port *port; int baud = 9600; int bits = 8; int parity = 'n'; int flow = 'n'; - int ret; + int ret = 0; - if (!state || !state->uart_port) + if (!state) return -1; - port = state->uart_port; - if (!(port->ops->poll_get_char && port->ops->poll_put_char)) - return -1; + tport = &state->port; + mutex_lock(&tport->mutex); - if (port->ops->poll_init) { - struct tty_port *tport = &state->port; + port = uart_port_check(state); + if (!port || !(port->ops->poll_get_char && port->ops->poll_put_char)) { + ret = -1; + goto out; + } - ret = 0; - mutex_lock(&tport->mutex); + if (port->ops->poll_init) { /* - * We don't set ASYNCB_INITIALIZED as we only initialized the - * hw, e.g. state->xmit is still uninitialized. + * We don't set initialized as we only initialized the hw, + * e.g. state->xmit is still uninitialized. */ - if (!test_bit(ASYNCB_INITIALIZED, &tport->flags)) + if (!tty_port_initialized(tport)) ret = port->ops->poll_init(port); - mutex_unlock(&tport->mutex); - if (ret) - return ret; } - if (options) { + if (!ret && options) { uart_parse_options(options, &baud, &parity, &bits, &flow); - return uart_set_options(port, NULL, baud, parity, bits, flow); + ret = uart_set_options(port, NULL, baud, parity, bits, flow); } - - return 0; +out: + mutex_unlock(&tport->mutex); + return ret; } static int uart_poll_get_char(struct tty_driver *driver, int line) @@ -2271,12 +2403,15 @@ static int uart_poll_get_char(struct tty_driver *driver, int line) struct uart_driver *drv = driver->driver_state; struct uart_state *state = drv->state + line; struct uart_port *port; + int ret = -1; - if (!state || !state->uart_port) - return -1; - - port = state->uart_port; - return port->ops->poll_get_char(port); + if (state) { + port = uart_port_ref(state); + if (port) + ret = port->ops->poll_get_char(port); + uart_port_deref(port); + } + return ret; } static void uart_poll_put_char(struct tty_driver *driver, int line, char ch) @@ -2285,14 +2420,17 @@ static void uart_poll_put_char(struct tty_driver *driver, int line, char ch) struct uart_state *state = drv->state + line; struct uart_port *port; - if (!state || !state->uart_port) + if (!state) return; - port = state->uart_port; + port = uart_port_ref(state); + if (!port) + return; if (ch == '\n') port->ops->poll_put_char(port, '\r'); port->ops->poll_put_char(port, ch); + uart_port_deref(port); } #endif @@ -2639,6 +2777,8 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) } /* Link the port to the driver state table and vice versa */ + atomic_set(&state->refcount, 1); + init_waitqueue_head(&state->remove_wait); state->uart_port = uport; uport->state = state; @@ -2711,15 +2851,12 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) { struct uart_state *state = drv->state + uport->line; struct tty_port *port = &state->port; + struct uart_port *uart_port; struct tty_struct *tty; int ret = 0; BUG_ON(in_interrupt()); - if (state->uart_port != uport) - dev_alert(uport->dev, "Removing wrong port: %p != %p\n", - state->uart_port, uport); - mutex_lock(&port_mutex); /* @@ -2727,7 +2864,12 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) * succeeding while we shut down the port. */ mutex_lock(&port->mutex); - if (!state->uart_port) { + uart_port = uart_port_check(state); + if (uart_port != uport) + dev_alert(uport->dev, "Removing wrong port: %p != %p\n", + uart_port, uport); + + if (!uart_port) { mutex_unlock(&port->mutex); ret = -EINVAL; goto out; @@ -2764,7 +2906,11 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) */ uport->type = PORT_UNKNOWN; + mutex_lock(&port->mutex); + WARN_ON(atomic_dec_return(&state->refcount) < 0); + wait_event(state->remove_wait, !atomic_read(&state->refcount)); state->uart_port = NULL; + mutex_unlock(&port->mutex); out: mutex_unlock(&port_mutex); diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index 02147361e..e8dd5097d 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -43,8 +43,6 @@ static const struct { { "rng", TIOCM_RNG, false, }, { "rts", TIOCM_RTS, true, }, { "dtr", TIOCM_DTR, true, }, - { "out1", TIOCM_OUT1, true, }, - { "out2", TIOCM_OUT2, true, }, }; void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) @@ -125,9 +123,12 @@ static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context) struct uart_port *port = gpios->port; u32 mctrl = gpios->mctrl_prev; u32 mctrl_diff; + unsigned long flags; mctrl_gpio_get(gpios, &mctrl); + spin_lock_irqsave(&port->lock, flags); + mctrl_diff = mctrl ^ gpios->mctrl_prev; gpios->mctrl_prev = mctrl; @@ -147,6 +148,8 @@ static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context) wake_up_interruptible(&port->state->port.delta_msr_wait); } + spin_unlock_irqrestore(&port->lock, flags); + return IRQ_HANDLED; } diff --git a/drivers/tty/serial/serial_mctrl_gpio.h b/drivers/tty/serial/serial_mctrl_gpio.h index 9716db283..332a33ab0 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.h +++ b/drivers/tty/serial/serial_mctrl_gpio.h @@ -32,8 +32,6 @@ enum mctrl_gpio_idx { UART_GPIO_RI = UART_GPIO_RNG, UART_GPIO_RTS, UART_GPIO_DTR, - UART_GPIO_OUT1, - UART_GPIO_OUT2, UART_GPIO_MAX, }; @@ -62,7 +60,7 @@ struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, enum mctrl_gpio_idx gidx); /* - * Request and set direction of modem control lines GPIOs and sets up irq + * Request and set direction of modem control line GPIOs and set up irq * handling. * devm_* functions are used, so there's no need to call mctrl_gpio_free(). * Returns a pointer to the allocated mctrl structure if ok, -ENOMEM on @@ -71,7 +69,7 @@ struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx); /* - * Request and set direction of modem control lines GPIOs. + * Request and set direction of modem control line GPIOs. * devm_* functions are used, so there's no need to call mctrl_gpio_free(). * Returns a pointer to the allocated mctrl structure if ok, -ENOMEM on * allocation error. diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index c6657de78..b186c9c4f 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -1264,6 +1264,7 @@ MODULE_DEVICE_TABLE(of, sirfsoc_uart_ids); static int sirfsoc_uart_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct sirfsoc_uart_port *sirfport; struct uart_port *port; struct resource *res; @@ -1276,13 +1277,13 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) }; const struct of_device_id *match; - match = of_match_node(sirfsoc_uart_ids, pdev->dev.of_node); + match = of_match_node(sirfsoc_uart_ids, np); sirfport = devm_kzalloc(&pdev->dev, sizeof(*sirfport), GFP_KERNEL); if (!sirfport) { ret = -ENOMEM; goto err; } - sirfport->port.line = of_alias_get_id(pdev->dev.of_node, "serial"); + sirfport->port.line = of_alias_get_id(np, "serial"); sirf_ports[sirfport->port.line] = sirfport; sirfport->port.iotype = UPIO_MEM; sirfport->port.flags = UPF_BOOT_AUTOCONF; @@ -1291,25 +1292,25 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) port->private_data = sirfport; sirfport->uart_reg = (struct sirfsoc_uart_register *)match->data; - sirfport->hw_flow_ctrl = of_property_read_bool(pdev->dev.of_node, - "sirf,uart-has-rtscts"); - if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-uart") || - of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-uart")) + sirfport->hw_flow_ctrl = + of_property_read_bool(np, "uart-has-rtscts") || + of_property_read_bool(np, "sirf,uart-has-rtscts") /* deprecated */; + if (of_device_is_compatible(np, "sirf,prima2-uart") || + of_device_is_compatible(np, "sirf,atlas7-uart")) sirfport->uart_reg->uart_type = SIRF_REAL_UART; - if (of_device_is_compatible(pdev->dev.of_node, - "sirf,prima2-usp-uart") || of_device_is_compatible( - pdev->dev.of_node, "sirf,atlas7-usp-uart")) { + if (of_device_is_compatible(np, "sirf,prima2-usp-uart") || + of_device_is_compatible(np, "sirf,atlas7-usp-uart")) { sirfport->uart_reg->uart_type = SIRF_USP_UART; if (!sirfport->hw_flow_ctrl) goto usp_no_flow_control; - if (of_find_property(pdev->dev.of_node, "cts-gpios", NULL)) - sirfport->cts_gpio = of_get_named_gpio( - pdev->dev.of_node, "cts-gpios", 0); + if (of_find_property(np, "cts-gpios", NULL)) + sirfport->cts_gpio = + of_get_named_gpio(np, "cts-gpios", 0); else sirfport->cts_gpio = -1; - if (of_find_property(pdev->dev.of_node, "rts-gpios", NULL)) - sirfport->rts_gpio = of_get_named_gpio( - pdev->dev.of_node, "rts-gpios", 0); + if (of_find_property(np, "rts-gpios", NULL)) + sirfport->rts_gpio = + of_get_named_gpio(np, "rts-gpios", 0); else sirfport->rts_gpio = -1; @@ -1336,13 +1337,11 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) gpio_direction_output(sirfport->rts_gpio, 1); } usp_no_flow_control: - if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-uart") || - of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-usp-uart")) + if (of_device_is_compatible(np, "sirf,atlas7-uart") || + of_device_is_compatible(np, "sirf,atlas7-usp-uart")) sirfport->is_atlas7 = true; - if (of_property_read_u32(pdev->dev.of_node, - "fifosize", - &port->fifosize)) { + if (of_property_read_u32(np, "fifosize", &port->fifosize)) { dev_err(&pdev->dev, "Unable to find fifosize in uart node.\n"); ret = -EFAULT; diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 18971063f..699447aa8 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -654,7 +654,7 @@ static int sprd_probe_dt_alias(int index, struct device *dev) return ret; ret = of_alias_get_id(np, "serial"); - if (IS_ERR_VALUE(ret)) + if (ret < 0) ret = index; else if (ret >= ARRAY_SIZE(sprd_port) || sprd_port[ret] != NULL) { dev_warn(dev, "requested serial port %d not available.\n", ret); diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index d08baa668..05089b6c2 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -72,7 +72,7 @@ static void uartlite_outbe32(u32 val, void __iomem *addr) iowrite32be(val, addr); } -static struct uartlite_reg_ops uartlite_be = { +static const struct uartlite_reg_ops uartlite_be = { .in = uartlite_inbe32, .out = uartlite_outbe32, }; @@ -87,21 +87,21 @@ static void uartlite_outle32(u32 val, void __iomem *addr) iowrite32(val, addr); } -static struct uartlite_reg_ops uartlite_le = { +static const struct uartlite_reg_ops uartlite_le = { .in = uartlite_inle32, .out = uartlite_outle32, }; static inline u32 uart_in32(u32 offset, struct uart_port *port) { - struct uartlite_reg_ops *reg_ops = port->private_data; + const struct uartlite_reg_ops *reg_ops = port->private_data; return reg_ops->in(port->membase + offset); } static inline void uart_out32(u32 val, u32 offset, struct uart_port *port) { - struct uartlite_reg_ops *reg_ops = port->private_data; + const struct uartlite_reg_ops *reg_ops = port->private_data; reg_ops->out(val, port->membase + offset); } @@ -345,13 +345,13 @@ static int ulite_request_port(struct uart_port *port) return -EBUSY; } - port->private_data = &uartlite_be; + port->private_data = (void *)&uartlite_be; ret = uart_in32(ULITE_CONTROL, port); uart_out32(ULITE_CONTROL_RST_TX, ULITE_CONTROL, port); ret = uart_in32(ULITE_STATUS, port); /* Endianess detection */ if ((ret & ULITE_STATUS_TXEMPTY) != ULITE_STATUS_TXEMPTY) - port->private_data = &uartlite_le; + port->private_data = (void *)&uartlite_le; return 0; } -- cgit v1.2.3