TTY/Serial driver fixes for 7.1-rc6

Here are some small serial driver fixes for 7.1-rc6.  Included in here
 are:
   - mips serial driver fixes to resolve some long-standing issues with
     how they interacted with the console.  That's the "majority" of the
     changes in this merge request
   - sh-sci driver regression fix
   - 8250 driver regression fixes
   - other small serial driver fixes for reported problems.
 
 All of these have been in linux-next for over a week with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCahrImA8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ykedQCfb1CR0Z6lElo/02m3wmR+EfvGyoUAoLj8QU71
 dFaLWzZQk8Hb6ajmVYK5
 =tgAC
 -----END PGP SIGNATURE-----

Merge tag 'tty-7.1-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty/serial driver fixes from Greg KH:
 "Here are some small serial driver fixes for 7.1-rc6. Included in here
  are:

   - mips serial driver fixes to resolve some long-standing issues with
     how they interacted with the console. That's the "majority" of the
     changes in this merge request

   - sh-sci driver regression fix

   - 8250 driver regression fixes

   - other small serial driver fixes for reported problems.

  All of these have been in linux-next for over a week with no reported
  issues"

* tag 'tty-7.1-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
  serial: dz: Enable modular build
  serial: zs: Convert to use a platform device
  serial: dz: Convert to use a platform device
  serial: zs: Switch to using channel reset
  serial: zs: Fix bootconsole handover lockup
  serial: dz: Fix bootconsole handover lockup
  serial: dz: Fix bootconsole message clobbering at chip reset
  serial: 8250_dw: dispatch SysRq character in dw8250_handle_irq()
  serial: 8250: dispatch SysRq character in serial8250_handle_irq()
  serial: core: introduce guard(uart_port_lock_check_sysrq_irqsave)
  tty: serial: samsung: Remove redundant port lock acquisition in rx helpers
  serial: altera_jtaguart: handle uart_add_one_port() failures
  serial: qcom_geni: fix kfifo underflow when flush precedes DMA completion IRQ
  serial: fsl_lpuart: fix rx buffer and DMA map leaks in start_rx_dma
  tty: add missing tty_driver include to tty_port.h
  serial: qcom-geni: fix UART_RX_PAR_EN bit position
  serial: sh-sci: fix memory region release in error path
  tty: serial: pch_uart: add check for dma_alloc_coherent()
  serial: zs: Fix swapped RI/DSR modem line transition counting
This commit is contained in:
Linus Torvalds 2026-05-30 08:34:03 -07:00
commit 495fb8dd7f
15 changed files with 341 additions and 250 deletions

View File

@ -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);

View File

@ -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:

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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)

View File

@ -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)

View File

@ -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;

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
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;
probed = 1;
zport->scc = &zs_sccs[chip];
zport->clk_mode = 16;
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;
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;
zport->scc = &zs_sccs[chip];
zport->clk_mode = 16;
for (i = 0; i < ZS_NUM_REGS; i++)
zport->regs[i] = zs_init_regs[i];
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];
}
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);
}

View File

@ -41,7 +41,6 @@ struct zs_scc {
struct zs_port zport[2];
spinlock_t zlock;
atomic_t irq_guard;
int initialised;
};
#endif /* __KERNEL__ */

View File

@ -1274,6 +1274,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...
*/

View File

@ -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;