diff options
Diffstat (limited to 'drivers/tty/serial/pmac_zilog.c')
| -rw-r--r-- | drivers/tty/serial/pmac_zilog.c | 242 |
1 files changed, 72 insertions, 170 deletions
diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index 0da52947e59e..e3a919328695 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for PowerMac Z85c30 based ESCC cell found in the * "macio" ASICs of various PowerMac models @@ -13,20 +14,6 @@ * and once done, I expect that driver to remain fairly stable in * the long term, unless we change the driver model again... * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * * 2004-08-06 Harald Welte <laforge@gnumonks.org> * - Enable BREAK interrupt * - Add support for sysreq @@ -37,7 +24,6 @@ */ #undef DEBUG -#undef DEBUG_HARD #undef USE_CTRL_O_SYSRQ #include <linux/module.h> @@ -60,33 +46,23 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <asm/sections.h> -#include <asm/io.h> +#include <linux/io.h> #include <asm/irq.h> #ifdef CONFIG_PPC_PMAC -#include <asm/prom.h> #include <asm/machdep.h> #include <asm/pmac_feature.h> -#include <asm/dbdma.h> #include <asm/macio.h> #else #include <linux/platform_device.h> #define of_machine_is_compatible(x) (0) #endif -#if defined (CONFIG_SERIAL_PMACZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - #include <linux/serial.h> #include <linux/serial_core.h> #include "pmac_zilog.h" -/* Not yet implemented */ -#undef HAS_DBDMA - -static char version[] __initdata = "pmac_zilog: 0.6 (Benjamin Herrenschmidt <benh@kernel.crashing.org>)"; MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); MODULE_DESCRIPTION("Driver for the Mac and PowerMac serial ports."); MODULE_LICENSE("GPL"); @@ -230,10 +206,10 @@ static void pmz_interrupt_control(struct uart_pmac_port *uap, int enable) } static bool pmz_receive_chars(struct uart_pmac_port *uap) + __must_hold(&uap->port.lock) { struct tty_port *port; - unsigned char ch, r1, drop, error, flag; - int loops = 0; + unsigned char ch, r1, drop, flag; /* Sanity check, make sure the old bug is no longer happening */ if (uap->port.state == NULL) { @@ -244,7 +220,6 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap) port = &uap->port.state->port; while (1) { - error = 0; drop = 0; r1 = read_zsreg(uap, R1); @@ -270,9 +245,9 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap) #endif /* USE_CTRL_O_SYSRQ */ if (uap->port.sysrq) { int swallow; - spin_unlock(&uap->port.lock); + uart_port_unlock(&uap->port); swallow = uart_handle_sysrq_char(&uap->port, ch); - spin_lock(&uap->port.lock); + uart_port_lock(&uap->port); if (swallow) goto next_char; } @@ -286,7 +261,6 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap) uap->port.icount.rx++; if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | BRK_ABRT)) { - error = 1; if (r1 & BRK_ABRT) { pmz_debug("pmz: got break !\n"); r1 &= ~(PAR_ERR | CRC_ERR); @@ -316,25 +290,12 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap) if (r1 & Rx_OVR) tty_insert_flip_char(port, 0, TTY_OVERRUN); next_char: - /* We can get stuck in an infinite loop getting char 0 when the - * line is in a wrong HW state, we break that here. - * When that happens, I disable the receive side of the driver. - * Note that what I've been experiencing is a real irq loop where - * I'm getting flooded regardless of the actual port speed. - * Something strange is going on with the HW - */ - if ((++loops) > 1000) - goto flood; ch = read_zsreg(uap, R0); if (!(ch & Rx_CH_AV)) break; } return true; - flood: - pmz_interrupt_control(uap, 0); - pmz_error("pmz: rx irq flood !\n"); - return true; } static void pmz_status_handle(struct uart_pmac_port *uap) @@ -372,7 +333,8 @@ static void pmz_status_handle(struct uart_pmac_port *uap) static void pmz_transmit_chars(struct uart_pmac_port *uap) { - struct circ_buf *xmit; + struct tty_port *tport; + unsigned char ch; if (ZS_IS_CONS(uap)) { unsigned char status = read_zsreg(uap, R0); @@ -423,8 +385,8 @@ static void pmz_transmit_chars(struct uart_pmac_port *uap) if (uap->port.state == NULL) goto ack_tx_int; - xmit = &uap->port.state->xmit; - if (uart_circ_empty(xmit)) { + tport = &uap->port.state->port; + if (kfifo_is_empty(&tport->xmit_fifo)) { uart_write_wakeup(&uap->port); goto ack_tx_int; } @@ -432,13 +394,11 @@ static void pmz_transmit_chars(struct uart_pmac_port *uap) goto ack_tx_int; uap->flags |= PMACZILOG_FLAG_TX_ACTIVE; - write_zsdata(uap, xmit->buf[xmit->tail]); + WARN_ON(!uart_fifo_get(&uap->port, &ch)); + write_zsdata(uap, ch); zssync(uap); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - uap->port.icount.tx++; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); return; @@ -461,12 +421,9 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) uap_a = pmz_get_port_A(uap); uap_b = uap_a->mate; - spin_lock(&uap_a->port.lock); + uart_port_lock(&uap_a->port); r3 = read_zsreg(uap_a, R3); -#ifdef DEBUG_HARD - pmz_debug("irq, r3: %x\n", r3); -#endif /* Channel A */ push = false; if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { @@ -485,14 +442,14 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) rc = IRQ_HANDLED; } skip_a: - spin_unlock(&uap_a->port.lock); + uart_port_unlock(&uap_a->port); if (push) tty_flip_buffer_push(&uap->port.state->port); if (!uap_b) goto out; - spin_lock(&uap_b->port.lock); + uart_port_lock(&uap_b->port); push = false; if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { if (!ZS_IS_OPEN(uap_b)) { @@ -510,7 +467,7 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) rc = IRQ_HANDLED; } skip_b: - spin_unlock(&uap_b->port.lock); + uart_port_unlock(&uap_b->port); if (push) tty_flip_buffer_push(&uap->port.state->port); @@ -526,9 +483,9 @@ static inline u8 pmz_peek_status(struct uart_pmac_port *uap) unsigned long flags; u8 status; - spin_lock_irqsave(&uap->port.lock, flags); + uart_port_lock_irqsave(&uap->port, &flags); status = read_zsreg(uap, R0); - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); return status; } @@ -631,8 +588,6 @@ static void pmz_start_tx(struct uart_port *port) struct uart_pmac_port *uap = to_pmz(port); unsigned char status; - pmz_debug("pmz: start_tx()\n"); - uap->flags |= PMACZILOG_FLAG_TX_ACTIVE; uap->flags &= ~PMACZILOG_FLAG_TX_STOPPED; @@ -651,20 +606,17 @@ static void pmz_start_tx(struct uart_port *port) port->icount.tx++; port->x_char = 0; } else { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char ch; - if (uart_circ_empty(xmit)) - goto out; - write_zsdata(uap, xmit->buf[xmit->tail]); + if (!uart_fifo_get(&uap->port, &ch)) + return; + write_zsdata(uap, ch); zssync(uap); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); } - out: - pmz_debug("pmz: start_tx() done.\n"); } /* @@ -677,13 +629,9 @@ static void pmz_stop_rx(struct uart_port *port) { struct uart_pmac_port *uap = to_pmz(port); - pmz_debug("pmz: stop_rx()()\n"); - /* Disable all RX interrupts. */ uap->curregs[R1] &= ~RxINT_MASK; pmz_maybe_update_regs(uap); - - pmz_debug("pmz: stop_rx() done.\n"); } /* @@ -723,7 +671,7 @@ static void pmz_break_ctl(struct uart_port *port, int break_state) else clear_bits |= SND_BRK; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); new_reg = (uap->curregs[R5] | set_bits) & ~clear_bits; if (new_reg != uap->curregs[R5]) { @@ -731,7 +679,7 @@ static void pmz_break_ctl(struct uart_port *port, int break_state) write_zsreg(uap, R5, uap->curregs[R5]); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } #ifdef CONFIG_PPC_PMAC @@ -903,18 +851,18 @@ static void pmz_irda_reset(struct uart_pmac_port *uap) { unsigned long flags; - spin_lock_irqsave(&uap->port.lock, flags); + uart_port_lock_irqsave(&uap->port, &flags); uap->curregs[R5] |= DTR; write_zsreg(uap, R5, uap->curregs[R5]); zssync(uap); - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); msleep(110); - spin_lock_irqsave(&uap->port.lock, flags); + uart_port_lock_irqsave(&uap->port, &flags); uap->curregs[R5] &= ~DTR; write_zsreg(uap, R5, uap->curregs[R5]); zssync(uap); - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); msleep(10); } @@ -928,17 +876,15 @@ static int pmz_startup(struct uart_port *port) unsigned long flags; int pwr_delay = 0; - pmz_debug("pmz: startup()\n"); - uap->flags |= PMACZILOG_FLAG_IS_OPEN; /* A console is never powered down. Else, power up and * initialize the chip */ if (!ZS_IS_CONS(uap)) { - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); pwr_delay = __pmz_startup(uap); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } sprintf(uap->irq_name, PMACZILOG_NAME"%d", uap->port.line); if (request_irq(uap->port.irq, pmz_interrupt, IRQF_SHARED, @@ -961,11 +907,9 @@ static int pmz_startup(struct uart_port *port) pmz_irda_reset(uap); /* Enable interrupt requests for the channel */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); pmz_interrupt_control(uap, 1); - spin_unlock_irqrestore(&port->lock, flags); - - pmz_debug("pmz: startup() done.\n"); + uart_port_unlock_irqrestore(port, flags); return 0; } @@ -975,9 +919,7 @@ static void pmz_shutdown(struct uart_port *port) struct uart_pmac_port *uap = to_pmz(port); unsigned long flags; - pmz_debug("pmz: shutdown()\n"); - - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Disable interrupt requests for the channel */ pmz_interrupt_control(uap, 0); @@ -992,21 +934,19 @@ static void pmz_shutdown(struct uart_port *port) pmz_maybe_update_regs(uap); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); /* Release interrupt handler */ free_irq(uap->port.irq, uap); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uap->flags &= ~PMACZILOG_FLAG_IS_OPEN; if (!ZS_IS_CONS(uap)) pmz_set_scc_power(uap, 0); /* Shut the chip down */ - spin_unlock_irqrestore(&port->lock, flags); - - pmz_debug("pmz: shutdown() done.\n"); + uart_port_unlock_irqrestore(port, flags); } /* Shared by TTY driver and serial console setup. The port lock is held @@ -1246,15 +1186,11 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud) static void __pmz_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) + const struct ktermios *old) { struct uart_pmac_port *uap = to_pmz(port); unsigned long baud; - pmz_debug("pmz: set_termios()\n"); - - memcpy(&uap->termios_cache, termios, sizeof(struct ktermios)); - /* XXX Check which revs of machines actually allow 1 and 4Mb speeds * on the IR dongle. Note that the IRTTY driver currently doesn't know * about the FIR mode and high speed modes. So these are unused. For @@ -1288,18 +1224,16 @@ static void __pmz_set_termios(struct uart_port *port, struct ktermios *termios, pmz_maybe_update_regs(uap); } uart_update_timeout(port, termios->c_cflag, baud); - - pmz_debug("pmz: set_termios() done.\n"); } /* The port lock is not held. */ static void pmz_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) + const struct ktermios *old) { struct uart_pmac_port *uap = to_pmz(port); unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Disable IRQs on the port */ pmz_interrupt_control(uap, 0); @@ -1311,7 +1245,7 @@ static void pmz_set_termios(struct uart_port *port, struct ktermios *termios, if (ZS_IS_OPEN(uap)) pmz_interrupt_control(uap, 1); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *pmz_type(struct uart_port *port) @@ -1418,7 +1352,7 @@ static int __init pmz_init_port(struct uart_pmac_port *uap) char name[1]; } *slots; int len; - struct resource r_ports, r_rxdma, r_txdma; + struct resource r_ports; /* * Request & map chip registers @@ -1430,35 +1364,6 @@ static int __init pmz_init_port(struct uart_pmac_port *uap) uap->control_reg = uap->port.membase; uap->data_reg = uap->control_reg + 0x10; - - /* - * Request & map DBDMA registers - */ -#ifdef HAS_DBDMA - if (of_address_to_resource(np, 1, &r_txdma) == 0 && - of_address_to_resource(np, 2, &r_rxdma) == 0) - uap->flags |= PMACZILOG_FLAG_HAS_DMA; -#else - memset(&r_txdma, 0, sizeof(struct resource)); - memset(&r_rxdma, 0, sizeof(struct resource)); -#endif - if (ZS_HAS_DMA(uap)) { - uap->tx_dma_regs = ioremap(r_txdma.start, 0x100); - if (uap->tx_dma_regs == NULL) { - uap->flags &= ~PMACZILOG_FLAG_HAS_DMA; - goto no_dma; - } - uap->rx_dma_regs = ioremap(r_rxdma.start, 0x100); - if (uap->rx_dma_regs == NULL) { - iounmap(uap->tx_dma_regs); - uap->tx_dma_regs = NULL; - uap->flags &= ~PMACZILOG_FLAG_HAS_DMA; - goto no_dma; - } - uap->tx_dma_irq = irq_of_parse_and_map(np, 1); - uap->rx_dma_irq = irq_of_parse_and_map(np, 2); - } -no_dma: /* * Detect port type @@ -1524,8 +1429,6 @@ no_dma: of_device_is_compatible(np->parent->parent, "gatwick")) { /* IRQs on gatwick are offset by 64 */ uap->port.irq = irq_create_mapping(NULL, 64 + 15); - uap->tx_dma_irq = irq_create_mapping(NULL, 64 + 4); - uap->rx_dma_irq = irq_create_mapping(NULL, 64 + 5); } /* Setup some valid baud rate information in the register @@ -1545,8 +1448,6 @@ static void pmz_dispose_port(struct uart_pmac_port *uap) struct device_node *np; np = uap->node; - iounmap(uap->rx_dma_regs); - iounmap(uap->tx_dma_regs); iounmap(uap->control_reg); uap->node = NULL; of_node_put(np); @@ -1579,9 +1480,9 @@ static int pmz_attach(struct macio_dev *mdev, const struct of_device_id *match) * to work around bugs in ancient Apple device-trees */ if (macio_request_resources(uap->dev, "pmac_zilog")) - printk(KERN_WARNING "%s: Failed to request resource" + printk(KERN_WARNING "%pOFn: Failed to request resource" ", port still active\n", - uap->node->name); + uap->node); else uap->flags |= PMACZILOG_FLAG_RSRC_REQUESTED; @@ -1592,12 +1493,12 @@ static int pmz_attach(struct macio_dev *mdev, const struct of_device_id *match) * That one should not be called, macio isn't really a hotswap device, * we don't expect one of those serial ports to go away... */ -static int pmz_detach(struct macio_dev *mdev) +static void pmz_detach(struct macio_dev *mdev) { struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); if (!uap) - return -ENODEV; + return; uart_remove_one_port(&pmz_uart_reg, &uap->port); @@ -1608,11 +1509,8 @@ static int pmz_detach(struct macio_dev *mdev) dev_set_drvdata(&mdev->ofdev.dev, NULL); uap->dev = NULL; uap->port.dev = NULL; - - return 0; } - static int pmz_suspend(struct macio_dev *mdev, pm_message_t pm_state) { struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); @@ -1662,17 +1560,17 @@ static int __init pmz_probe(void) * TODO: Add routines with proper locking to do that... */ node_a = node_b = NULL; - for (np = NULL; (np = of_get_next_child(node_p, np)) != NULL;) { - if (strncmp(np->name, "ch-a", 4) == 0) + for_each_child_of_node(node_p, np) { + if (of_node_name_prefix(np, "ch-a")) node_a = of_node_get(np); - else if (strncmp(np->name, "ch-b", 4) == 0) + else if (of_node_name_prefix(np, "ch-b")) node_b = of_node_get(np); } if (!node_a && !node_b) { of_node_put(node_a); of_node_put(node_b); - printk(KERN_ERR "pmac_zilog: missing node %c for escc %s\n", - (!node_a) ? 'a' : 'b', node_p->full_name); + printk(KERN_ERR "pmac_zilog: missing node %c for escc %pOF\n", + (!node_a) ? 'a' : 'b', node_p); continue; } @@ -1711,6 +1609,11 @@ static int __init pmz_probe(void) #else +/* On PCI PowerMacs, pmz_probe() does an explicit search of the OpenFirmware + * tree to obtain the device_nodes needed to start the console before the + * macio driver. On Macs without OpenFirmware, global platform_devices take + * the place of those device_nodes. + */ extern struct platform_device scc_a_pdev, scc_b_pdev; static int __init pmz_init_port(struct uart_pmac_port *uap) @@ -1719,10 +1622,13 @@ static int __init pmz_init_port(struct uart_pmac_port *uap) int irq; r_ports = platform_get_resource(uap->pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(uap->pdev, 0); - if (!r_ports || irq <= 0) + if (!r_ports) return -ENODEV; + irq = platform_get_irq(uap->pdev, 0); + if (irq < 0) + return irq; + uap->port.mapbase = r_ports->start; uap->port.membase = (unsigned char __iomem *) r_ports->start; uap->port.iotype = UPIO_MEM; @@ -1736,6 +1642,7 @@ static int __init pmz_init_port(struct uart_pmac_port *uap) uap->control_reg = uap->port.membase; uap->data_reg = uap->control_reg + 4; uap->port_type = 0; + uap->port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_PMACZILOG_CONSOLE); pmz_convert_to_zs(uap, CS8, 0, 9600); @@ -1774,7 +1681,7 @@ static void pmz_dispose_port(struct uart_pmac_port *uap) memset(uap, 0, sizeof(struct uart_pmac_port)); } -static int __init pmz_attach(struct platform_device *pdev) +static int pmz_attach(struct platform_device *pdev) { struct uart_pmac_port *uap; int i; @@ -1793,18 +1700,13 @@ static int __init pmz_attach(struct platform_device *pdev) return uart_add_one_port(&pmz_uart_reg, &uap->port); } -static int __exit pmz_detach(struct platform_device *pdev) +static void pmz_detach(struct platform_device *pdev) { struct uart_pmac_port *uap = platform_get_drvdata(pdev); - if (!uap) - return -ENODEV; - uart_remove_one_port(&pmz_uart_reg, &uap->port); uap->port.dev = NULL; - - return 0; } #endif /* !CONFIG_PPC_PMAC */ @@ -1873,7 +1775,8 @@ static struct macio_driver pmz_driver = { #else static struct platform_driver pmz_driver = { - .remove = __exit_p(pmz_detach), + .probe = pmz_attach, + .remove = pmz_detach, .driver = { .name = "scc", }, @@ -1884,7 +1787,6 @@ static struct platform_driver pmz_driver = { static int __init init_pmz(void) { int rc, i; - printk(KERN_INFO "%s\n", version); /* * First, we need to do a direct OF-based probe pass. We @@ -1922,7 +1824,7 @@ static int __init init_pmz(void) #ifdef CONFIG_PPC_PMAC return macio_register_driver(&pmz_driver); #else - return platform_driver_probe(&pmz_driver, pmz_attach); + return platform_driver_register(&pmz_driver); #endif } @@ -1953,7 +1855,7 @@ static void __exit exit_pmz(void) #ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE -static void pmz_console_putchar(struct uart_port *port, int ch) +static void pmz_console_putchar(struct uart_port *port, unsigned char ch) { struct uart_pmac_port *uap = container_of(port, struct uart_pmac_port, port); @@ -1973,7 +1875,7 @@ static void pmz_console_write(struct console *con, const char *s, unsigned int c struct uart_pmac_port *uap = &pmz_ports[con->index]; unsigned long flags; - spin_lock_irqsave(&uap->port.lock, flags); + uart_port_lock_irqsave(&uap->port, &flags); /* Turn of interrupts and enable the transmitter. */ write_zsreg(uap, R1, uap->curregs[1] & ~TxINT_ENAB); @@ -1985,7 +1887,7 @@ static void pmz_console_write(struct console *con, const char *s, unsigned int c write_zsreg(uap, R1, uap->curregs[1]); /* Don't disable the transmitter. */ - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); } /* |
