diff options
| -rw-r--r-- | arch/mips/dec/platform.c | 109 | ||||
| -rw-r--r-- | drivers/tty/serial/8250/8250_dw.c | 2 | ||||
| -rw-r--r-- | drivers/tty/serial/8250/8250_port.c | 7 | ||||
| -rw-r--r-- | drivers/tty/serial/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/tty/serial/altera_jtaguart.c | 7 | ||||
| -rw-r--r-- | drivers/tty/serial/dz.c | 171 | ||||
| -rw-r--r-- | drivers/tty/serial/fsl_lpuart.c | 15 | ||||
| -rw-r--r-- | drivers/tty/serial/pch_uart.c | 19 | ||||
| -rw-r--r-- | drivers/tty/serial/qcom_geni_serial.c | 16 | ||||
| -rw-r--r-- | drivers/tty/serial/samsung_tty.c | 8 | ||||
| -rw-r--r-- | drivers/tty/serial/sh-sci.c | 2 | ||||
| -rw-r--r-- | drivers/tty/serial/zs.c | 218 | ||||
| -rw-r--r-- | drivers/tty/serial/zs.h | 1 | ||||
| -rw-r--r-- | include/linux/serial_core.h | 12 | ||||
| -rw-r--r-- | include/linux/tty_port.h | 2 |
15 files changed, 341 insertions, 250 deletions
diff --git a/arch/mips/dec/platform.c b/arch/mips/dec/platform.c index c4fcb8c58e01c..723ce16cbfc0c 100644 --- a/arch/mips/dec/platform.c +++ b/arch/mips/dec/platform.c @@ -10,6 +10,14 @@ #include <linux/mc146818rtc.h> #include <linux/platform_device.h> +#include <asm/bootinfo.h> + +#include <asm/dec/interrupts.h> +#include <asm/dec/ioasic_addrs.h> +#include <asm/dec/kn01.h> +#include <asm/dec/kn02.h> +#include <asm/dec/system.h> + static struct resource dec_rtc_resources[] = { { .name = "rtc", @@ -30,11 +38,110 @@ static struct platform_device dec_rtc_device = { .num_resources = ARRAY_SIZE(dec_rtc_resources), }; +static struct resource dec_dz_resources[] = { + { .name = "dz", .flags = IORESOURCE_MEM, }, + { .name = "dz", .flags = IORESOURCE_IRQ, }, +}; + +static struct platform_device dec_dz_device = { + .name = "dz", + .id = PLATFORM_DEVID_NONE, + .resource = dec_dz_resources, + .num_resources = ARRAY_SIZE(dec_dz_resources), +}; + +static struct platform_device *dec_dz_devices[] __initdata = { + &dec_dz_device, +}; + +static struct resource dec_zs_resources[][2] = { + { + { .name = "scc0", .flags = IORESOURCE_MEM, }, + { .name = "scc0", .flags = IORESOURCE_IRQ, }, + }, + { + { .name = "scc1", .flags = IORESOURCE_MEM, }, + { .name = "scc1", .flags = IORESOURCE_IRQ, }, + }, +}; + +static struct platform_device dec_zs_device[] = { + { + .name = "zs", + .id = 0, + .resource = dec_zs_resources[0], + .num_resources = ARRAY_SIZE(dec_zs_resources[0]), + }, + { + .name = "zs", + .id = 1, + .resource = dec_zs_resources[1], + .num_resources = ARRAY_SIZE(dec_zs_resources[1]), + }, +}; + static int __init dec_add_devices(void) { + struct platform_device *dec_zs_devices[ARRAY_SIZE(dec_zs_device)]; + int ret1, ret2, ret3; + int num_dz, num_zs; + int irq, i; + dec_rtc_resources[0].start = RTC_PORT(0); dec_rtc_resources[0].end = RTC_PORT(0) + dec_kn_slot_size - 1; - return platform_device_register(&dec_rtc_device); + + i = 0; + irq = dec_interrupt[DEC_IRQ_DZ11]; + if (IS_ENABLED(CONFIG_32BIT) && irq >= 0) { + resource_size_t base; + + switch (mips_machtype) { + case MACH_DS23100: + case MACH_DS5100: + base = dec_kn_slot_base + KN01_DZ11; + break; + default: + base = dec_kn_slot_base + KN02_DZ11; + break; + } + dec_dz_device.resource[0].start = base; + dec_dz_device.resource[0].end = base + dec_kn_slot_size - 1; + dec_dz_device.resource[1].start = irq; + dec_dz_device.resource[1].end = irq; + i++; + } + num_dz = i; + + i = 0; + irq = dec_interrupt[DEC_IRQ_SCC0]; + if (irq >= 0) { + resource_size_t base = dec_kn_slot_base + IOASIC_SCC0; + + dec_zs_device[i].resource[0].start = base; + dec_zs_device[i].resource[0].end = base + dec_kn_slot_size - 1; + dec_zs_device[i].resource[1].start = irq; + dec_zs_device[i].resource[1].end = irq; + dec_zs_devices[i] = &dec_zs_device[i]; + i++; + } + irq = dec_interrupt[DEC_IRQ_SCC1]; + if (irq >= 0) { + resource_size_t base = dec_kn_slot_base + IOASIC_SCC1; + + dec_zs_device[i].resource[0].start = base; + dec_zs_device[i].resource[0].end = base + dec_kn_slot_size - 1; + dec_zs_device[i].resource[1].start = irq; + dec_zs_device[i].resource[1].end = irq; + dec_zs_devices[i] = &dec_zs_device[i]; + i++; + } + num_zs = i; + + ret1 = platform_device_register(&dec_rtc_device); + ret2 = IS_ENABLED(CONFIG_32BIT) ? + platform_add_devices(dec_dz_devices, num_dz) : 0; + ret3 = platform_add_devices(dec_zs_devices, num_zs); + return ret1 ? ret1 : ret2 ? ret2 : ret3; } device_initcall(dec_add_devices); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 94beadb4024df..2af0c4d0ad823 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -427,7 +427,7 @@ static int dw8250_handle_irq(struct uart_port *p) unsigned int quirks = d->pdata->quirks; unsigned int status; - guard(uart_port_lock_irqsave)(p); + guard(uart_port_lock_check_sysrq_irqsave)(p); switch (FIELD_GET(DW_UART_IIR_IID, iir)) { case UART_IIR_NO_INT: diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index af78cc02f38e7..c66ba714caa5f 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1784,7 +1784,10 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) } /* - * Context: port's lock must be held by the caller. + * Context: port's lock must be held by the caller. The caller must + * release it via guard(uart_port_lock_check_sysrq_irqsave) or + * uart_unlock_and_check_sysrq_irqrestore(), which captures SysRq + * character on unlock. */ void serial8250_handle_irq_locked(struct uart_port *port, unsigned int iir) { @@ -1837,7 +1840,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) if (iir & UART_IIR_NO_INT) return 0; - guard(uart_port_lock_irqsave)(port); + guard(uart_port_lock_check_sysrq_irqsave)(port); serial8250_handle_irq_locked(port, iir); return 1; diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 9aa61c93d7bc6..ec284aceb9093 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -334,7 +334,7 @@ config SERIAL_MAX310X Say Y here if you want to support this ICs. config SERIAL_DZ - bool "DECstation DZ serial driver" + tristate "DECstation DZ serial driver" depends on MACH_DECSTATION && 32BIT select SERIAL_CORE default y diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index d47a62d1c9f7d..20f079fe11d82 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -379,6 +379,7 @@ static int altera_jtaguart_probe(struct platform_device *pdev) struct resource *res_mem; int i = pdev->id; int irq; + int ret; /* -1 emphasizes that the platform must have one port, no .N suffix */ if (i == -1) @@ -418,7 +419,11 @@ static int altera_jtaguart_probe(struct platform_device *pdev) port->flags = UPF_BOOT_AUTOCONF; port->dev = &pdev->dev; - uart_add_one_port(&altera_jtaguart_driver, port); + ret = uart_add_one_port(&altera_jtaguart_driver, port); + if (ret) { + iounmap(port->membase); + return ret; + } return 0; } diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index e53c54353c3e4..39d93e9c2d157 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -40,6 +40,7 @@ #include <linux/kernel.h> #include <linux/major.h> #include <linux/module.h> +#include <linux/platform_device.h> #include <linux/serial.h> #include <linux/serial_core.h> #include <linux/sysrq.h> @@ -48,14 +49,6 @@ #include <linux/atomic.h> #include <linux/io.h> -#include <asm/bootinfo.h> - -#include <asm/dec/interrupts.h> -#include <asm/dec/kn01.h> -#include <asm/dec/kn02.h> -#include <asm/dec/machtype.h> -#include <asm/dec/prom.h> -#include <asm/dec/system.h> #include "dz.h" @@ -65,7 +58,9 @@ MODULE_LICENSE("GPL"); static char dz_name[] __initdata = "DECstation DZ serial driver version "; -static char dz_version[] __initdata = "1.04"; +static char dz_version[] __initdata = "1.05"; + +#define DZ_IO_SIZE 0x20 /* IOMEM space size. */ struct dz_port { struct dz_mux *mux; @@ -81,6 +76,7 @@ struct dz_mux { }; static struct dz_mux dz_mux; +static struct uart_driver dz_reg; static inline struct dz_port *to_dport(struct uart_port *uport) { @@ -542,14 +538,47 @@ static int dz_encode_baud_rate(unsigned int baud) static void dz_reset(struct dz_port *dport) { struct dz_mux *mux = dport->mux; + unsigned short tcr; + int loops = 10000; if (mux->initialised) return; + tcr = dz_in(dport, DZ_TCR); + + /* Do not disturb any ongoing transmissions. */ + if (dz_in(dport, DZ_CSR) & DZ_MSE) { + unsigned short csr, mask; + + mask = tcr; + while ((mask & DZ_LNENB) && loops--) { + csr = dz_in(dport, DZ_CSR); + if (!(csr & DZ_TRDY)) + continue; + mask &= ~(1 << ((csr & DZ_TLINE) >> 8)); + dz_out(dport, DZ_TCR, mask); + iob(); + udelay(2); /* 1.4us TRDY recovery. */ + } + fsleep(1200); /* Transmitter drain. */ + } + dz_out(dport, DZ_CSR, DZ_CLR); while (dz_in(dport, DZ_CSR) & DZ_CLR); iob(); + /* + * Set parameters across all lines such as not to interfere + * with the initial PROM-based console. Otherwise any output + * produced before the console handover would cause the system + * firmware to produce rubbish. + */ + for (int line = 0; line < DZ_NB_PORT; line++) + dz_out(dport, DZ_LPR, DZ_B9600 | DZ_CS8 | line); + + /* Re-enable transmission for the initial PROM-based console. */ + dz_out(dport, DZ_TCR, tcr); + /* Enable scanning. */ dz_out(dport, DZ_CSR, DZ_MSE); @@ -633,26 +662,6 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, uart_port_unlock_irqrestore(&dport->port, flags); } -/* - * Hack alert! - * Required solely so that the initial PROM-based console - * works undisturbed in parallel with this one. - */ -static void dz_pm(struct uart_port *uport, unsigned int state, - unsigned int oldstate) -{ - struct dz_port *dport = to_dport(uport); - unsigned long flags; - - uart_port_lock_irqsave(&dport->port, &flags); - if (state < 3) - dz_start_tx(&dport->port); - else - dz_stop_tx(&dport->port); - uart_port_unlock_irqrestore(&dport->port, flags); -} - - static const char *dz_type(struct uart_port *uport) { return "DZ"; @@ -668,14 +677,13 @@ static void dz_release_port(struct uart_port *uport) map_guard = atomic_add_return(-1, &mux->map_guard); if (!map_guard) - release_mem_region(uport->mapbase, dec_kn_slot_size); + release_mem_region(uport->mapbase, DZ_IO_SIZE); } static int dz_map_port(struct uart_port *uport) { if (!uport->membase) - uport->membase = ioremap(uport->mapbase, - dec_kn_slot_size); + uport->membase = ioremap(uport->mapbase, DZ_IO_SIZE); if (!uport->membase) { printk(KERN_ERR "dz: Cannot map MMIO\n"); return -ENOMEM; @@ -691,8 +699,7 @@ static int dz_request_port(struct uart_port *uport) map_guard = atomic_add_return(1, &mux->map_guard); if (map_guard == 1) { - if (!request_mem_region(uport->mapbase, dec_kn_slot_size, - "dz")) { + if (!request_mem_region(uport->mapbase, DZ_IO_SIZE, "dz")) { atomic_add(-1, &mux->map_guard); printk(KERN_ERR "dz: Unable to reserve MMIO resource\n"); @@ -703,7 +710,7 @@ static int dz_request_port(struct uart_port *uport) if (ret) { map_guard = atomic_add_return(-1, &mux->map_guard); if (!map_guard) - release_mem_region(uport->mapbase, dec_kn_slot_size); + release_mem_region(uport->mapbase, DZ_IO_SIZE); return ret; } return 0; @@ -748,7 +755,6 @@ static const struct uart_ops dz_ops = { .startup = dz_startup, .shutdown = dz_shutdown, .set_termios = dz_set_termios, - .pm = dz_pm, .type = dz_type, .release_port = dz_release_port, .request_port = dz_request_port, @@ -756,20 +762,15 @@ static const struct uart_ops dz_ops = { .verify_port = dz_verify_port, }; -static void __init dz_init_ports(void) +static int __init dz_probe(struct platform_device *pdev) { - static int first = 1; - unsigned long base; + struct resource *mem_resource, *irq_resource; int line; - if (!first) - return; - first = 0; - - if (mips_machtype == MACH_DS23100 || mips_machtype == MACH_DS5100) - base = dec_kn_slot_base + KN01_DZ11; - else - base = dec_kn_slot_base + KN02_DZ11; + mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!mem_resource || !irq_resource) + return -ENODEV; for (line = 0; line < DZ_NB_PORT; line++) { struct dz_port *dport = &dz_mux.dport[line]; @@ -777,14 +778,33 @@ static void __init dz_init_ports(void) dport->mux = &dz_mux; - uport->irq = dec_interrupt[DEC_IRQ_DZ11]; + uport->dev = &pdev->dev; + uport->irq = irq_resource->start; uport->fifosize = 1; uport->iotype = UPIO_MEM; uport->flags = UPF_BOOT_AUTOCONF; uport->ops = &dz_ops; uport->line = line; - uport->mapbase = base; + uport->mapbase = mem_resource->start; uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_DZ_CONSOLE); + + if (uart_add_one_port(&dz_reg, uport)) + uport->dev = NULL; + } + + return 0; +} + +static void __exit dz_remove(struct platform_device *pdev) +{ + int line; + + for (line = DZ_NB_PORT - 1; line >= 0; line--) { + struct dz_port *dport = &dz_mux.dport[line]; + struct uart_port *uport = &dport->port; + + if (uport->dev) + uart_remove_one_port(&dz_reg, uport); } } @@ -867,24 +887,14 @@ static int __init dz_console_setup(struct console *co, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; - int ret; - - ret = dz_map_port(uport); - if (ret) - return ret; - - spin_lock_init(&dport->port.lock); /* For dz_pm(). */ - - dz_reset(dport); - dz_pm(uport, 0, -1); + if (!dport->mux) + return -ENODEV; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&dport->port, co, baud, parity, bits, flow); + return uart_set_options(uport, co, baud, parity, bits, flow); } -static struct uart_driver dz_reg; static struct console dz_console = { .name = "ttyS", .write = dz_console_print, @@ -895,18 +905,6 @@ static struct console dz_console = { .data = &dz_reg, }; -static int __init dz_serial_console_init(void) -{ - if (!IOASIC) { - dz_init_ports(); - register_console(&dz_console); - return 0; - } else - return -ENXIO; -} - -console_initcall(dz_serial_console_init); - #define SERIAL_DZ_CONSOLE &dz_console #else #define SERIAL_DZ_CONSOLE NULL @@ -922,25 +920,32 @@ static struct uart_driver dz_reg = { .cons = SERIAL_DZ_CONSOLE, }; +static struct platform_driver dz_driver = { + .remove = __exit_p(dz_remove), + .driver = { .name = "dz" }, +}; + static int __init dz_init(void) { - int ret, i; - - if (IOASIC) - return -ENXIO; + int ret; printk("%s%s\n", dz_name, dz_version); - dz_init_ports(); - ret = uart_register_driver(&dz_reg); if (ret) return ret; + ret = platform_driver_probe(&dz_driver, dz_probe); + if (ret) + uart_unregister_driver(&dz_reg); - for (i = 0; i < DZ_NB_PORT; i++) - uart_add_one_port(&dz_reg, &dz_mux.dport[i].port); + return ret; +} - return 0; +static void __exit dz_exit(void) +{ + platform_driver_unregister(&dz_driver); + uart_unregister_driver(&dz_reg); } module_init(dz_init); +module_exit(dz_exit); diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 1bd7ec9c81ea4..b7919c05f0fb8 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1379,7 +1379,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) if (!nent) { dev_err(sport->port.dev, "DMA Rx mapping error\n"); - return -EINVAL; + ret = -EINVAL; + goto err_free_buf; } dma_rx_sconfig.src_addr = lpuart_dma_datareg_addr(sport); @@ -1391,7 +1392,7 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) if (ret < 0) { dev_err(sport->port.dev, "DMA Rx slave config failed, err = %d\n", ret); - return ret; + goto err_unmap_sg; } sport->dma_rx_desc = dmaengine_prep_dma_cyclic(chan, @@ -1402,7 +1403,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) DMA_PREP_INTERRUPT); if (!sport->dma_rx_desc) { dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n"); - return -EFAULT; + ret = -ENOMEM; + goto err_unmap_sg; } sport->dma_rx_desc->callback = lpuart_dma_rx_complete; @@ -1426,6 +1428,13 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) } return 0; + +err_unmap_sg: + dma_unmap_sg(chan->device->dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE); +err_free_buf: + kfree(ring->buf); + ring->buf = NULL; + return ret; } static void lpuart_dma_rx_free(struct uart_port *port) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 6729d8e83c3c5..ba1fcd663fe22 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -689,8 +689,7 @@ static void pch_request_dma(struct uart_port *port) if (!chan) { dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Tx)\n", __func__); - pci_dev_put(dma_dev); - return; + goto err_pci_get; } priv->chan_tx = chan; @@ -704,18 +703,26 @@ static void pch_request_dma(struct uart_port *port) if (!chan) { dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Rx)\n", __func__); - dma_release_channel(priv->chan_tx); - priv->chan_tx = NULL; - pci_dev_put(dma_dev); - return; + goto err_req_tx; } /* Get Consistent memory for DMA */ priv->rx_buf_virt = dma_alloc_coherent(port->dev, port->fifosize, &priv->rx_buf_dma, GFP_KERNEL); + if (!priv->rx_buf_virt) + goto err_req_rx; priv->chan_rx = chan; pci_dev_put(dma_dev); + return; + +err_req_rx: + dma_release_channel(chan); +err_req_tx: + dma_release_channel(priv->chan_tx); + priv->chan_tx = NULL; +err_pci_get: + pci_dev_put(dma_dev); } static void pch_dma_rx_complete(void *arg) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index b365dd5da3cb7..17da115b1e78b 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -50,7 +50,7 @@ #define TX_STOP_BIT_LEN_2 2 /* SE_UART_RX_TRANS_CFG */ -#define UART_RX_PAR_EN BIT(3) +#define UART_RX_PAR_EN BIT(4) /* SE_UART_RX_WORD_LEN */ #define RX_WORD_LEN_MASK GENMASK(9, 0) @@ -1031,8 +1031,20 @@ static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport); struct tty_port *tport = &uport->state->port; + unsigned int fifo_len = kfifo_len(&tport->xmit_fifo); + + /* + * Only advance the kfifo if it still contains the bytes that were + * transferred. uart_flush_buffer() may have run before this IRQ + * fired: it calls kfifo_reset() under the port lock, making + * fifo_len = 0 while tx_remaining remains non-zero. Calling + * uart_xmit_advance() in that case would underflow kfifo->out past + * kfifo->in, making kfifo_len() wrap to UART_XMIT_SIZE - tx_remaining + * and triggering a spurious large DMA transfer of stale data. + */ + if (fifo_len >= port->tx_remaining) + uart_xmit_advance(uport, port->tx_remaining); - uart_xmit_advance(uport, port->tx_remaining); geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining); port->tx_dma_addr = 0; port->tx_remaining = 0; diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index e27806bf2cf3e..17cd5bb100b15 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -245,12 +245,9 @@ static bool s3c24xx_serial_txempty_nofifo(const struct uart_port *port) static void s3c24xx_serial_rx_enable(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); - unsigned long flags; int count = 10000; u32 ucon, ufcon; - uart_port_lock_irqsave(port, &flags); - while (--count && !s3c24xx_serial_txempty_nofifo(port)) udelay(100); @@ -263,23 +260,18 @@ static void s3c24xx_serial_rx_enable(struct uart_port *port) wr_regl(port, S3C2410_UCON, ucon); ourport->rx_enabled = 1; - uart_port_unlock_irqrestore(port, flags); } static void s3c24xx_serial_rx_disable(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); - unsigned long flags; u32 ucon; - uart_port_lock_irqsave(port, &flags); - ucon = rd_regl(port, S3C2410_UCON); ucon &= ~S3C2410_UCON_RXIRQMODE; wr_regl(port, S3C2410_UCON, ucon); ourport->rx_enabled = 0; - uart_port_unlock_irqrestore(port, flags); } static void s3c24xx_serial_stop_tx(struct uart_port *port) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 6c819b6b24258..54db019a5bfcd 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -3025,7 +3025,7 @@ int sci_request_port(struct uart_port *port) ret = sci_remap_port(port); if (unlikely(ret != 0)) { - release_resource(res); + release_mem_region(port->mapbase, sport->reg_size); return ret; } diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 72a3c0d90f409..8f92b4129a385 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -56,6 +56,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/major.h> +#include <linux/platform_device.h> #include <linux/serial.h> #include <linux/serial_core.h> #include <linux/spinlock.h> @@ -66,10 +67,6 @@ #include <linux/atomic.h> -#include <asm/dec/interrupts.h> -#include <asm/dec/ioasic_addrs.h> -#include <asm/dec/system.h> - #include "zs.h" @@ -79,7 +76,7 @@ MODULE_LICENSE("GPL"); static char zs_name[] __initdata = "DECstation Z85C30 serial driver version "; -static char zs_version[] __initdata = "0.10"; +static char zs_version[] __initdata = "0.11"; /* * It would be nice to dynamically allocate everything that @@ -98,25 +95,27 @@ static char zs_version[] __initdata = "0.10"; #define to_zport(uport) container_of(uport, struct zs_port, port) -struct zs_parms { - resource_size_t scc[ZS_NUM_SCCS]; - int irq[ZS_NUM_SCCS]; -}; - static struct zs_scc zs_sccs[ZS_NUM_SCCS]; +static struct uart_driver zs_reg; +/* + * Set parameters in WR5, WR12, WR13 such as not to interfere + * with the initial PROM-based console. Otherwise any output + * produced before the console handover would cause the system + * firmware to hang (TxENAB) or produce rubbish (Tx8, B9600). + */ static u8 zs_init_regs[ZS_NUM_REGS] __initdata = { 0, /* write 0 */ PAR_SPEC, /* write 1 */ 0, /* write 2 */ 0, /* write 3 */ X16CLK | SB1, /* write 4 */ - 0, /* write 5 */ + Tx8 | TxENAB, /* write 5 */ 0, 0, 0, /* write 6, 7, 8 */ MIE | DLC | NV, /* write 9 */ NRZ, /* write 10 */ TCBR | RCBR, /* write 11 */ - 0, 0, /* BRG time constant, write 12 + 13 */ + 0x16, 0x00, /* BRG time constant, write 12 + 13 */ BRSRC | BRENABL, /* write 14 */ 0, /* write 15 */ }; @@ -680,9 +679,9 @@ static void zs_status_handle(struct zs_port *zport, struct zs_port *zport_a) uart_handle_dcd_change(uport, zport->mctrl & TIOCM_CAR); if (delta & TIOCM_RNG) - uport->icount.dsr++; - if (delta & TIOCM_DSR) uport->icount.rng++; + if (delta & TIOCM_DSR) + uport->icount.dsr++; if (delta) wake_up_interruptible(&uport->state->port.delta_msr_wait); @@ -826,22 +825,22 @@ static void zs_shutdown(struct uart_port *uport) static void zs_reset(struct zs_port *zport) { + struct zs_port *zport_a = &zport->scc->zport[ZS_CHAN_A]; struct zs_scc *scc = zport->scc; int irq; unsigned long flags; spin_lock_irqsave(&scc->zlock, flags); irq = !irqs_disabled_flags(flags); - if (!scc->initialised) { - /* Reset the pointer first, just in case... */ - read_zsreg(zport, R0); - /* And let the current transmission finish. */ - zs_line_drain(zport, irq); - write_zsreg(zport, R9, FHWRES); - udelay(10); - write_zsreg(zport, R9, 0); - scc->initialised = 1; - } + + /* Reset the pointer first, just in case... */ + read_zsreg(zport, R0); + /* And let the current transmission finish. */ + zs_line_drain(zport, irq); + write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB); + udelay(10); + write_zsreg(zport, R9, 0); + load_zsregs(zport, zport->regs, irq); spin_unlock_irqrestore(&scc->zlock, flags); } @@ -956,23 +955,6 @@ static void zs_set_termios(struct uart_port *uport, struct ktermios *termios, spin_unlock_irqrestore(&scc->zlock, flags); } -/* - * Hack alert! - * Required solely so that the initial PROM-based console - * works undisturbed in parallel with this one. - */ -static void zs_pm(struct uart_port *uport, unsigned int state, - unsigned int oldstate) -{ - struct zs_port *zport = to_zport(uport); - - if (state < 3) - zport->regs[5] |= TxENAB; - else - zport->regs[5] &= ~TxENAB; - write_zsreg(zport, R5, zport->regs[5]); -} - static const char *zs_type(struct uart_port *uport) { @@ -1055,7 +1037,6 @@ static const struct uart_ops zs_ops = { .startup = zs_startup, .shutdown = zs_shutdown, .set_termios = zs_set_termios, - .pm = zs_pm, .type = zs_type, .release_port = zs_release_port, .request_port = zs_request_port, @@ -1066,63 +1047,62 @@ static const struct uart_ops zs_ops = { /* * Initialize Z85C30 port structures. */ -static int __init zs_probe_sccs(void) +static int __init zs_probe(struct platform_device *pdev) { - static int probed; - struct zs_parms zs_parms; - int chip, side, irq; - int n_chips = 0; + struct resource *mem_resource, *irq_resource; + int chip, side; int i; - if (probed) - return 0; + mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!mem_resource || !irq_resource) + return -ENODEV; - irq = dec_interrupt[DEC_IRQ_SCC0]; - if (irq >= 0) { - zs_parms.scc[n_chips] = IOASIC_SCC0; - zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC0]; - n_chips++; - } - irq = dec_interrupt[DEC_IRQ_SCC1]; - if (irq >= 0) { - zs_parms.scc[n_chips] = IOASIC_SCC1; - zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC1]; - n_chips++; - } - if (!n_chips) - return -ENXIO; - - probed = 1; + chip = pdev->id; + spin_lock_init(&zs_sccs[chip].zlock); + for (side = 0; side < ZS_NUM_CHAN; side++) { + struct zs_port *zport = &zs_sccs[chip].zport[side]; + struct uart_port *uport = &zport->port; - for (chip = 0; chip < n_chips; chip++) { - spin_lock_init(&zs_sccs[chip].zlock); - for (side = 0; side < ZS_NUM_CHAN; side++) { - struct zs_port *zport = &zs_sccs[chip].zport[side]; - struct uart_port *uport = &zport->port; + zport->scc = &zs_sccs[chip]; + zport->clk_mode = 16; - zport->scc = &zs_sccs[chip]; - zport->clk_mode = 16; + uport->dev = &pdev->dev; + uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE); + uport->irq = irq_resource->start; + uport->uartclk = ZS_CLOCK; + uport->fifosize = 1; + uport->iotype = UPIO_MEM; + uport->flags = UPF_BOOT_AUTOCONF; + uport->ops = &zs_ops; + uport->line = chip * ZS_NUM_CHAN + side; + uport->mapbase = mem_resource->start + + (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE; - uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE); - uport->irq = zs_parms.irq[chip]; - uport->uartclk = ZS_CLOCK; - uport->fifosize = 1; - uport->iotype = UPIO_MEM; - uport->flags = UPF_BOOT_AUTOCONF; - uport->ops = &zs_ops; - uport->line = chip * ZS_NUM_CHAN + side; - uport->mapbase = dec_kn_slot_base + - zs_parms.scc[chip] + - (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE; + for (i = 0; i < ZS_NUM_REGS; i++) + zport->regs[i] = zs_init_regs[i]; - for (i = 0; i < ZS_NUM_REGS; i++) - zport->regs[i] = zs_init_regs[i]; - } + if (uart_add_one_port(&zs_reg, uport)) + uport->dev = NULL; } return 0; } +static void __exit zs_remove(struct platform_device *pdev) +{ + int chip, side; + + chip = pdev->id; + for (side = ZS_NUM_CHAN - 1; side >= 0; side--) { + struct zs_port *zport = &zs_sccs[chip].zport[side]; + struct uart_port *uport = &zport->port; + + if (uport->dev) + uart_remove_one_port(&zs_reg, uport); + } +} + #ifdef CONFIG_SERIAL_ZS_CONSOLE static void zs_console_putchar(struct uart_port *uport, unsigned char ch) @@ -1203,21 +1183,14 @@ static int __init zs_console_setup(struct console *co, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; - int ret; - - ret = zs_map_port(uport); - if (ret) - return ret; - - zs_reset(zport); - zs_pm(uport, 0, -1); + if (!zport->scc) + return -ENODEV; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); return uart_set_options(uport, co, baud, parity, bits, flow); } -static struct uart_driver zs_reg; static struct console zs_console = { .name = "ttyS", .write = zs_console_write, @@ -1228,23 +1201,6 @@ static struct console zs_console = { .data = &zs_reg, }; -/* - * Register console. - */ -static int __init zs_serial_console_init(void) -{ - int ret; - - ret = zs_probe_sccs(); - if (ret) - return ret; - register_console(&zs_console); - - return 0; -} - -console_initcall(zs_serial_console_init); - #define SERIAL_ZS_CONSOLE &zs_console #else #define SERIAL_ZS_CONSOLE NULL @@ -1260,47 +1216,31 @@ static struct uart_driver zs_reg = { .cons = SERIAL_ZS_CONSOLE, }; +static struct platform_driver zs_driver = { + .remove = __exit_p(zs_remove), + .driver = { .name = "zs" }, +}; + /* zs_init inits the driver. */ static int __init zs_init(void) { - int i, ret; + int ret; pr_info("%s%s\n", zs_name, zs_version); - /* Find out how many Z85C30 SCCs we have. */ - ret = zs_probe_sccs(); - if (ret) - return ret; - ret = uart_register_driver(&zs_reg); if (ret) return ret; + ret = platform_driver_probe(&zs_driver, zs_probe); + if (ret) + uart_unregister_driver(&zs_reg); - for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) { - struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; - struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; - struct uart_port *uport = &zport->port; - - if (zport->scc) - uart_add_one_port(&zs_reg, uport); - } - - return 0; + return ret; } static void __exit zs_exit(void) { - int i; - - for (i = ZS_NUM_SCCS * ZS_NUM_CHAN - 1; i >= 0; i--) { - struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; - struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; - struct uart_port *uport = &zport->port; - - if (zport->scc) - uart_remove_one_port(&zs_reg, uport); - } - + platform_driver_unregister(&zs_driver); uart_unregister_driver(&zs_reg); } diff --git a/drivers/tty/serial/zs.h b/drivers/tty/serial/zs.h index 26ef8eafa1c12..e0d3c189b33f6 100644 --- a/drivers/tty/serial/zs.h +++ b/drivers/tty/serial/zs.h @@ -41,7 +41,6 @@ struct zs_scc { struct zs_port zport[2]; spinlock_t zlock; atomic_t irq_guard; - int initialised; }; #endif /* __KERNEL__ */ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 666430b478997..110ad4e2aef99 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -1275,6 +1275,18 @@ static inline void uart_unlock_and_check_sysrq_irqrestore(struct uart_port *port #endif /* CONFIG_MAGIC_SYSRQ_SERIAL */ /* + * Variant of guard(uart_port_lock_irqsave) for IRQ handlers that may capture + * a SysRq character via uart_prepare_sysrq_char(). The destructor uses the + * sysrq-aware unlock helper so that a captured port->sysrq_ch is dispatched + * to handle_sysrq() on scope exit. The plain guard variant silently drops + * sysrq_ch and must not be used by callers that process RX. + */ +DEFINE_LOCK_GUARD_1(uart_port_lock_check_sysrq_irqsave, struct uart_port, + uart_port_lock_irqsave(_T->lock, &_T->flags), + uart_unlock_and_check_sysrq_irqrestore(_T->lock, _T->flags), + unsigned long flags); + +/* * We do the SysRQ and SAK checking like this... */ static inline int uart_handle_break(struct uart_port *port) diff --git a/include/linux/tty_port.h b/include/linux/tty_port.h index d2a7882c0b58b..23cad403bb8f5 100644 --- a/include/linux/tty_port.h +++ b/include/linux/tty_port.h @@ -6,10 +6,10 @@ #include <linux/kref.h> #include <linux/mutex.h> #include <linux/tty_buffer.h> +#include <linux/tty_driver.h> #include <linux/wait.h> struct attribute_group; -struct tty_driver; struct tty_port; struct tty_struct; |
