diff options
Diffstat (limited to 'drivers/tty')
101 files changed, 4341 insertions, 4663 deletions
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index c01f45095877..82c4d2e45319 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -226,7 +226,7 @@ config CYCLADES config CYZ_INTR bool "Cyclades-Z interrupt mode operation" - depends on CYCLADES + depends on CYCLADES && PCI help The Cyclades-Z family of multiport cards allows 2 (two) driver op modes: polling and interrupt. In polling mode, the driver will check diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index e53d9a512c6d..eacf4c9f3b29 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -32,7 +32,6 @@ #include <linux/delay.h> #undef SERIAL_PARANOIA_CHECK -#define SERIAL_DO_RESTART /* Set of debugging defines */ @@ -640,7 +639,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info) custom.adkcon = AC_UARTBRK; mb(); - if (tty->termios.c_cflag & HUPCL) + if (C_HUPCL(tty)) info->MCR &= ~(SER_DTR|SER_RTS); rtsdtr_ctrl(info->MCR); @@ -966,8 +965,7 @@ static void rs_throttle(struct tty_struct * tty) struct serial_state *info = tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE - printk("throttle %s: %d....\n", tty_name(tty), - tty->ldisc.chars_in_buffer(tty)); + printk("throttle %s ....\n", tty_name(tty)); #endif if (serial_paranoia_check(info, tty->name, "rs_throttle")) @@ -976,7 +974,7 @@ static void rs_throttle(struct tty_struct * tty) if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty)); - if (tty->termios.c_cflag & CRTSCTS) + if (C_CRTSCTS(tty)) info->MCR &= ~SER_RTS; local_irq_save(flags); @@ -989,8 +987,7 @@ static void rs_unthrottle(struct tty_struct * tty) struct serial_state *info = tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE - printk("unthrottle %s: %d....\n", tty_name(tty), - tty->ldisc.chars_in_buffer(tty)); + printk("unthrottle %s ....\n", tty_name(tty)); #endif if (serial_paranoia_check(info, tty->name, "rs_unthrottle")) @@ -1002,7 +999,7 @@ static void rs_unthrottle(struct tty_struct * tty) else rs_send_xchar(tty, START_CHAR(tty)); } - if (tty->termios.c_cflag & CRTSCTS) + if (C_CRTSCTS(tty)) info->MCR |= SER_RTS; local_irq_save(flags); rtsdtr_ctrl(info->MCR); @@ -1335,8 +1332,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) change_speed(tty, info, old_termios); /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && - !(cflag & CBAUD)) { + if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) { info->MCR &= ~(SER_DTR|SER_RTS); local_irq_save(flags); rtsdtr_ctrl(info->MCR); @@ -1344,21 +1340,17 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) } /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && - (cflag & CBAUD)) { + if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { info->MCR |= SER_DTR; - if (!(tty->termios.c_cflag & CRTSCTS) || - !test_bit(TTY_THROTTLED, &tty->flags)) { + if (!C_CRTSCTS(tty) || !test_bit(TTY_THROTTLED, &tty->flags)) info->MCR |= SER_RTS; - } local_irq_save(flags); rtsdtr_ctrl(info->MCR); local_irq_restore(flags); } /* Handle turning off CRTSCTS */ - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios.c_cflag & CRTSCTS)) { + if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { tty->hw_stopped = 0; rs_start(tty); } @@ -1370,8 +1362,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) * XXX It's not clear whether the current behavior is correct * or not. Hence, this may change..... */ - if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios.c_cflag & CLOCAL)) + if (!(old_termios->c_cflag & CLOCAL) && C_CLOCAL(tty)) wake_up_interruptible(&info->open_wait); #endif } diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index d4a1331675ed..d67e542bab1c 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -292,14 +292,14 @@ static void cyz_rx_restart(unsigned long); static struct timer_list cyz_rx_full_timer[NR_PORTS]; #endif /* CONFIG_CYZ_INTR */ -static inline void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val) +static void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val) { struct cyclades_card *card = port->card; cy_writeb(port->u.cyy.base_addr + (reg << card->bus_index), val); } -static inline u8 cyy_readb(struct cyclades_port *port, u32 reg) +static u8 cyy_readb(struct cyclades_port *port, u32 reg) { struct cyclades_card *card = port->card; @@ -321,7 +321,7 @@ static inline bool cyz_fpga_loaded(struct cyclades_card *card) return __cyz_fpga_loaded(card->ctl_addr.p9060); } -static inline bool cyz_is_loaded(struct cyclades_card *card) +static bool cyz_is_loaded(struct cyclades_card *card) { struct FIRM_ID __iomem *fw_id = card->base_addr + ID_ADDRESS; @@ -329,7 +329,7 @@ static inline bool cyz_is_loaded(struct cyclades_card *card) readl(&fw_id->signature) == ZFIRM_ID; } -static inline int serial_paranoia_check(struct cyclades_port *info, +static int serial_paranoia_check(struct cyclades_port *info, const char *name, const char *routine) { #ifdef SERIAL_PARANOIA_CHECK @@ -1440,7 +1440,7 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) info->port.xmit_buf = NULL; free_page((unsigned long)temp); } - if (tty->termios.c_cflag & HUPCL) + if (C_HUPCL(tty)) cyy_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR); cyy_issue_cmd(info, CyCHAN_CTL | CyDIS_RCVR); @@ -1469,7 +1469,7 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) free_page((unsigned long)temp); } - if (tty->termios.c_cflag & HUPCL) + if (C_HUPCL(tty)) tty_port_lower_dtr_rts(&info->port); set_bit(TTY_IO_ERROR, &tty->flags); @@ -2795,8 +2795,7 @@ static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) cy_set_line_char(info, tty); - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios.c_cflag & CRTSCTS)) { + if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { tty->hw_stopped = 0; cy_start(tty); } @@ -2807,8 +2806,7 @@ static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) * XXX It's not clear whether the current behavior is correct * or not. Hence, this may change..... */ - if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios.c_cflag & CLOCAL)) + if (!(old_termios->c_cflag & CLOCAL) && C_CLOCAL(tty)) wake_up_interruptible(&info->port.open_wait); #endif } /* cy_set_termios */ @@ -2852,8 +2850,8 @@ static void cy_throttle(struct tty_struct *tty) unsigned long flags; #ifdef CY_DEBUG_THROTTLE - printk(KERN_DEBUG "cyc:throttle %s: %ld...ttyC%d\n", tty_name(tty), - tty->ldisc.chars_in_buffer(tty), info->line); + printk(KERN_DEBUG "cyc:throttle %s ...ttyC%d\n", tty_name(tty), + info->line); #endif if (serial_paranoia_check(info, tty->name, "cy_throttle")) @@ -2868,7 +2866,7 @@ static void cy_throttle(struct tty_struct *tty) info->throttle = 1; } - if (tty->termios.c_cflag & CRTSCTS) { + if (C_CRTSCTS(tty)) { if (!cy_is_Z(card)) { spin_lock_irqsave(&card->card_lock, flags); cyy_change_rts_dtr(info, 0, TIOCM_RTS); @@ -2891,8 +2889,8 @@ static void cy_unthrottle(struct tty_struct *tty) unsigned long flags; #ifdef CY_DEBUG_THROTTLE - printk(KERN_DEBUG "cyc:unthrottle %s: %ld...ttyC%d\n", - tty_name(tty), tty_chars_in_buffer(tty), info->line); + printk(KERN_DEBUG "cyc:unthrottle %s ...ttyC%d\n", + tty_name(tty), info->line); #endif if (serial_paranoia_check(info, tty->name, "cy_unthrottle")) @@ -2905,7 +2903,7 @@ static void cy_unthrottle(struct tty_struct *tty) cy_send_xchar(tty, START_CHAR(tty)); } - if (tty->termios.c_cflag & CRTSCTS) { + if (C_CRTSCTS(tty)) { card = info->card; if (!cy_is_Z(card)) { spin_lock_irqsave(&card->card_lock, flags); diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index 342b36b9ad35..7ac9bcdf1e61 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -23,7 +23,6 @@ * byte channel used for the console is designated as the default tty. */ -#include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/err.h> @@ -719,19 +718,6 @@ error: return ret; } -static int ehv_bc_tty_remove(struct platform_device *pdev) -{ - struct ehv_bc_data *bc = dev_get_drvdata(&pdev->dev); - - tty_unregister_device(ehv_bc_driver, bc - bcs); - - tty_port_destroy(&bc->port); - irq_dispose_mapping(bc->tx_irq); - irq_dispose_mapping(bc->rx_irq); - - return 0; -} - static const struct of_device_id ehv_bc_tty_of_ids[] = { { .compatible = "epapr,hv-byte-channel" }, {} @@ -741,15 +727,15 @@ static struct platform_driver ehv_bc_tty_driver = { .driver = { .name = "ehv-bc", .of_match_table = ehv_bc_tty_of_ids, + .suppress_bind_attrs = true, }, .probe = ehv_bc_tty_probe, - .remove = ehv_bc_tty_remove, }; /** * ehv_bc_init - ePAPR hypervisor byte channel driver initialization * - * This function is called when this module is loaded. + * This function is called when this driver is loaded. */ static int __init ehv_bc_init(void) { @@ -814,24 +800,4 @@ error: return ret; } - - -/** - * ehv_bc_exit - ePAPR hypervisor byte channel driver termination - * - * This function is called when this driver is unloaded. - */ -static void __exit ehv_bc_exit(void) -{ - platform_driver_unregister(&ehv_bc_tty_driver); - tty_unregister_driver(ehv_bc_driver); - put_tty_driver(ehv_bc_driver); - kfree(bcs); -} - -module_init(ehv_bc_init); -module_exit(ehv_bc_exit); - -MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); -MODULE_DESCRIPTION("ePAPR hypervisor byte channel driver"); -MODULE_LICENSE("GPL v2"); +device_initcall(ehv_bc_init); diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index 0f82c0b146f6..3fc912373adf 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -68,8 +68,7 @@ static void goldfish_tty_do_write(int line, const char *buf, unsigned count) static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id) { - struct platform_device *pdev = dev_id; - struct goldfish_tty *qtty = &goldfish_ttys[pdev->id]; + struct goldfish_tty *qtty = dev_id; void __iomem *base = qtty->base; unsigned long irq_flags; unsigned char *buf; @@ -162,7 +161,7 @@ static int goldfish_tty_console_setup(struct console *co, char *options) return 0; } -static struct tty_port_operations goldfish_port_ops = { +static const struct tty_port_operations goldfish_port_ops = { .activate = goldfish_tty_activate, .shutdown = goldfish_tty_shutdown }; @@ -233,6 +232,7 @@ static int goldfish_tty_probe(struct platform_device *pdev) struct device *ttydev; void __iomem *base; u32 irq; + unsigned int line; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) @@ -248,10 +248,16 @@ static int goldfish_tty_probe(struct platform_device *pdev) irq = r->start; - if (pdev->id >= goldfish_tty_line_count) - goto err_unmap; - mutex_lock(&goldfish_tty_lock); + + if (pdev->id == PLATFORM_DEVID_NONE) + line = goldfish_tty_current_line_count; + else + line = pdev->id; + + if (line >= goldfish_tty_line_count) + goto err_create_driver_failed; + if (goldfish_tty_current_line_count == 0) { ret = goldfish_tty_create_driver(); if (ret) @@ -259,7 +265,7 @@ static int goldfish_tty_probe(struct platform_device *pdev) } goldfish_tty_current_line_count++; - qtty = &goldfish_ttys[pdev->id]; + qtty = &goldfish_ttys[line]; spin_lock_init(&qtty->lock); tty_port_init(&qtty->port); qtty->port.ops = &goldfish_port_ops; @@ -269,13 +275,13 @@ static int goldfish_tty_probe(struct platform_device *pdev) writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_CMD); ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED, - "goldfish_tty", pdev); + "goldfish_tty", qtty); if (ret) goto err_request_irq_failed; ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver, - pdev->id, &pdev->dev); + line, &pdev->dev); if (IS_ERR(ttydev)) { ret = PTR_ERR(ttydev); goto err_tty_register_device_failed; @@ -286,8 +292,9 @@ static int goldfish_tty_probe(struct platform_device *pdev) qtty->console.device = goldfish_tty_console_device; qtty->console.setup = goldfish_tty_console_setup; qtty->console.flags = CON_PRINTBUFFER; - qtty->console.index = pdev->id; + qtty->console.index = line; register_console(&qtty->console); + platform_set_drvdata(pdev, qtty); mutex_unlock(&goldfish_tty_lock); return 0; @@ -307,13 +314,12 @@ err_unmap: static int goldfish_tty_remove(struct platform_device *pdev) { - struct goldfish_tty *qtty; + struct goldfish_tty *qtty = platform_get_drvdata(pdev); mutex_lock(&goldfish_tty_lock); - qtty = &goldfish_ttys[pdev->id]; unregister_console(&qtty->console); - tty_unregister_device(goldfish_tty_driver, pdev->id); + tty_unregister_device(goldfish_tty_driver, qtty->console.index); iounmap(qtty->base); qtty->base = NULL; free_irq(qtty->irq, pdev); @@ -324,11 +330,19 @@ static int goldfish_tty_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id goldfish_tty_of_match[] = { + { .compatible = "google,goldfish-tty", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, goldfish_tty_of_match); + static struct platform_driver goldfish_tty_platform_driver = { .probe = goldfish_tty_probe, .remove = goldfish_tty_remove, .driver = { - .name = "goldfish_tty" + .name = "goldfish_tty", + .of_match_table = goldfish_tty_of_match, } }; diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index f575a9b5ede7..b05dc5086627 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c @@ -41,7 +41,6 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/console.h> -#include <linux/module.h> #include <asm/hvconsole.h> #include <asm/vio.h> @@ -61,7 +60,6 @@ static struct vio_device_id hvc_driver_table[] = { #endif { "", "" } }; -MODULE_DEVICE_TABLE(vio, hvc_driver_table); typedef enum hv_protocol { HV_PROTOCOL_RAW, @@ -363,26 +361,13 @@ static int hvc_vio_probe(struct vio_dev *vdev, return 0; } -static int hvc_vio_remove(struct vio_dev *vdev) -{ - struct hvc_struct *hp = dev_get_drvdata(&vdev->dev); - int rc, termno; - - termno = hp->vtermno; - rc = hvc_remove(hp); - if (rc == 0) { - if (hvterm_privs[termno] != &hvterm_priv0) - kfree(hvterm_privs[termno]); - hvterm_privs[termno] = NULL; - } - return rc; -} - static struct vio_driver hvc_vio_driver = { .id_table = hvc_driver_table, .probe = hvc_vio_probe, - .remove = hvc_vio_remove, .name = hvc_driver_name, + .driver = { + .suppress_bind_attrs = true, + }, }; static int __init hvc_vio_init(void) @@ -394,13 +379,7 @@ static int __init hvc_vio_init(void) return rc; } -module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */ - -static void __exit hvc_vio_exit(void) -{ - vio_unregister_driver(&hvc_vio_driver); -} -module_exit(hvc_vio_exit); +device_initcall(hvc_vio_init); /* after drivers/tty/hvc/hvc_console.c */ void __init hvc_vio_init_early(void) { diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index fa816b7193b6..f417fa1ee47c 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -162,7 +162,7 @@ static int domU_read_console(uint32_t vtermno, char *buf, int len) return recv; } -static struct hv_ops domU_hvc_ops = { +static const struct hv_ops domU_hvc_ops = { .get_chars = domU_read_console, .put_chars = domU_write_console, .notifier_add = notifier_add_irq, @@ -188,7 +188,7 @@ static int dom0_write_console(uint32_t vtermno, const char *str, int len) return len; } -static struct hv_ops dom0_hvc_ops = { +static const struct hv_ops dom0_hvc_ops = { .get_chars = dom0_read_console, .put_chars = dom0_write_console, .notifier_add = notifier_add_irq, @@ -323,6 +323,7 @@ void xen_console_resume(void) } } +#ifdef CONFIG_HVC_XEN_FRONTEND static void xencons_disconnect_backend(struct xencons_info *info) { if (info->irq > 0) @@ -363,7 +364,6 @@ static int xen_console_remove(struct xencons_info *info) return 0; } -#ifdef CONFIG_HVC_XEN_FRONTEND static int xencons_remove(struct xenbus_device *dev) { return xen_console_remove(dev_get_drvdata(&dev->dev)); diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index 2054427992e0..8bf67630018b 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -220,7 +220,7 @@ static struct isi_port isi_ports[PORT_COUNT]; * it wants to talk. */ -static inline int WaitTillCardIsFree(unsigned long base) +static int WaitTillCardIsFree(unsigned long base) { unsigned int count = 0; unsigned int a = in_atomic(); /* do we run under spinlock? */ @@ -280,7 +280,7 @@ static void raise_dtr(struct isi_port *port) } /* card->lock HAS to be held */ -static inline void drop_dtr(struct isi_port *port) +static void drop_dtr(struct isi_port *port) { struct isi_board *card = port->card; unsigned long base = card->base; @@ -1204,8 +1204,7 @@ static void isicom_set_termios(struct tty_struct *tty, isicom_config_port(tty); spin_unlock_irqrestore(&port->card->card_lock, flags); - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios.c_cflag & CRTSCTS)) { + if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { tty->hw_stopped = 0; isicom_start(tty); } diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 14c54e041065..92982d7c0489 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -155,7 +155,6 @@ struct mon_str { #define LOWWAIT 2 #define EMPTYWAIT 3 -#define SERIAL_DO_RESTART #define WAKEUP_CHARS 256 diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 4c4a23674569..2f12bb9f4336 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -254,6 +254,7 @@ struct mxser_port { int xmit_head; int xmit_tail; int xmit_cnt; + int closing; struct ktermios normal_termios; @@ -1081,6 +1082,7 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) return; if (tty_port_close_start(port, tty, filp) == 0) return; + info->closing = 1; mutex_lock(&port->mutex); mxser_close_port(port); mxser_flush_buffer(tty); @@ -1091,6 +1093,7 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) mxser_shutdown_port(port); clear_bit(ASYNCB_INITIALIZED, &port->flags); mutex_unlock(&port->mutex); + info->closing = 0; /* Right now the tty_port set is done outside of the close_end helper as we don't yet have everyone using refcounts */ tty_port_close_end(port, tty); @@ -1864,7 +1867,7 @@ static void mxser_stoprx(struct tty_struct *tty) } } - if (tty->termios.c_cflag & CRTSCTS) { + if (C_CRTSCTS(tty)) { info->MCR &= ~UART_MCR_RTS; outb(info->MCR, info->ioaddr + UART_MCR); } @@ -1901,7 +1904,7 @@ static void mxser_unthrottle(struct tty_struct *tty) } } - if (tty->termios.c_cflag & CRTSCTS) { + if (C_CRTSCTS(tty)) { info->MCR |= UART_MCR_RTS; outb(info->MCR, info->ioaddr + UART_MCR); } @@ -1949,15 +1952,13 @@ static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termi mxser_change_speed(tty, old_termios); spin_unlock_irqrestore(&info->slock, flags); - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios.c_cflag & CRTSCTS)) { + if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { tty->hw_stopped = 0; mxser_start(tty); } /* Handle sw stopped */ - if ((old_termios->c_iflag & IXON) && - !(tty->termios.c_iflag & IXON)) { + if ((old_termios->c_iflag & IXON) && !I_IXON(tty)) { tty->stopped = 0; if (info->board->chip_flag) { @@ -2255,10 +2256,8 @@ static irqreturn_t mxser_interrupt(int irq, void *dev_id) break; iir &= MOXA_MUST_IIR_MASK; tty = tty_port_tty_get(&port->port); - if (!tty || - (port->port.flags & ASYNC_CLOSING) || - !(port->port.flags & - ASYNC_INITIALIZED)) { + if (!tty || port->closing || + !(port->port.flags & ASYNC_INITIALIZED)) { status = inb(port->ioaddr + UART_LSR); outb(0x27, port->ioaddr + UART_FCR); inb(port->ioaddr + UART_MSR); @@ -2337,7 +2336,7 @@ static const struct tty_operations mxser_ops = { .get_icount = mxser_get_icount, }; -static struct tty_port_operations mxser_port_ops = { +static const struct tty_port_operations mxser_port_ops = { .carrier_raised = mxser_carrier_raised, .dtr_rts = mxser_dtr_rts, .activate = mxser_activate, diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index c3fe026d3168..c01620780f5b 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -1066,7 +1066,7 @@ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci, /* Carrier drop -> hangup */ if (tty) { if ((mlines & TIOCM_CD) == 0 && (dlci->modem_rx & TIOCM_CD)) - if (!(tty->termios.c_cflag & CLOCAL)) + if (!C_CLOCAL(tty)) tty_hangup(tty); } if (brk & 0x01) @@ -2304,21 +2304,6 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, } /** - * gsmld_chars_in_buffer - report available bytes - * @tty: tty device - * - * Report the number of characters buffered to be delivered to user - * at this instant in time. - * - * Locking: gsm lock - */ - -static ssize_t gsmld_chars_in_buffer(struct tty_struct *tty) -{ - return 0; -} - -/** * gsmld_flush_buffer - clean input queue * @tty: terminal device * @@ -2830,7 +2815,6 @@ static struct tty_ldisc_ops tty_ldisc_packet = { .open = gsmld_open, .close = gsmld_close, .flush_buffer = gsmld_flush_buffer, - .chars_in_buffer = gsmld_chars_in_buffer, .read = gsmld_read, .write = gsmld_write, .ioctl = gsmld_ioctl, @@ -3132,7 +3116,7 @@ static void gsmtty_throttle(struct tty_struct *tty) struct gsm_dlci *dlci = tty->driver_data; if (dlci->state == DLCI_CLOSED) return; - if (tty->termios.c_cflag & CRTSCTS) + if (C_CRTSCTS(tty)) dlci->modem_tx &= ~TIOCM_DTR; dlci->throttled = 1; /* Send an MSC with DTR cleared */ @@ -3144,7 +3128,7 @@ static void gsmtty_unthrottle(struct tty_struct *tty) struct gsm_dlci *dlci = tty->driver_data; if (dlci->state == DLCI_CLOSED) return; - if (tty->termios.c_cflag & CRTSCTS) + if (C_CRTSCTS(tty)) dlci->modem_tx |= TIOCM_DTR; dlci->throttled = 0; /* Send an MSC with DTR set */ diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index bbc4ce66c2c1..bcaba17688f6 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -159,7 +159,6 @@ struct n_hdlc { /* * HDLC buffer list manipulation functions */ -static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list); static void n_hdlc_buf_put(struct n_hdlc_buf_list *list, struct n_hdlc_buf *buf); static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list); @@ -853,10 +852,10 @@ static struct n_hdlc *n_hdlc_alloc(void) if (!n_hdlc) return NULL; - n_hdlc_buf_list_init(&n_hdlc->rx_free_buf_list); - n_hdlc_buf_list_init(&n_hdlc->tx_free_buf_list); - n_hdlc_buf_list_init(&n_hdlc->rx_buf_list); - n_hdlc_buf_list_init(&n_hdlc->tx_buf_list); + spin_lock_init(&n_hdlc->rx_free_buf_list.spinlock); + spin_lock_init(&n_hdlc->tx_free_buf_list.spinlock); + spin_lock_init(&n_hdlc->rx_buf_list.spinlock); + spin_lock_init(&n_hdlc->tx_buf_list.spinlock); /* allocate free rx buffer list */ for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) { @@ -885,16 +884,6 @@ static struct n_hdlc *n_hdlc_alloc(void) } /* end of n_hdlc_alloc() */ /** - * n_hdlc_buf_list_init - initialize specified HDLC buffer list - * @list - pointer to buffer list - */ -static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list) -{ - memset(list, 0, sizeof(*list)); - spin_lock_init(&list->spinlock); -} /* end of n_hdlc_buf_list_init() */ - -/** * n_hdlc_buf_put - add specified HDLC buffer to tail of specified list * @list - pointer to buffer list * @buf - pointer to buffer diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index e49c2bce551d..fb76a7d80e7e 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -113,8 +113,6 @@ struct n_tty_data { DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE); unsigned char echo_buf[N_TTY_BUF_SIZE]; - int minimum_to_wake; - /* consumer-published */ size_t read_tail; size_t line_start; @@ -153,23 +151,25 @@ static inline unsigned char *echo_buf_addr(struct n_tty_data *ldata, size_t i) return &ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; } -static inline int tty_put_user(struct tty_struct *tty, unsigned char x, - unsigned char __user *ptr) +static int tty_copy_to_user(struct tty_struct *tty, void __user *to, + size_t tail, size_t n) { struct n_tty_data *ldata = tty->disc_data; + size_t size = N_TTY_BUF_SIZE - tail; + const void *from = read_buf_addr(ldata, tail); + int uncopied; - tty_audit_add_data(tty, &x, 1, ldata->icanon); - return put_user(x, ptr); -} - -static inline int tty_copy_to_user(struct tty_struct *tty, - void __user *to, - const void *from, - unsigned long n) -{ - struct n_tty_data *ldata = tty->disc_data; + if (n > size) { + tty_audit_add_data(tty, from, size); + uncopied = copy_to_user(to, from, size); + if (uncopied) + return uncopied; + to += size; + n -= size; + from = ldata->read_buf; + } - tty_audit_add_data(tty, from, n, ldata->icanon); + tty_audit_add_data(tty, from, n); return copy_to_user(to, from, n); } @@ -228,8 +228,8 @@ static ssize_t chars_in_buffer(struct tty_struct *tty) static void n_tty_write_wakeup(struct tty_struct *tty) { - if (tty->fasync && test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) - kill_fasync(&tty->fasync, SIGIO, POLL_OUT); + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + kill_fasync(&tty->fasync, SIGIO, POLL_OUT); } static void n_tty_check_throttle(struct tty_struct *tty) @@ -258,16 +258,11 @@ static void n_tty_check_throttle(struct tty_struct *tty) static void n_tty_check_unthrottle(struct tty_struct *tty) { - if (tty->driver->type == TTY_DRIVER_TYPE_PTY && - tty->link->ldisc->ops->write_wakeup == n_tty_write_wakeup) { + if (tty->driver->type == TTY_DRIVER_TYPE_PTY) { if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) return; - if (!tty->count) - return; n_tty_kick_worker(tty); - n_tty_write_wakeup(tty->link); - if (waitqueue_active(&tty->link->write_wait)) - wake_up_interruptible_poll(&tty->link->write_wait, POLLOUT); + tty_wakeup(tty->link); return; } @@ -284,8 +279,6 @@ static void n_tty_check_unthrottle(struct tty_struct *tty) tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE); if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) break; - if (!tty->count) - break; n_tty_kick_worker(tty); unthrottled = tty_unthrottle_safe(tty); if (!unthrottled) @@ -373,28 +366,6 @@ static void n_tty_flush_buffer(struct tty_struct *tty) } /** - * n_tty_chars_in_buffer - report available bytes - * @tty: tty device - * - * Report the number of characters buffered to be delivered to user - * at this instant in time. - * - * Locking: exclusive termios_rwsem - */ - -static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) -{ - ssize_t n; - - WARN_ONCE(1, "%s is deprecated and scheduled for removal.", __func__); - - down_write(&tty->termios_rwsem); - n = chars_in_buffer(tty); - up_write(&tty->termios_rwsem); - return n; -} - -/** * is_utf8_continuation - utf8 multibyte check * @c: byte to check * @@ -1201,9 +1172,7 @@ static void n_tty_receive_overrun(struct tty_struct *tty) ldata->num_overrun++; if (time_after(jiffies, ldata->overrun_time + HZ) || time_after(ldata->overrun_time, jiffies)) { - printk(KERN_WARNING "%s: %d input overrun(s)\n", - tty_name(tty), - ldata->num_overrun); + tty_warn(tty, "%d input overrun(s)\n", ldata->num_overrun); ldata->overrun_time = jiffies; ldata->num_overrun = 0; } @@ -1486,8 +1455,7 @@ n_tty_receive_char_flagged(struct tty_struct *tty, unsigned char c, char flag) n_tty_receive_overrun(tty); break; default: - printk(KERN_ERR "%s: unknown flag %d\n", - tty_name(tty), flag); + tty_err(tty, "unknown flag %d\n", flag); break; } } @@ -1556,8 +1524,6 @@ n_tty_receive_buf_closing(struct tty_struct *tty, const unsigned char *cp, flag = *fp++; if (likely(flag == TTY_NORMAL)) n_tty_receive_char_closing(tty, *cp++); - else - n_tty_receive_char_flagged(tty, *cp++, flag); } } @@ -1659,7 +1625,7 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, /* publish read_head to consumer */ smp_store_release(&ldata->commit_head, ldata->read_head); - if ((read_cnt(ldata) >= ldata->minimum_to_wake) || L_EXTPROC(tty)) { + if (read_cnt(ldata)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); wake_up_interruptible_poll(&tty->read_wait, POLLIN); } @@ -1780,12 +1746,6 @@ static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp, return n_tty_receive_buf_common(tty, cp, fp, count, 1); } -int is_ignored(int sig) -{ - return (sigismember(¤t->blocked, sig) || - current->sighand->action[sig-1].sa.sa_handler == SIG_IGN); -} - /** * n_tty_set_termios - termios data changed * @tty: terminal @@ -1932,7 +1892,6 @@ static int n_tty_open(struct tty_struct *tty) reset_buffer_flags(tty->disc_data); ldata->column = 0; ldata->canon_column = 0; - ldata->minimum_to_wake = 1; ldata->num_overrun = 0; ldata->no_room = 0; ldata->lnext = 0; @@ -2006,11 +1965,11 @@ static int copy_from_read_buf(struct tty_struct *tty, n = min(head - ldata->read_tail, N_TTY_BUF_SIZE - tail); n = min(*nr, n); if (n) { - retval = copy_to_user(*b, read_buf_addr(ldata, tail), n); + const unsigned char *from = read_buf_addr(ldata, tail); + retval = copy_to_user(*b, from, n); n -= retval; - is_eof = n == 1 && read_buf(ldata, tail) == EOF_CHAR(tty); - tty_audit_add_data(tty, read_buf_addr(ldata, tail), n, - ldata->icanon); + is_eof = n == 1 && *from == EOF_CHAR(tty); + tty_audit_add_data(tty, from, n); smp_store_release(&ldata->read_tail, ldata->read_tail + n); /* Turn single EOF into zero-length read */ if (L_EXTPROC(tty) && ldata->icanon && is_eof && @@ -2072,12 +2031,10 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, if (eol == N_TTY_BUF_SIZE && more) { /* scan wrapped without finding set bit */ eol = find_next_bit(ldata->read_flags, more, 0); - if (eol != more) - found = 1; - } else if (eol != size) - found = 1; + found = eol != more; + } else + found = eol != size; - size = N_TTY_BUF_SIZE - tail; n = eol - tail; if (n > N_TTY_BUF_SIZE) n += N_TTY_BUF_SIZE; @@ -2088,17 +2045,10 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, n = c; } - n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu size:%zu more:%zu\n", - __func__, eol, found, n, c, size, more); - - if (n > size) { - ret = tty_copy_to_user(tty, *b, read_buf_addr(ldata, tail), size); - if (ret) - return -EFAULT; - ret = tty_copy_to_user(tty, *b + size, ldata->read_buf, n - size); - } else - ret = tty_copy_to_user(tty, *b, read_buf_addr(ldata, tail), n); + n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu tail:%zu more:%zu\n", + __func__, eol, found, n, c, tail, more); + ret = tty_copy_to_user(tty, *b, tail, n); if (ret) return -EFAULT; *b += n; @@ -2113,7 +2063,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, ldata->line_start = ldata->read_tail; else ldata->push = 0; - tty_audit_push(tty); + tty_audit_push(); } return 0; } @@ -2204,14 +2154,9 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, minimum = MIN_CHAR(tty); if (minimum) { time = (HZ / 10) * TIME_CHAR(tty); - if (time) - ldata->minimum_to_wake = 1; - else if (!waitqueue_active(&tty->read_wait) || - (ldata->minimum_to_wake > minimum)) - ldata->minimum_to_wake = minimum; } else { timeout = (HZ / 10) * TIME_CHAR(tty); - ldata->minimum_to_wake = minimum = 1; + minimum = 1; } } @@ -2229,19 +2174,15 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, cs = tty->link->ctrl_status; tty->link->ctrl_status = 0; spin_unlock_irq(&tty->link->ctrl_lock); - if (tty_put_user(tty, cs, b++)) { + if (put_user(cs, b)) { retval = -EFAULT; - b--; break; } + b++; nr--; break; } - if (((minimum - (b - buf)) < ldata->minimum_to_wake) && - ((minimum - (b - buf)) >= 1)) - ldata->minimum_to_wake = (minimum - (b - buf)); - done = check_other_done(tty); if (!input_available_p(tty, 0)) { @@ -2279,11 +2220,11 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, /* Deal with packet mode. */ if (packet && b == buf) { - if (tty_put_user(tty, TIOCPKT_DATA, b++)) { + if (put_user(TIOCPKT_DATA, b)) { retval = -EFAULT; - b--; break; } + b++; nr--; } @@ -2307,9 +2248,6 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, up_read(&tty->termios_rwsem); remove_wait_queue(&tty->read_wait, &wait); - if (!waitqueue_active(&tty->read_wait)) - ldata->minimum_to_wake = minimum; - mutex_unlock(&ldata->atomic_read_lock); if (b - buf) @@ -2421,7 +2359,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, } break_out: remove_wait_queue(&tty->write_wait, &wait); - if (b - buf != nr && tty->fasync) + if (nr && tty->fasync) set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); up_read(&tty->termios_rwsem); return (b - buf) ? b - buf : retval; @@ -2444,7 +2382,6 @@ break_out: static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) { - struct n_tty_data *ldata = tty->disc_data; unsigned int mask = 0; poll_wait(file, &tty->read_wait, wait); @@ -2457,12 +2394,6 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, mask |= POLLPRI | POLLIN | POLLRDNORM; if (tty_hung_up_p(file)) mask |= POLLHUP; - if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) { - if (MIN_CHAR(tty) && !TIME_CHAR(tty)) - ldata->minimum_to_wake = MIN_CHAR(tty); - else - ldata->minimum_to_wake = 1; - } if (tty->ops->write && !tty_is_writelocked(tty) && tty_chars_in_buffer(tty) < WAKEUP_CHARS && tty_write_room(tty) > 0) @@ -2511,25 +2442,12 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file, } } -static void n_tty_fasync(struct tty_struct *tty, int on) -{ - struct n_tty_data *ldata = tty->disc_data; - - if (!waitqueue_active(&tty->read_wait)) { - if (on) - ldata->minimum_to_wake = 1; - else if (!tty->fasync) - ldata->minimum_to_wake = N_TTY_BUF_SIZE; - } -} - -struct tty_ldisc_ops tty_ldisc_N_TTY = { +static struct tty_ldisc_ops n_tty_ops = { .magic = TTY_LDISC_MAGIC, .name = "n_tty", .open = n_tty_open, .close = n_tty_close, .flush_buffer = n_tty_flush_buffer, - .chars_in_buffer = n_tty_chars_in_buffer, .read = n_tty_read, .write = n_tty_write, .ioctl = n_tty_ioctl, @@ -2537,7 +2455,6 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = { .poll = n_tty_poll, .receive_buf = n_tty_receive_buf, .write_wakeup = n_tty_write_wakeup, - .fasync = n_tty_fasync, .receive_buf2 = n_tty_receive_buf2, }; @@ -2545,14 +2462,18 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = { * n_tty_inherit_ops - inherit N_TTY methods * @ops: struct tty_ldisc_ops where to save N_TTY methods * - * Enables a 'subclass' line discipline to 'inherit' N_TTY - * methods. + * Enables a 'subclass' line discipline to 'inherit' N_TTY methods. */ void n_tty_inherit_ops(struct tty_ldisc_ops *ops) { - *ops = tty_ldisc_N_TTY; + *ops = n_tty_ops; ops->owner = NULL; ops->refcount = ops->flags = 0; } EXPORT_SYMBOL_GPL(n_tty_inherit_ops); + +void __init n_tty_init(void) +{ + tty_register_ldisc(N_TTY, &n_tty_ops); +} diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 80f9de907563..5cc80b80c82b 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -823,7 +823,7 @@ static int receive_data(enum port_type index, struct nozomi *dc) struct tty_struct *tty = tty_port_tty_get(&port->port); int i, ret; - read_mem32((u32 *) &size, addr, 4); + size = __le32_to_cpu(readl(addr)); /* DBG1( "%d bytes port: %d", size, index); */ if (tty && test_bit(TTY_THROTTLED, &tty->flags)) { diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index a45660f62db5..e16a49b507ef 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -263,8 +263,7 @@ static void pty_set_termios(struct tty_struct *tty, { /* See if packet mode change of state. */ if (tty->link && tty->link->packet) { - int extproc = (old_termios->c_lflag & EXTPROC) | - (tty->termios.c_lflag & EXTPROC); + int extproc = (old_termios->c_lflag & EXTPROC) | L_EXTPROC(tty); int old_flow = ((old_termios->c_iflag & IXON) && (old_termios->c_cc[VSTOP] == '\023') && (old_termios->c_cc[VSTART] == '\021')); @@ -406,13 +405,8 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, if (legacy) { /* We always use new tty termios data so we can do this the easy way .. */ - retval = tty_init_termios(tty); - if (retval) - goto err_deinit_tty; - - retval = tty_init_termios(o_tty); - if (retval) - goto err_free_termios; + tty_init_termios(tty); + tty_init_termios(o_tty); driver->other->ttys[idx] = o_tty; driver->ttys[idx] = tty; @@ -444,12 +438,7 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, tty->count++; o_tty->count++; return 0; -err_free_termios: - if (legacy) - tty_free_termios(tty); -err_deinit_tty: - deinitialize_tty_struct(o_tty); - free_tty_struct(o_tty); + err_put_module: module_put(driver->other->owner); err: @@ -666,22 +655,22 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver, return tty; } -/* We have no need to install and remove our tty objects as devpts does all - the work for us */ - static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty) { return pty_common_install(driver, tty, false); } +/* this is called once with whichever end is closed last */ static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) { -} + struct inode *ptmx_inode; -/* this is called once with whichever end is closed last */ -static void pty_unix98_shutdown(struct tty_struct *tty) -{ - devpts_kill_index(tty->driver_data, tty->index); + if (tty->driver->subtype == PTY_TYPE_MASTER) + ptmx_inode = tty->driver_data; + else + ptmx_inode = tty->link->driver_data; + devpts_kill_index(ptmx_inode, tty->index); + devpts_del_ref(ptmx_inode); } static const struct tty_operations ptm_unix98_ops = { @@ -697,7 +686,6 @@ static const struct tty_operations ptm_unix98_ops = { .unthrottle = pty_unthrottle, .ioctl = pty_unix98_ioctl, .resize = pty_resize, - .shutdown = pty_unix98_shutdown, .cleanup = pty_cleanup }; @@ -715,7 +703,6 @@ static const struct tty_operations pty_unix98_ops = { .set_termios = pty_set_termios, .start = pty_start, .stop = pty_stop, - .shutdown = pty_unix98_shutdown, .cleanup = pty_cleanup, }; @@ -773,6 +760,18 @@ static int ptmx_open(struct inode *inode, struct file *filp) set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ tty->driver_data = inode; + /* + * In the case where all references to ptmx inode are dropped and we + * still have /dev/tty opened pointing to the master/slave pair (ptmx + * is closed/released before /dev/tty), we must make sure that the inode + * is still valid when we call the final pty_unix98_shutdown, thus we + * hold an additional reference to the ptmx inode. For the same /dev/tty + * last close case, we also need to make sure the super_block isn't + * destroyed (devpts instance unmounted), before /dev/tty is closed and + * on its release devpts_kill_index is called. + */ + devpts_add_ref(inode); + tty_add_file(tty, filp); slave_inode = devpts_pty_new(inode, @@ -788,7 +787,7 @@ static int ptmx_open(struct inode *inode, struct file *filp) if (retval) goto err_release; - tty_debug_hangup(tty, "(tty count=%d)\n", tty->count); + tty_debug_hangup(tty, "opening (count=%d)\n", tty->count); tty_unlock(tty); return 0; diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index 802eac7e561b..0b802cdd70d0 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -643,7 +643,6 @@ static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) info->chan = chan; tty_port_init(&info->port); info->port.ops = &rocket_port_ops; - init_completion(&info->close_wait); info->flags &= ~ROCKET_MODE_MASK; switch (pc104[board][line]) { case 422: @@ -960,7 +959,7 @@ static int rp_open(struct tty_struct *tty, struct file *filp) tty->alt_speed = 460800; configure_r_port(tty, info, NULL); - if (tty->termios.c_cflag & CBAUD) { + if (C_BAUD(tty)) { sSetDTR(cp); sSetRTS(cp); } @@ -1043,13 +1042,12 @@ static void rp_close(struct tty_struct *tty, struct file *filp) } } spin_lock_irq(&port->lock); - info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING | ASYNC_NORMAL_ACTIVE); + info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_NORMAL_ACTIVE); tty->closing = 0; spin_unlock_irq(&port->lock); mutex_unlock(&port->mutex); tty_port_tty_set(port, NULL); - complete_all(&info->close_wait); atomic_dec(&rp_num_ports_open); #ifdef ROCKET_DEBUG_OPEN @@ -1086,18 +1084,18 @@ static void rp_set_termios(struct tty_struct *tty, cp = &info->channel; /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && !(tty->termios.c_cflag & CBAUD)) { + if ((old_termios->c_cflag & CBAUD) && !C_BAUD(tty)) { sClrDTR(cp); sClrRTS(cp); } /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && (tty->termios.c_cflag & CBAUD)) { + if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) { sSetRTS(cp); sSetDTR(cp); } - if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios.c_cflag & CRTSCTS)) + if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) rp_start(tty); } @@ -1360,8 +1358,7 @@ static void rp_throttle(struct tty_struct *tty) struct r_port *info = tty->driver_data; #ifdef ROCKET_DEBUG_THROTTLE - printk(KERN_INFO "throttle %s: %d....\n", tty->name, - tty->ldisc.chars_in_buffer(tty)); + printk(KERN_INFO "throttle %s ....\n", tty->name); #endif if (rocket_paranoia_check(info, "rp_throttle")) @@ -1377,8 +1374,7 @@ static void rp_unthrottle(struct tty_struct *tty) { struct r_port *info = tty->driver_data; #ifdef ROCKET_DEBUG_THROTTLE - printk(KERN_INFO "unthrottle %s: %d....\n", tty->name, - tty->ldisc.chars_in_buffer(tty)); + printk(KERN_INFO "unthrottle %s ....\n", tty->name); #endif if (rocket_paranoia_check(info, "rp_unthrottle")) diff --git a/drivers/tty/rocket_int.h b/drivers/tty/rocket_int.h index 67e0f1e778a2..ef1e1be6b26d 100644 --- a/drivers/tty/rocket_int.h +++ b/drivers/tty/rocket_int.h @@ -1144,7 +1144,6 @@ struct r_port { int read_status_mask; int cps; - struct completion close_wait; /* Not yet matching the core */ spinlock_t slock; struct mutex write_mtx; }; diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c deleted file mode 100644 index 0140ba4aacde..000000000000 --- a/drivers/tty/serial/68328serial.c +++ /dev/null @@ -1,1322 +0,0 @@ -/* 68328serial.c: Serial port driver for 68328 microcontroller - * - * Copyright (C) 1995 David S. Miller <davem@caip.rutgers.edu> - * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com> - * Copyright (C) 1998, 1999 D. Jeff Dionne <jeff@uclinux.org> - * Copyright (C) 1999 Vladimir Gurevich <vgurevic@cisco.com> - * Copyright (C) 2002-2003 David McCullough <davidm@snapgear.com> - * Copyright (C) 2002 Greg Ungerer <gerg@snapgear.com> - * - * VZ Support/Fixes Evan Stawnyczy <e@lineo.ca> - * Multiple UART support Daniel Potts <danielp@cse.unsw.edu.au> - * Power management support Daniel Potts <danielp@cse.unsw.edu.au> - * VZ Second Serial Port enable Phil Wilshire - * 2.4/2.5 port David McCullough - */ - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/serial.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/major.h> -#include <linux/string.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/kernel.h> -#include <linux/console.h> -#include <linux/reboot.h> -#include <linux/keyboard.h> -#include <linux/init.h> -#include <linux/pm.h> -#include <linux/bitops.h> -#include <linux/delay.h> -#include <linux/gfp.h> - -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/delay.h> -#include <asm/uaccess.h> - -/* (es) */ -/* note: perhaps we can murge these files, so that you can just - * define 1 of them, and they can sort that out for themselves - */ -#if defined(CONFIG_M68EZ328) -#include <asm/MC68EZ328.h> -#else -#if defined(CONFIG_M68VZ328) -#include <asm/MC68VZ328.h> -#else -#include <asm/MC68328.h> -#endif /* CONFIG_M68VZ328 */ -#endif /* CONFIG_M68EZ328 */ - -/* Turn off usage of real serial interrupt code, to "support" Copilot */ -#ifdef CONFIG_XCOPILOT_BUGS -#undef USE_INTS -#else -#define USE_INTS -#endif - -/* - * I believe this is the optimal setting that reduces the number of interrupts. - * At high speeds the output might become a little "bursted" (use USTCNT_TXHE - * if that bothers you), but in most cases it will not, since we try to - * transmit characters every time rs_interrupt is called. Thus, quite often - * you'll see that a receive interrupt occures before the transmit one. - * -- Vladimir Gurevich - */ -#define USTCNT_TX_INTR_MASK (USTCNT_TXEE) - -/* - * 68328 and 68EZ328 UARTS are a little bit different. EZ328 has special - * "Old data interrupt" which occures whenever the data stay in the FIFO - * longer than 30 bits time. This allows us to use FIFO without compromising - * latency. '328 does not have this feature and without the real 328-based - * board I would assume that RXRE is the safest setting. - * - * For EZ328 I use RXHE (Half empty) interrupt to reduce the number of - * interrupts. RXFE (receive queue full) causes the system to lose data - * at least at 115200 baud - * - * If your board is busy doing other stuff, you might consider to use - * RXRE (data ready intrrupt) instead. - * - * The other option is to make these INTR masks run-time configurable, so - * that people can dynamically adapt them according to the current usage. - * -- Vladimir Gurevich - */ - -/* (es) */ -#if defined(CONFIG_M68EZ328) || defined(CONFIG_M68VZ328) -#define USTCNT_RX_INTR_MASK (USTCNT_RXHE | USTCNT_ODEN) -#elif defined(CONFIG_M68328) -#define USTCNT_RX_INTR_MASK (USTCNT_RXRE) -#else -#error Please, define the Rx interrupt events for your CPU -#endif -/* (/es) */ - -/* - * This is our internal structure for each serial port's state. - */ -struct m68k_serial { - struct tty_port tport; - char is_cons; /* Is this our console. */ - int magic; - int baud_base; - int port; - int irq; - int type; /* UART type */ - int custom_divisor; - int x_char; /* xon/xoff character */ - int line; - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; -}; - -#define SERIAL_MAGIC 0x5301 - -/* - * Define the number of ports supported and their irqs. - */ -#define NR_PORTS 1 - -static struct m68k_serial m68k_soft[NR_PORTS]; - -static unsigned int uart_irqs[NR_PORTS] = { UART_IRQ_NUM }; - -/* multiple ports are contiguous in memory */ -m68328_uart *uart_addr = (m68328_uart *)USTCNT_ADDR; - -struct tty_driver *serial_driver; - -static void change_speed(struct m68k_serial *info, struct tty_struct *tty); - -/* - * Setup for console. Argument comes from the boot command line. - */ - -/* note: this is messy, but it works, again, perhaps defined somewhere else?*/ -#ifdef CONFIG_M68VZ328 -#define CONSOLE_BAUD_RATE 19200 -#define DEFAULT_CBAUD B19200 -#endif - - -#ifndef CONSOLE_BAUD_RATE -#define CONSOLE_BAUD_RATE 9600 -#define DEFAULT_CBAUD B9600 -#endif - - -static int m68328_console_initted = 0; -static int m68328_console_baud = CONSOLE_BAUD_RATE; -static int m68328_console_cbaud = DEFAULT_CBAUD; - - -static inline int serial_paranoia_check(struct m68k_serial *info, - char *name, const char *routine) -{ -#ifdef SERIAL_PARANOIA_CHECK - static const char *badmagic = - "Warning: bad magic number for serial struct %s in %s\n"; - static const char *badinfo = - "Warning: null m68k_serial for %s in %s\n"; - - if (!info) { - printk(badinfo, name, routine); - return 1; - } - if (info->magic != SERIAL_MAGIC) { - printk(badmagic, name, routine); - return 1; - } -#endif - return 0; -} - -/* - * This is used to figure out the divisor speeds and the timeouts - */ -static int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 0 }; - -/* Utility routines */ -static inline int get_baud(struct m68k_serial *ss) -{ - unsigned long result = 115200; - unsigned short int baud = uart_addr[ss->line].ubaud; - if (GET_FIELD(baud, UBAUD_PRESCALER) == 0x38) result = 38400; - result >>= GET_FIELD(baud, UBAUD_DIVIDE); - - return result; -} - -/* - * ------------------------------------------------------------ - * rs_stop() and rs_start() - * - * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter interrupts, as necessary. - * ------------------------------------------------------------ - */ -static void rs_stop(struct tty_struct *tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_stop")) - return; - - local_irq_save(flags); - uart->ustcnt &= ~USTCNT_TXEN; - local_irq_restore(flags); -} - -static int rs_put_char(char ch) -{ - unsigned long flags; - int loops = 0; - - local_irq_save(flags); - - while (!(UTX & UTX_TX_AVAIL) && (loops < 1000)) { - loops++; - udelay(5); - } - - UTX_TXDATA = ch; - udelay(5); - local_irq_restore(flags); - return 1; -} - -static void rs_start(struct tty_struct *tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_start")) - return; - - local_irq_save(flags); - if (info->xmit_cnt && info->xmit_buf && !(uart->ustcnt & USTCNT_TXEN)) { -#ifdef USE_INTS - uart->ustcnt |= USTCNT_TXEN | USTCNT_TX_INTR_MASK; -#else - uart->ustcnt |= USTCNT_TXEN; -#endif - } - local_irq_restore(flags); -} - -static void receive_chars(struct m68k_serial *info, unsigned short rx) -{ - m68328_uart *uart = &uart_addr[info->line]; - unsigned char ch, flag; - - /* - * This do { } while() loop will get ALL chars out of Rx FIFO - */ -#ifndef CONFIG_XCOPILOT_BUGS - do { -#endif - ch = GET_FIELD(rx, URX_RXDATA); - - if(info->is_cons) { - if(URX_BREAK & rx) { /* whee, break received */ - return; -#ifdef CONFIG_MAGIC_SYSRQ - } else if (ch == 0x10) { /* ^P */ - show_state(); - show_free_areas(0); - show_buffers(); -/* show_net_buffers(); */ - return; - } else if (ch == 0x12) { /* ^R */ - emergency_restart(); - return; -#endif /* CONFIG_MAGIC_SYSRQ */ - } - } - - flag = TTY_NORMAL; - - if (rx & URX_PARITY_ERROR) - flag = TTY_PARITY; - else if (rx & URX_OVRUN) - flag = TTY_OVERRUN; - else if (rx & URX_FRAME_ERROR) - flag = TTY_FRAME; - - tty_insert_flip_char(&info->tport, ch, flag); -#ifndef CONFIG_XCOPILOT_BUGS - } while((rx = uart->urx.w) & URX_DATA_READY); -#endif - - tty_schedule_flip(&info->tport); -} - -static void transmit_chars(struct m68k_serial *info, struct tty_struct *tty) -{ - m68328_uart *uart = &uart_addr[info->line]; - - if (info->x_char) { - /* Send next char */ - uart->utx.b.txdata = info->x_char; - info->x_char = 0; - goto clear_and_return; - } - - if ((info->xmit_cnt <= 0) || !tty || tty->stopped) { - /* That's peculiar... TX ints off */ - uart->ustcnt &= ~USTCNT_TX_INTR_MASK; - goto clear_and_return; - } - - /* Send char */ - uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++]; - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt--; - - if(info->xmit_cnt <= 0) { - /* All done for now... TX ints off */ - uart->ustcnt &= ~USTCNT_TX_INTR_MASK; - goto clear_and_return; - } - -clear_and_return: - /* Clear interrupt (should be auto)*/ - return; -} - -/* - * This is the serial driver's generic interrupt routine - */ -irqreturn_t rs_interrupt(int irq, void *dev_id) -{ - struct m68k_serial *info = dev_id; - struct tty_struct *tty = tty_port_tty_get(&info->tport); - m68328_uart *uart; - unsigned short rx; - unsigned short tx; - - uart = &uart_addr[info->line]; - rx = uart->urx.w; - -#ifdef USE_INTS - tx = uart->utx.w; - - if (rx & URX_DATA_READY) - receive_chars(info, rx); - if (tx & UTX_TX_AVAIL) - transmit_chars(info, tty); -#else - receive_chars(info, rx); -#endif - tty_kref_put(tty); - - return IRQ_HANDLED; -} - -static int startup(struct m68k_serial *info, struct tty_struct *tty) -{ - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - - if (info->tport.flags & ASYNC_INITIALIZED) - return 0; - - if (!info->xmit_buf) { - info->xmit_buf = (unsigned char *) __get_free_page(GFP_KERNEL); - if (!info->xmit_buf) - return -ENOMEM; - } - - local_irq_save(flags); - - /* - * Clear the FIFO buffers and disable them - * (they will be reenabled in change_speed()) - */ - - uart->ustcnt = USTCNT_UEN; - uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_TXEN; - (void)uart->urx.w; - - /* - * Finally, enable sequencing and interrupts - */ -#ifdef USE_INTS - uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | - USTCNT_RX_INTR_MASK | USTCNT_TX_INTR_MASK; -#else - uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_RX_INTR_MASK; -#endif - - if (tty) - clear_bit(TTY_IO_ERROR, &tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - /* - * and set the speed of the serial port - */ - - change_speed(info, tty); - - info->tport.flags |= ASYNC_INITIALIZED; - local_irq_restore(flags); - return 0; -} - -/* - * This routine will shutdown a serial port; interrupts are disabled, and - * DTR is dropped if the hangup on close termio flag is on. - */ -static void shutdown(struct m68k_serial *info, struct tty_struct *tty) -{ - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - - uart->ustcnt = 0; /* All off! */ - if (!(info->tport.flags & ASYNC_INITIALIZED)) - return; - - local_irq_save(flags); - - if (info->xmit_buf) { - free_page((unsigned long) info->xmit_buf); - info->xmit_buf = 0; - } - - if (tty) - set_bit(TTY_IO_ERROR, &tty->flags); - - info->tport.flags &= ~ASYNC_INITIALIZED; - local_irq_restore(flags); -} - -struct { - int divisor, prescale; -} -#ifndef CONFIG_M68VZ328 - hw_baud_table[18] = { - {0,0}, /* 0 */ - {0,0}, /* 50 */ - {0,0}, /* 75 */ - {0,0}, /* 110 */ - {0,0}, /* 134 */ - {0,0}, /* 150 */ - {0,0}, /* 200 */ - {7,0x26}, /* 300 */ - {6,0x26}, /* 600 */ - {5,0x26}, /* 1200 */ - {0,0}, /* 1800 */ - {4,0x26}, /* 2400 */ - {3,0x26}, /* 4800 */ - {2,0x26}, /* 9600 */ - {1,0x26}, /* 19200 */ - {0,0x26}, /* 38400 */ - {1,0x38}, /* 57600 */ - {0,0x38}, /* 115200 */ -}; -#else - hw_baud_table[18] = { - {0,0}, /* 0 */ - {0,0}, /* 50 */ - {0,0}, /* 75 */ - {0,0}, /* 110 */ - {0,0}, /* 134 */ - {0,0}, /* 150 */ - {0,0}, /* 200 */ - {0,0}, /* 300 */ - {7,0x26}, /* 600 */ - {6,0x26}, /* 1200 */ - {0,0}, /* 1800 */ - {5,0x26}, /* 2400 */ - {4,0x26}, /* 4800 */ - {3,0x26}, /* 9600 */ - {2,0x26}, /* 19200 */ - {1,0x26}, /* 38400 */ - {0,0x26}, /* 57600 */ - {1,0x38}, /* 115200 */ -}; -#endif -/* rate = 1036800 / ((65 - prescale) * (1<<divider)) */ - -/* - * This routine is called to set the UART divisor registers to match - * the specified baud rate for a serial port. - */ -static void change_speed(struct m68k_serial *info, struct tty_struct *tty) -{ - m68328_uart *uart = &uart_addr[info->line]; - unsigned short port; - unsigned short ustcnt; - unsigned cflag; - int i; - - cflag = tty->termios.c_cflag; - port = info->port; - if (!port) - return; - - ustcnt = uart->ustcnt; - uart->ustcnt = ustcnt & ~USTCNT_TXEN; - - i = cflag & CBAUD; - if (i & CBAUDEX) { - i = (i & ~CBAUDEX) + B38400; - } - - uart->ubaud = PUT_FIELD(UBAUD_DIVIDE, hw_baud_table[i].divisor) | - PUT_FIELD(UBAUD_PRESCALER, hw_baud_table[i].prescale); - - ustcnt &= ~(USTCNT_PARITYEN | USTCNT_ODD_EVEN | USTCNT_STOP | USTCNT_8_7); - - if ((cflag & CSIZE) == CS8) - ustcnt |= USTCNT_8_7; - - if (cflag & CSTOPB) - ustcnt |= USTCNT_STOP; - - if (cflag & PARENB) - ustcnt |= USTCNT_PARITYEN; - if (cflag & PARODD) - ustcnt |= USTCNT_ODD_EVEN; - -#ifdef CONFIG_SERIAL_68328_RTS_CTS - if (cflag & CRTSCTS) { - uart->utx.w &= ~ UTX_NOCTS; - } else { - uart->utx.w |= UTX_NOCTS; - } -#endif - - ustcnt |= USTCNT_TXEN; - - uart->ustcnt = ustcnt; - return; -} - -/* - * Fair output driver allows a process to speak. - */ -static void rs_fair_output(void) -{ - int left; /* Output no more than that */ - unsigned long flags; - struct m68k_serial *info = &m68k_soft[0]; - char c; - - if (info == NULL) return; - if (info->xmit_buf == NULL) return; - - local_irq_save(flags); - left = info->xmit_cnt; - while (left != 0) { - c = info->xmit_buf[info->xmit_tail]; - info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt--; - local_irq_restore(flags); - - rs_put_char(c); - - local_irq_save(flags); - left = min(info->xmit_cnt, left-1); - } - - /* Last character is being transmitted now (hopefully). */ - udelay(5); - - local_irq_restore(flags); - return; -} - -/* - * m68k_console_print is registered for printk. - */ -void console_print_68328(const char *p) -{ - char c; - - while((c=*(p++)) != 0) { - if(c == '\n') - rs_put_char('\r'); - rs_put_char(c); - } - - /* Comment this if you want to have a strict interrupt-driven output */ - rs_fair_output(); - - return; -} - -static void rs_set_ldisc(struct tty_struct *tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_set_ldisc")) - return; - - info->is_cons = (tty->termios.c_line == N_TTY); - - printk("ttyS%d console mode %s\n", info->line, info->is_cons ? "on" : "off"); -} - -static void rs_flush_chars(struct tty_struct *tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_flush_chars")) - return; -#ifndef USE_INTS - for(;;) { -#endif - - /* Enable transmitter */ - local_irq_save(flags); - - if (info->xmit_cnt <= 0 || tty->stopped || !info->xmit_buf) { - local_irq_restore(flags); - return; - } - -#ifdef USE_INTS - uart->ustcnt |= USTCNT_TXEN | USTCNT_TX_INTR_MASK; -#else - uart->ustcnt |= USTCNT_TXEN; -#endif - -#ifdef USE_INTS - if (uart->utx.w & UTX_TX_AVAIL) { -#else - if (1) { -#endif - /* Send char */ - uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++]; - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt--; - } - -#ifndef USE_INTS - while (!(uart->utx.w & UTX_TX_AVAIL)) udelay(5); - } -#endif - local_irq_restore(flags); -} - -extern void console_printn(const char * b, int count); - -static int rs_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - int c, total = 0; - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_write")) - return 0; - - if (!tty || !info->xmit_buf) - return 0; - - local_save_flags(flags); - while (1) { - local_irq_disable(); - c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - local_irq_restore(flags); - - if (c <= 0) - break; - - memcpy(info->xmit_buf + info->xmit_head, buf, c); - - local_irq_disable(); - info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt += c; - local_irq_restore(flags); - buf += c; - count -= c; - total += c; - } - - if (info->xmit_cnt && !tty->stopped) { - /* Enable transmitter */ - local_irq_disable(); -#ifndef USE_INTS - while(info->xmit_cnt) { -#endif - - uart->ustcnt |= USTCNT_TXEN; -#ifdef USE_INTS - uart->ustcnt |= USTCNT_TX_INTR_MASK; -#else - while (!(uart->utx.w & UTX_TX_AVAIL)) udelay(5); -#endif - if (uart->utx.w & UTX_TX_AVAIL) { - uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++]; - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt--; - } - -#ifndef USE_INTS - } -#endif - local_irq_restore(flags); - } - - return total; -} - -static int rs_write_room(struct tty_struct *tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - int ret; - - if (serial_paranoia_check(info, tty->name, "rs_write_room")) - return 0; - ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - return ret; -} - -static int rs_chars_in_buffer(struct tty_struct *tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer")) - return 0; - return info->xmit_cnt; -} - -static void rs_flush_buffer(struct tty_struct *tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_flush_buffer")) - return; - local_irq_save(flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - local_irq_restore(flags); - tty_wakeup(tty); -} - -/* - * ------------------------------------------------------------ - * rs_throttle() - * - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled. - * ------------------------------------------------------------ - */ -static void rs_throttle(struct tty_struct * tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_throttle")) - return; - - if (I_IXOFF(tty)) - info->x_char = STOP_CHAR(tty); - - /* Turn off RTS line (do this atomic) */ -} - -static void rs_unthrottle(struct tty_struct * tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_unthrottle")) - return; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - info->x_char = START_CHAR(tty); - } - - /* Assert RTS line (do this atomic) */ -} - -/* - * ------------------------------------------------------------ - * rs_ioctl() and friends - * ------------------------------------------------------------ - */ - -static int get_serial_info(struct m68k_serial * info, - struct serial_struct * retinfo) -{ - struct serial_struct tmp; - - if (!retinfo) - return -EFAULT; - memset(&tmp, 0, sizeof(tmp)); - tmp.type = info->type; - tmp.line = info->line; - tmp.port = info->port; - tmp.irq = info->irq; - tmp.flags = info->tport.flags; - tmp.baud_base = info->baud_base; - tmp.close_delay = info->tport.close_delay; - tmp.closing_wait = info->tport.closing_wait; - tmp.custom_divisor = info->custom_divisor; - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - - return 0; -} - -static int set_serial_info(struct m68k_serial *info, struct tty_struct *tty, - struct serial_struct * new_info) -{ - struct tty_port *port = &info->tport; - struct serial_struct new_serial; - struct m68k_serial old_info; - int retval = 0; - - if (!new_info) - return -EFAULT; - if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) - return -EFAULT; - old_info = *info; - - if (!capable(CAP_SYS_ADMIN)) { - if ((new_serial.baud_base != info->baud_base) || - (new_serial.type != info->type) || - (new_serial.close_delay != port->close_delay) || - ((new_serial.flags & ~ASYNC_USR_MASK) != - (port->flags & ~ASYNC_USR_MASK))) - return -EPERM; - port->flags = ((port->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - info->custom_divisor = new_serial.custom_divisor; - goto check_and_exit; - } - - if (port->count > 1) - return -EBUSY; - - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - - info->baud_base = new_serial.baud_base; - port->flags = ((port->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); - info->type = new_serial.type; - port->close_delay = new_serial.close_delay; - port->closing_wait = new_serial.closing_wait; - -check_and_exit: - retval = startup(info, tty); - return retval; -} - -/* - * get_lsr_info - get line status register info - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static int get_lsr_info(struct m68k_serial * info, unsigned int *value) -{ -#ifdef CONFIG_SERIAL_68328_RTS_CTS - m68328_uart *uart = &uart_addr[info->line]; -#endif - unsigned char status; - unsigned long flags; - - local_irq_save(flags); -#ifdef CONFIG_SERIAL_68328_RTS_CTS - status = (uart->utx.w & UTX_CTS_STAT) ? 1 : 0; -#else - status = 0; -#endif - local_irq_restore(flags); - return put_user(status, value); -} - -/* - * This routine sends a break character out the serial port. - */ -static void send_break(struct m68k_serial * info, unsigned int duration) -{ - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - if (!info->port) - return; - local_irq_save(flags); -#ifdef USE_INTS - uart->utx.w |= UTX_SEND_BREAK; - msleep_interruptible(duration); - uart->utx.w &= ~UTX_SEND_BREAK; -#endif - local_irq_restore(flags); -} - -static int rs_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct m68k_serial * info = (struct m68k_serial *)tty->driver_data; - int retval; - - if (serial_paranoia_check(info, tty->name, "rs_ioctl")) - return -ENODEV; - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && - (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (!arg) - send_break(info, 250); /* 1/4 second */ - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - send_break(info, arg ? arg*(100) : 250); - return 0; - case TIOCGSERIAL: - return get_serial_info(info, - (struct serial_struct *) arg); - case TIOCSSERIAL: - return set_serial_info(info, tty, - (struct serial_struct *) arg); - case TIOCSERGETLSR: /* Get line status register */ - return get_lsr_info(info, (unsigned int *) arg); - case TIOCSERGSTRUCT: - if (copy_to_user((struct m68k_serial *) arg, - info, sizeof(struct m68k_serial))) - return -EFAULT; - return 0; - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - - change_speed(info, tty); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios.c_cflag & CRTSCTS)) - rs_start(tty); - -} - -/* - * ------------------------------------------------------------ - * rs_close() - * - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we unlink its - * S structure from the interrupt chain if necessary, and we free - * that IRQ if nothing is left in the chain. - * ------------------------------------------------------------ - */ -static void rs_close(struct tty_struct *tty, struct file * filp) -{ - struct m68k_serial * info = (struct m68k_serial *)tty->driver_data; - struct tty_port *port = &info->tport; - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_close")) - return; - - local_irq_save(flags); - - if (tty_hung_up_p(filp)) { - local_irq_restore(flags); - return; - } - - if ((tty->count == 1) && (port->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk("rs_close: bad serial port count; tty->count is 1, " - "port->count is %d\n", port->count); - port->count = 1; - } - if (--port->count < 0) { - printk("rs_close: bad serial port count for ttyS%d: %d\n", - info->line, port->count); - port->count = 0; - } - if (port->count) { - local_irq_restore(flags); - return; - } - port->flags |= ASYNC_CLOSING; - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, port->closing_wait); - /* - * At this point we stop accepting input. To do this, we - * disable the receive line status interrupts, and tell the - * interrupt driver to stop checking the data ready bit in the - * line status register. - */ - - uart->ustcnt &= ~USTCNT_RXEN; - uart->ustcnt &= ~(USTCNT_RXEN | USTCNT_RX_INTR_MASK); - - shutdown(info, tty); - rs_flush_buffer(tty); - - tty_ldisc_flush(tty); - tty->closing = 0; - tty_port_tty_set(&info->tport, NULL); -#warning "This is not and has never been valid so fix it" -#if 0 - if (tty->ldisc.num != ldiscs[N_TTY].num) { - if (tty->ldisc.close) - (tty->ldisc.close)(tty); - tty->ldisc = ldiscs[N_TTY]; - tty->termios.c_line = N_TTY; - if (tty->ldisc.open) - (tty->ldisc.open)(tty); - } -#endif - if (port->blocked_open) { - if (port->close_delay) - msleep_interruptible(jiffies_to_msecs(port->close_delay)); - wake_up_interruptible(&port->open_wait); - } - port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - local_irq_restore(flags); -} - -/* - * rs_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -void rs_hangup(struct tty_struct *tty) -{ - struct m68k_serial * info = (struct m68k_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_hangup")) - return; - - rs_flush_buffer(tty); - shutdown(info, tty); - info->tport.count = 0; - info->tport.flags &= ~ASYNC_NORMAL_ACTIVE; - tty_port_tty_set(&info->tport, NULL); - wake_up_interruptible(&info->tport.open_wait); -} - -/* - * This routine is called whenever a serial port is opened. It - * enables interrupts for a serial port, linking in its S structure into - * the IRQ chain. It also performs the serial-specific - * initialization for the tty structure. - */ -int rs_open(struct tty_struct *tty, struct file * filp) -{ - struct m68k_serial *info; - int retval; - - info = &m68k_soft[tty->index]; - - if (serial_paranoia_check(info, tty->name, "rs_open")) - return -ENODEV; - - info->tport.count++; - tty->driver_data = info; - tty_port_tty_set(&info->tport, tty); - - /* - * Start up serial port - */ - retval = startup(info, tty); - if (retval) - return retval; - - return tty_port_block_til_ready(&info->tport, tty, filp); -} - -/* Finally, routines used to initialize the serial driver. */ - -static void show_serial_version(void) -{ - printk("MC68328 serial driver version 1.00\n"); -} - -static const struct tty_operations rs_ops = { - .open = rs_open, - .close = rs_close, - .write = rs_write, - .flush_chars = rs_flush_chars, - .write_room = rs_write_room, - .chars_in_buffer = rs_chars_in_buffer, - .flush_buffer = rs_flush_buffer, - .ioctl = rs_ioctl, - .throttle = rs_throttle, - .unthrottle = rs_unthrottle, - .set_termios = rs_set_termios, - .stop = rs_stop, - .start = rs_start, - .hangup = rs_hangup, - .set_ldisc = rs_set_ldisc, -}; - -static const struct tty_port_operations rs_port_ops = { -}; - -/* rs_init inits the driver */ -static int __init -rs68328_init(void) -{ - unsigned long flags; - int i; - struct m68k_serial *info; - - serial_driver = alloc_tty_driver(NR_PORTS); - if (!serial_driver) - return -ENOMEM; - - show_serial_version(); - - /* Initialize the tty_driver structure */ - /* SPARC: Not all of this is exactly right for us. */ - - serial_driver->name = "ttyS"; - serial_driver->major = TTY_MAJOR; - serial_driver->minor_start = 64; - serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - serial_driver->subtype = SERIAL_TYPE_NORMAL; - serial_driver->init_termios = tty_std_termios; - serial_driver->init_termios.c_cflag = - m68328_console_cbaud | CS8 | CREAD | HUPCL | CLOCAL; - serial_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(serial_driver, &rs_ops); - - local_irq_save(flags); - - for(i=0;i<NR_PORTS;i++) { - - info = &m68k_soft[i]; - tty_port_init(&info->tport); - info->tport.ops = &rs_port_ops; - info->magic = SERIAL_MAGIC; - info->port = (int) &uart_addr[i]; - info->irq = uart_irqs[i]; - info->custom_divisor = 16; - info->x_char = 0; - info->line = i; - info->is_cons = 1; /* Means shortcuts work */ - - printk("%s%d at 0x%08x (irq = %d)", serial_driver->name, info->line, - info->port, info->irq); - printk(" is a builtin MC68328 UART\n"); - -#ifdef CONFIG_M68VZ328 - if (i > 0 ) - PJSEL &= 0xCF; /* PSW enable second port output */ -#endif - - if (request_irq(uart_irqs[i], - rs_interrupt, - 0, - "M68328_UART", info)) - panic("Unable to attach 68328 serial interrupt\n"); - - tty_port_link_device(&info->tport, serial_driver, i); - } - local_irq_restore(flags); - - if (tty_register_driver(serial_driver)) { - put_tty_driver(serial_driver); - for (i = 0; i < NR_PORTS; i++) - tty_port_destroy(&m68k_soft[i].tport); - printk(KERN_ERR "Couldn't register serial driver\n"); - return -ENOMEM; - } - - return 0; -} - -module_init(rs68328_init); - - - -static void m68328_set_baud(void) -{ - unsigned short ustcnt; - int i; - - ustcnt = USTCNT; - USTCNT = ustcnt & ~USTCNT_TXEN; - -again: - for (i = 0; i < ARRAY_SIZE(baud_table); i++) - if (baud_table[i] == m68328_console_baud) - break; - if (i >= ARRAY_SIZE(baud_table)) { - m68328_console_baud = 9600; - goto again; - } - - UBAUD = PUT_FIELD(UBAUD_DIVIDE, hw_baud_table[i].divisor) | - PUT_FIELD(UBAUD_PRESCALER, hw_baud_table[i].prescale); - ustcnt &= ~(USTCNT_PARITYEN | USTCNT_ODD_EVEN | USTCNT_STOP | USTCNT_8_7); - ustcnt |= USTCNT_8_7; - ustcnt |= USTCNT_TXEN; - USTCNT = ustcnt; - m68328_console_initted = 1; - return; -} - - -int m68328_console_setup(struct console *cp, char *arg) -{ - int i, n = CONSOLE_BAUD_RATE; - - if (!cp) - return(-1); - - if (arg) - n = simple_strtoul(arg,NULL,0); - - for (i = 0; i < ARRAY_SIZE(baud_table); i++) - if (baud_table[i] == n) - break; - if (i < ARRAY_SIZE(baud_table)) { - m68328_console_baud = n; - m68328_console_cbaud = 0; - if (i > 15) { - m68328_console_cbaud |= CBAUDEX; - i -= 15; - } - m68328_console_cbaud |= i; - } - - m68328_set_baud(); /* make sure baud rate changes */ - return(0); -} - - -static struct tty_driver *m68328_console_device(struct console *c, int *index) -{ - *index = c->index; - return serial_driver; -} - - -void m68328_console_write (struct console *co, const char *str, - unsigned int count) -{ - if (!m68328_console_initted) - m68328_set_baud(); - while (count--) { - if (*str == '\n') - rs_put_char('\r'); - rs_put_char( *str++ ); - } -} - - -static struct console m68328_driver = { - .name = "ttyS", - .write = m68328_console_write, - .device = m68328_console_device, - .setup = m68328_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - - -static int __init m68328_console_init(void) -{ - register_console(&m68328_driver); - return 0; -} - -console_initcall(m68328_console_init); diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index d54dcd87c67e..047a7ba6796a 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -92,6 +92,18 @@ struct serial8250_config { #define SERIAL8250_SHARE_IRQS 0 #endif +#define SERIAL8250_PORT_FLAGS(_base, _irq, _flags) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = UPF_BOOT_AUTOCONF | (_flags), \ + } + +#define SERIAL8250_PORT(_base, _irq) SERIAL8250_PORT_FLAGS(_base, _irq, 0) + + static inline int serial_in(struct uart_8250_port *up, int offset) { return up->port.serial_in(&up->port, offset); @@ -117,6 +129,8 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value) struct uart_8250_port *serial8250_get_port(int line); void serial8250_rpm_get(struct uart_8250_port *p); void serial8250_rpm_put(struct uart_8250_port *p); +int serial8250_em485_init(struct uart_8250_port *p); +void serial8250_em485_destroy(struct uart_8250_port *p); #if defined(__alpha__) && !defined(CONFIG_PCI) /* diff --git a/drivers/tty/serial/8250/8250_accent.c b/drivers/tty/serial/8250/8250_accent.c index 34b51c651192..522aeae05192 100644 --- a/drivers/tty/serial/8250/8250_accent.c +++ b/drivers/tty/serial/8250/8250_accent.c @@ -10,18 +10,11 @@ #include <linux/init.h> #include <linux/serial_8250.h> -#define PORT(_base,_irq) \ - { \ - .iobase = _base, \ - .irq = _irq, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ - .flags = UPF_BOOT_AUTOCONF, \ - } +#include "8250.h" static struct plat_serial8250_port accent_data[] = { - PORT(0x330, 4), - PORT(0x338, 4), + SERIAL8250_PORT(0x330, 4), + SERIAL8250_PORT(0x338, 4), { }, }; diff --git a/drivers/tty/serial/8250/8250_acorn.c b/drivers/tty/serial/8250/8250_acorn.c index 549aa07c0d27..402dfdd4940e 100644 --- a/drivers/tty/serial/8250/8250_acorn.c +++ b/drivers/tty/serial/8250/8250_acorn.c @@ -70,7 +70,7 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) uart.port.regshift = 2; uart.port.dev = &ec->dev; - for (i = 0; i < info->num_ports; i ++) { + for (i = 0; i < info->num_ports; i++) { uart.port.membase = info->vaddr + type->offset[i]; uart.port.mapbase = bus_addr + type->offset[i]; diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c new file mode 100644 index 000000000000..e10f1244409b --- /dev/null +++ b/drivers/tty/serial/8250/8250_bcm2835aux.c @@ -0,0 +1,146 @@ +/* + * Serial port driver for BCM2835AUX UART + * + * Copyright (C) 2016 Martin Sperl <kernel@martin.sperl.org> + * + * Based on 8250_lpc18xx.c: + * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> + * + * 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. + * + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include "8250.h" + +struct bcm2835aux_data { + struct uart_8250_port uart; + struct clk *clk; + int line; +}; + +static int bcm2835aux_serial_probe(struct platform_device *pdev) +{ + struct bcm2835aux_data *data; + struct resource *res; + int ret; + + /* allocate the custom structure */ + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* initialize data */ + spin_lock_init(&data->uart.port.lock); + data->uart.capabilities = UART_CAP_FIFO; + data->uart.port.dev = &pdev->dev; + data->uart.port.regshift = 2; + data->uart.port.type = PORT_16550; + data->uart.port.iotype = UPIO_MEM; + data->uart.port.fifosize = 8; + data->uart.port.flags = UPF_SHARE_IRQ | + UPF_FIXED_PORT | + UPF_FIXED_TYPE | + UPF_SKIP_TEST; + + /* get the clock - this also enables the HW */ + data->clk = devm_clk_get(&pdev->dev, NULL); + ret = PTR_ERR_OR_ZERO(data->clk); + if (ret) { + dev_err(&pdev->dev, "could not get clk: %d\n", ret); + return ret; + } + + /* get the interrupt */ + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(&pdev->dev, "irq not found - %i", ret); + return ret; + } + data->uart.port.irq = ret; + + /* map the main registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "memory resource not found"); + return -EINVAL; + } + data->uart.port.membase = devm_ioremap_resource(&pdev->dev, res); + ret = PTR_ERR_OR_ZERO(data->uart.port.membase); + if (ret) + return ret; + + /* Check for a fixed line number */ + ret = of_alias_get_id(pdev->dev.of_node, "serial"); + if (ret >= 0) + data->uart.port.line = ret; + + /* enable the clock as a last step */ + ret = clk_prepare_enable(data->clk); + if (ret) { + dev_err(&pdev->dev, "unable to enable uart clock - %d\n", + ret); + return ret; + } + + /* the HW-clock divider for bcm2835aux is 8, + * but 8250 expects a divider of 16, + * so we have to multiply the actual clock by 2 + * to get identical baudrates. + */ + data->uart.port.uartclk = clk_get_rate(data->clk) * 2; + + /* register the port */ + ret = serial8250_register_8250_port(&data->uart); + if (ret < 0) { + dev_err(&pdev->dev, "unable to register 8250 port - %d\n", + ret); + goto dis_clk; + } + data->line = ret; + + platform_set_drvdata(pdev, data); + + return 0; + +dis_clk: + clk_disable_unprepare(data->clk); + return ret; +} + +static int bcm2835aux_serial_remove(struct platform_device *pdev) +{ + struct bcm2835aux_data *data = platform_get_drvdata(pdev); + + serial8250_unregister_port(data->uart.port.line); + clk_disable_unprepare(data->clk); + + return 0; +} + +static const struct of_device_id bcm2835aux_serial_match[] = { + { .compatible = "brcm,bcm2835-aux-uart" }, + { }, +}; +MODULE_DEVICE_TABLE(of, bcm2835aux_serial_match); + +static struct platform_driver bcm2835aux_serial_driver = { + .driver = { + .name = "bcm2835-aux-uart", + .of_match_table = bcm2835aux_serial_match, + }, + .probe = bcm2835aux_serial_probe, + .remove = bcm2835aux_serial_remove, +}; +module_platform_driver(bcm2835aux_serial_driver); + +MODULE_DESCRIPTION("BCM2835 auxiliar UART driver"); +MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/8250_boca.c b/drivers/tty/serial/8250/8250_boca.c index d125dc107985..a63b5998e383 100644 --- a/drivers/tty/serial/8250/8250_boca.c +++ b/drivers/tty/serial/8250/8250_boca.c @@ -10,32 +10,25 @@ #include <linux/init.h> #include <linux/serial_8250.h> -#define PORT(_base,_irq) \ - { \ - .iobase = _base, \ - .irq = _irq, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ - .flags = UPF_BOOT_AUTOCONF, \ - } +#include "8250.h" static struct plat_serial8250_port boca_data[] = { - PORT(0x100, 12), - PORT(0x108, 12), - PORT(0x110, 12), - PORT(0x118, 12), - PORT(0x120, 12), - PORT(0x128, 12), - PORT(0x130, 12), - PORT(0x138, 12), - PORT(0x140, 12), - PORT(0x148, 12), - PORT(0x150, 12), - PORT(0x158, 12), - PORT(0x160, 12), - PORT(0x168, 12), - PORT(0x170, 12), - PORT(0x178, 12), + SERIAL8250_PORT(0x100, 12), + SERIAL8250_PORT(0x108, 12), + SERIAL8250_PORT(0x110, 12), + SERIAL8250_PORT(0x118, 12), + SERIAL8250_PORT(0x120, 12), + SERIAL8250_PORT(0x128, 12), + SERIAL8250_PORT(0x130, 12), + SERIAL8250_PORT(0x138, 12), + SERIAL8250_PORT(0x140, 12), + SERIAL8250_PORT(0x148, 12), + SERIAL8250_PORT(0x150, 12), + SERIAL8250_PORT(0x158, 12), + SERIAL8250_PORT(0x160, 12), + SERIAL8250_PORT(0x168, 12), + SERIAL8250_PORT(0x170, 12), + SERIAL8250_PORT(0x178, 12), { }, }; diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 39126460c1f5..2f4f5ee651db 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -597,6 +597,7 @@ static void univ8250_console_write(struct console *co, const char *s, static int univ8250_console_setup(struct console *co, char *options) { struct uart_port *port; + int retval; /* * Check whether an invalid uart number has been specified, and @@ -609,7 +610,10 @@ static int univ8250_console_setup(struct console *co, char *options) /* link port to console */ port->cons = co; - return serial8250_console_setup(port, options, false); + retval = serial8250_console_setup(port, options, false); + if (retval != 0) + port->cons = NULL; + return retval; } /** @@ -620,7 +624,7 @@ static int univ8250_console_setup(struct console *co, char *options) * @options: ptr to option string from console command line * * Only attempts to match console command lines of the form: - * console=uart[8250],io|mmio|mmio32,<addr>[,<options>] + * console=uart[8250],io|mmio|mmio16|mmio32,<addr>[,<options>] * console=uart[8250],0x<addr>[,<options>] * This form is used to register an initial earlycon boot console and * replace it with the serial8250_console at 8250 driver init. @@ -650,8 +654,9 @@ static int univ8250_console_match(struct console *co, char *name, int idx, if (port->iotype != iotype) continue; - if ((iotype == UPIO_MEM || iotype == UPIO_MEM32) && - (port->mapbase != addr)) + if ((iotype == UPIO_MEM || iotype == UPIO_MEM16 || + iotype == UPIO_MEM32 || iotype == UPIO_MEM32BE) + && (port->mapbase != addr)) continue; if (iotype == UPIO_PORT && port->iobase != addr) continue; @@ -686,7 +691,7 @@ static int __init univ8250_console_init(void) } console_initcall(univ8250_console_init); -#define SERIAL8250_CONSOLE &univ8250_console +#define SERIAL8250_CONSOLE (&univ8250_console) #else #define SERIAL8250_CONSOLE NULL #endif @@ -763,6 +768,7 @@ void serial8250_suspend_port(int line) uart_suspend_port(&serial8250_reg, port); } +EXPORT_SYMBOL(serial8250_suspend_port); /** * serial8250_resume_port - resume one serial port @@ -788,6 +794,7 @@ void serial8250_resume_port(int line) } uart_resume_port(&serial8250_reg, port); } +EXPORT_SYMBOL(serial8250_resume_port); /* * Register a set of serial devices attached to a platform device. The @@ -1067,6 +1074,15 @@ void serial8250_unregister_port(int line) struct uart_8250_port *uart = &serial8250_ports[line]; mutex_lock(&serial_mutex); + + if (uart->em485) { + unsigned long flags; + + spin_lock_irqsave(&uart->port.lock, flags); + serial8250_em485_destroy(uart); + spin_unlock_irqrestore(&uart->port.lock, flags); + } + uart_remove_one_port(&serial8250_reg, &uart->port); if (serial8250_isa_devs) { uart->port.flags &= ~UPF_BOOT_AUTOCONF; @@ -1092,9 +1108,8 @@ static int __init serial8250_init(void) serial8250_isa_init_ports(); - printk(KERN_INFO "Serial: 8250/16550 driver, " - "%d ports, IRQ sharing %sabled\n", nr_uarts, - share_irqs ? "en" : "dis"); + pr_info("Serial: 8250/16550 driver, %d ports, IRQ sharing %sabled\n", + nr_uarts, share_irqs ? "en" : "dis"); #ifdef CONFIG_SPARC ret = sunserial_register_minors(&serial8250_reg, UART_NR); @@ -1167,15 +1182,11 @@ static void __exit serial8250_exit(void) module_init(serial8250_init); module_exit(serial8250_exit); -EXPORT_SYMBOL(serial8250_suspend_port); -EXPORT_SYMBOL(serial8250_resume_port); - MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic 8250/16x50 serial driver"); module_param(share_irqs, uint, 0644); -MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices" - " (unsafe)"); +MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices (unsafe)"); module_param(nr_uarts, uint, 0644); MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STRING(CONFIG_SERIAL_8250_NR_UARTS) ")"); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index a5d319e4aae6..a3fb95d85d7c 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -68,12 +68,6 @@ struct dw8250_data { unsigned int uart_16550_compatible:1; }; -#define BYT_PRV_CLK 0x800 -#define BYT_PRV_CLK_EN (1 << 0) -#define BYT_PRV_CLK_M_VAL_SHIFT 1 -#define BYT_PRV_CLK_N_VAL_SHIFT 16 -#define BYT_PRV_CLK_UPDATE (1 << 31) - static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value) { struct dw8250_data *d = p->private_data; @@ -95,25 +89,45 @@ static void dw8250_force_idle(struct uart_port *p) (void)p->serial_in(p, UART_RX); } -static void dw8250_serial_out(struct uart_port *p, int offset, int value) +static void dw8250_check_lcr(struct uart_port *p, int value) { - writeb(value, p->membase + (offset << p->regshift)); + void __iomem *offset = p->membase + (UART_LCR << p->regshift); + int tries = 1000; /* Make sure LCR write wasn't ignored */ - if (offset == UART_LCR) { - int tries = 1000; - while (tries--) { - unsigned int lcr = p->serial_in(p, UART_LCR); - if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) - return; - dw8250_force_idle(p); - writeb(value, p->membase + (UART_LCR << p->regshift)); - } - /* - * FIXME: this deadlocks if port->lock is already held - * dev_err(p->dev, "Couldn't set LCR to %d\n", value); - */ + while (tries--) { + unsigned int lcr = p->serial_in(p, UART_LCR); + + if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) + return; + + dw8250_force_idle(p); + +#ifdef CONFIG_64BIT + __raw_writeq(value & 0xff, offset); +#else + 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 + * dev_err(p->dev, "Couldn't set LCR to %d\n", value); + */ +} + +static void dw8250_serial_out(struct uart_port *p, int offset, int value) +{ + struct dw8250_data *d = p->private_data; + + writeb(value, p->membase + (offset << p->regshift)); + + if (offset == UART_LCR && !d->uart_16550_compatible) + dw8250_check_lcr(p, value); } static unsigned int dw8250_serial_in(struct uart_port *p, int offset) @@ -135,49 +149,26 @@ static unsigned int dw8250_serial_inq(struct uart_port *p, int offset) static void dw8250_serial_outq(struct uart_port *p, int offset, int value) { + struct dw8250_data *d = p->private_data; + value &= 0xff; __raw_writeq(value, p->membase + (offset << p->regshift)); /* Read back to ensure register write ordering. */ __raw_readq(p->membase + (UART_LCR << p->regshift)); - /* Make sure LCR write wasn't ignored */ - if (offset == UART_LCR) { - int tries = 1000; - while (tries--) { - unsigned int lcr = p->serial_in(p, UART_LCR); - if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) - return; - dw8250_force_idle(p); - __raw_writeq(value & 0xff, - p->membase + (UART_LCR << p->regshift)); - } - /* - * FIXME: this deadlocks if port->lock is already held - * dev_err(p->dev, "Couldn't set LCR to %d\n", value); - */ - } + if (offset == UART_LCR && !d->uart_16550_compatible) + dw8250_check_lcr(p, value); } #endif /* CONFIG_64BIT */ static void dw8250_serial_out32(struct uart_port *p, int offset, int value) { + struct dw8250_data *d = p->private_data; + writel(value, p->membase + (offset << p->regshift)); - /* Make sure LCR write wasn't ignored */ - if (offset == UART_LCR) { - int tries = 1000; - while (tries--) { - unsigned int lcr = p->serial_in(p, UART_LCR); - if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) - return; - dw8250_force_idle(p); - writel(value, p->membase + (UART_LCR << p->regshift)); - } - /* - * FIXME: this deadlocks if port->lock is already held - * dev_err(p->dev, "Couldn't set LCR to %d\n", value); - */ - } + if (offset == UART_LCR && !d->uart_16550_compatible) + dw8250_check_lcr(p, value); } static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) @@ -187,14 +178,33 @@ static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) return dw8250_modify_msr(p, offset, value); } +static void dw8250_serial_out32be(struct uart_port *p, int offset, int value) +{ + struct dw8250_data *d = p->private_data; + + iowrite32be(value, p->membase + (offset << p->regshift)); + + if (offset == UART_LCR && !d->uart_16550_compatible) + dw8250_check_lcr(p, value); +} + +static unsigned int dw8250_serial_in32be(struct uart_port *p, int offset) +{ + unsigned int value = ioread32be(p->membase + (offset << p->regshift)); + + return dw8250_modify_msr(p, offset, value); +} + + static int dw8250_handle_irq(struct uart_port *p) { struct dw8250_data *d = p->private_data; unsigned int iir = p->serial_in(p, UART_IIR); - if (serial8250_handle_irq(p, iir)) { + if (serial8250_handle_irq(p, iir)) return 1; - } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { + + if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { /* Clear the USR */ (void)p->serial_in(p, d->usr_reg); @@ -281,6 +291,11 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) data->skip_autocfg = true; } #endif + if (of_device_is_big_endian(p->dev->of_node)) { + p->iotype = UPIO_MEM32BE; + p->serial_in = dw8250_serial_in32be; + p->serial_out = dw8250_serial_out32be; + } } else if (has_acpi_companion(p->dev)) { p->iotype = UPIO_MEM32; p->regshift = 2; @@ -309,14 +324,20 @@ static void dw8250_setup_port(struct uart_port *p) * If the Component Version Register returns zero, we know that * ADDITIONAL_FEATURES are not enabled. No need to go any further. */ - reg = readl(p->membase + DW_UART_UCV); + if (p->iotype == UPIO_MEM32BE) + reg = ioread32be(p->membase + DW_UART_UCV); + else + reg = readl(p->membase + DW_UART_UCV); if (!reg) return; dev_dbg(p->dev, "Designware UART version %c.%c%c\n", (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); - reg = readl(p->membase + DW_UART_CPR); + if (p->iotype == UPIO_MEM32BE) + reg = ioread32be(p->membase + DW_UART_CPR); + else + reg = readl(p->membase + DW_UART_CPR); if (!reg) return; @@ -463,10 +484,8 @@ static int dw8250_probe(struct platform_device *pdev) dw8250_quirks(p, data); /* If the Busy Functionality is not implemented, don't handle it */ - if (data->uart_16550_compatible) { - p->serial_out = NULL; + if (data->uart_16550_compatible) p->handle_irq = NULL; - } if (!data->skip_autocfg) dw8250_setup_port(p); diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index ceb85792a5cf..8d08ff5c4e34 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -39,13 +39,17 @@ static unsigned int __init serial8250_early_in(struct uart_port *port, int offset) { + offset <<= port->regshift; + switch (port->iotype) { case UPIO_MEM: return readb(port->membase + offset); + case UPIO_MEM16: + return readw(port->membase + offset); case UPIO_MEM32: - return readl(port->membase + (offset << 2)); + return readl(port->membase + offset); case UPIO_MEM32BE: - return ioread32be(port->membase + (offset << 2)); + return ioread32be(port->membase + offset); case UPIO_PORT: return inb(port->iobase + offset); default: @@ -55,15 +59,20 @@ static unsigned int __init serial8250_early_in(struct uart_port *port, int offse static void __init serial8250_early_out(struct uart_port *port, int offset, int value) { + offset <<= port->regshift; + switch (port->iotype) { case UPIO_MEM: writeb(value, port->membase + offset); break; + case UPIO_MEM16: + writew(value, port->membase + offset); + break; case UPIO_MEM32: - writel(value, port->membase + (offset << 2)); + writel(value, port->membase + offset); break; case UPIO_MEM32BE: - iowrite32be(value, port->membase + (offset << 2)); + iowrite32be(value, port->membase + offset); break; case UPIO_PORT: outb(value, port->iobase + offset); @@ -73,43 +82,27 @@ static void __init serial8250_early_out(struct uart_port *port, int offset, int #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) -static void __init wait_for_xmitr(struct uart_port *port) +static void __init serial_putc(struct uart_port *port, int c) { unsigned int status; + serial8250_early_out(port, UART_TX, c); + for (;;) { status = serial8250_early_in(port, UART_LSR); if ((status & BOTH_EMPTY) == BOTH_EMPTY) - return; + break; cpu_relax(); } } -static void __init serial_putc(struct uart_port *port, int c) -{ - wait_for_xmitr(port); - serial8250_early_out(port, UART_TX, c); -} - static void __init early_serial8250_write(struct console *console, const char *s, unsigned int count) { struct earlycon_device *device = console->data; struct uart_port *port = &device->port; - unsigned int ier; - - /* Save the IER and disable interrupts preserving the UUE bit */ - ier = serial8250_early_in(port, UART_IER); - if (ier) - serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); uart_console_write(port, s, count, serial_putc); - - /* Wait for transmitter to become empty and restore the IER */ - wait_for_xmitr(port); - - if (ier) - serial8250_early_out(port, UART_IER, ier); } static void __init init_port(struct earlycon_device *device) @@ -156,3 +149,25 @@ EARLYCON_DECLARE(uart8250, early_serial8250_setup); EARLYCON_DECLARE(uart, early_serial8250_setup); OF_EARLYCON_DECLARE(ns16550, "ns16550", early_serial8250_setup); OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup); +OF_EARLYCON_DECLARE(uart, "nvidia,tegra20-uart", early_serial8250_setup); + +#ifdef CONFIG_SERIAL_8250_OMAP + +static int __init early_omap8250_setup(struct earlycon_device *device, + const char *options) +{ + struct uart_port *port = &device->port; + + if (!(device->port.membase || device->port.iobase)) + return -ENODEV; + + port->regshift = 2; + device->con->write = early_serial8250_write; + return 0; +} + +OF_EARLYCON_DECLARE(omap8250, "ti,omap2-uart", early_omap8250_setup); +OF_EARLYCON_DECLARE(omap8250, "ti,omap3-uart", early_omap8250_setup); +OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart", early_omap8250_setup); + +#endif diff --git a/drivers/tty/serial/8250/8250_exar_st16c554.c b/drivers/tty/serial/8250/8250_exar_st16c554.c index bf53aabf9b5e..3a7cb8262bb9 100644 --- a/drivers/tty/serial/8250/8250_exar_st16c554.c +++ b/drivers/tty/serial/8250/8250_exar_st16c554.c @@ -13,20 +13,13 @@ #include <linux/init.h> #include <linux/serial_8250.h> -#define PORT(_base,_irq) \ - { \ - .iobase = _base, \ - .irq = _irq, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ - .flags = UPF_BOOT_AUTOCONF, \ - } +#include "8250.h" static struct plat_serial8250_port exar_data[] = { - PORT(0x100, 5), - PORT(0x108, 5), - PORT(0x110, 5), - PORT(0x118, 5), + SERIAL8250_PORT(0x100, 5), + SERIAL8250_PORT(0x108, 5), + SERIAL8250_PORT(0x110, 5), + SERIAL8250_PORT(0x118, 5), { }, }; diff --git a/drivers/tty/serial/8250/8250_fourport.c b/drivers/tty/serial/8250/8250_fourport.c index be1582609626..4045180a8cfc 100644 --- a/drivers/tty/serial/8250/8250_fourport.c +++ b/drivers/tty/serial/8250/8250_fourport.c @@ -10,24 +10,20 @@ #include <linux/init.h> #include <linux/serial_8250.h> -#define PORT(_base,_irq) \ - { \ - .iobase = _base, \ - .irq = _irq, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ - .flags = UPF_BOOT_AUTOCONF | UPF_FOURPORT, \ - } +#include "8250.h" + +#define SERIAL8250_FOURPORT(_base, _irq) \ + SERIAL8250_PORT_FLAGS(_base, _irq, UPF_FOURPORT) static struct plat_serial8250_port fourport_data[] = { - PORT(0x1a0, 9), - PORT(0x1a8, 9), - PORT(0x1b0, 9), - PORT(0x1b8, 9), - PORT(0x2a0, 5), - PORT(0x2a8, 5), - PORT(0x2b0, 5), - PORT(0x2b8, 5), + SERIAL8250_FOURPORT(0x1a0, 9), + SERIAL8250_FOURPORT(0x1a8, 9), + SERIAL8250_FOURPORT(0x1b0, 9), + SERIAL8250_FOURPORT(0x1b8, 9), + SERIAL8250_FOURPORT(0x2a0, 5), + SERIAL8250_FOURPORT(0x2a8, 5), + SERIAL8250_FOURPORT(0x2b0, 5), + SERIAL8250_FOURPORT(0x2b8, 5), { }, }; diff --git a/drivers/tty/serial/8250/8250_gsc.c b/drivers/tty/serial/8250/8250_gsc.c index 2e3ea1a70d7b..b1e6ae9f1ff9 100644 --- a/drivers/tty/serial/8250/8250_gsc.c +++ b/drivers/tty/serial/8250/8250_gsc.c @@ -42,7 +42,7 @@ static int __init serial_init_chip(struct parisc_device *dev) * the user what they're missing. */ if (parisc_parent(dev)->id.hw_type != HPHW_IOA) - printk(KERN_INFO + dev_info(&dev->dev, "Serial: device 0x%llx not configured.\n" "Enable support for Wax, Lasi, Asp or Dino.\n", (unsigned long long)dev->hpa.start); @@ -66,8 +66,9 @@ static int __init serial_init_chip(struct parisc_device *dev) err = serial8250_register_8250_port(&uart); if (err < 0) { - printk(KERN_WARNING - "serial8250_register_8250_port returned error %d\n", err); + dev_warn(&dev->dev, + "serial8250_register_8250_port returned error %d\n", + err); iounmap(uart.port.membase); return err; } diff --git a/drivers/tty/serial/8250/8250_hp300.c b/drivers/tty/serial/8250/8250_hp300.c index 2891958cd842..38166db2b824 100644 --- a/drivers/tty/serial/8250/8250_hp300.c +++ b/drivers/tty/serial/8250/8250_hp300.c @@ -24,8 +24,7 @@ #endif #ifdef CONFIG_HPAPCI -struct hp300_port -{ +struct hp300_port { struct hp300_port *next; /* next port */ int line; /* line (tty) number */ }; @@ -111,7 +110,7 @@ int __init hp300_setup_serial_console(void) /* Check for APCI console */ if (scode == 256) { #ifdef CONFIG_HPAPCI - printk(KERN_INFO "Serial console is HP APCI 1\n"); + pr_info("Serial console is HP APCI 1\n"); port.uartclk = HPAPCI_BAUD_BASE * 16; port.mapbase = (FRODO_BASE + FRODO_APCI_OFFSET(1)); @@ -119,7 +118,7 @@ int __init hp300_setup_serial_console(void) port.regshift = 2; add_preferred_console("ttyS", port.line, "9600n8"); #else - printk(KERN_WARNING "Serial console is APCI but support is disabled (CONFIG_HPAPCI)!\n"); + pr_warn("Serial console is APCI but support is disabled (CONFIG_HPAPCI)!\n"); return 0; #endif } else { @@ -128,7 +127,7 @@ int __init hp300_setup_serial_console(void) if (!pa) return 0; - printk(KERN_INFO "Serial console is HP DCA at select code %d\n", scode); + pr_info("Serial console is HP DCA at select code %d\n", scode); port.uartclk = HPDCA_BAUD_BASE * 16; port.mapbase = (pa + UART_OFFSET); @@ -142,13 +141,13 @@ int __init hp300_setup_serial_console(void) if (DIO_ID(pa + DIO_VIRADDRBASE) & 0x80) add_preferred_console("ttyS", port.line, "9600n8"); #else - printk(KERN_WARNING "Serial console is DCA but support is disabled (CONFIG_HPDCA)!\n"); + pr_warn("Serial console is DCA but support is disabled (CONFIG_HPDCA)!\n"); return 0; #endif } if (early_serial_setup(&port) < 0) - printk(KERN_WARNING "hp300_setup_serial_console(): early_serial_setup() failed.\n"); + pr_warn("%s: early_serial_setup() failed.\n", __func__); return 0; } #endif /* CONFIG_SERIAL_8250_CONSOLE */ @@ -180,8 +179,9 @@ static int hpdca_init_one(struct dio_dev *d, line = serial8250_register_8250_port(&uart); if (line < 0) { - printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d" - " irq %d failed\n", d->scode, uart.port.irq); + dev_notice(&d->dev, + "8250_hp300: register_serial() DCA scode %d irq %d failed\n", + d->scode, uart.port.irq); return -ENOMEM; } @@ -249,8 +249,8 @@ static int __init hp300_8250_init(void) /* Memory mapped I/O */ uart.port.iotype = UPIO_MEM; - uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \ - | UPF_BOOT_AUTOCONF; + uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ + | UPF_BOOT_AUTOCONF; /* XXX - no interrupt support yet */ uart.port.irq = 0; uart.port.uartclk = HPAPCI_BAUD_BASE * 16; @@ -261,8 +261,9 @@ static int __init hp300_8250_init(void) line = serial8250_register_8250_port(&uart); if (line < 0) { - printk(KERN_NOTICE "8250_hp300: register_serial() APCI" - " %d irq %d failed\n", i, uart.port.irq); + dev_notice(uart.port.dev, + "8250_hp300: register_serial() APCI %d irq %d failed\n", + i, uart.port.irq); kfree(port); continue; } diff --git a/drivers/tty/serial/8250/8250_hub6.c b/drivers/tty/serial/8250/8250_hub6.c index a5c778e83de0..27124e21eb96 100644 --- a/drivers/tty/serial/8250/8250_hub6.c +++ b/drivers/tty/serial/8250/8250_hub6.c @@ -10,7 +10,7 @@ #include <linux/init.h> #include <linux/serial_8250.h> -#define HUB6(card,port) \ +#define HUB6(card, port) \ { \ .iobase = 0x302, \ .irq = 3, \ diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c index 49394b4c5cfd..b0677f610863 100644 --- a/drivers/tty/serial/8250/8250_ingenic.c +++ b/drivers/tty/serial/8250/8250_ingenic.c @@ -48,6 +48,7 @@ static const struct of_device_id of_match[]; #define UART_MCR_MDCE BIT(7) #define UART_MCR_FCM BIT(6) +#if defined(CONFIG_SERIAL_EARLYCON) && !defined(MODULE) static struct earlycon_device *early_device; static uint8_t __init early_in(struct uart_port *port, int offset) @@ -140,6 +141,7 @@ OF_EARLYCON_DECLARE(jz4775_uart, "ingenic,jz4775-uart", EARLYCON_DECLARE(jz4780_uart, ingenic_early_console_setup); OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart", ingenic_early_console_setup); +#endif /* CONFIG_SERIAL_EARLYCON */ static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) { @@ -152,14 +154,18 @@ static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) break; case UART_IER: - /* Enable receive timeout interrupt with the - * receive line status interrupt */ + /* + * Enable receive timeout interrupt with the receive line + * status interrupt. + */ value |= (value & 0x4) << 2; break; case UART_MCR: - /* If we have enabled modem status IRQs we should enable modem - * mode. */ + /* + * If we have enabled modem status IRQs we should enable + * modem mode. + */ ier = p->serial_in(p, UART_IER); if (ier & UART_IER_MSI) diff --git a/drivers/tty/serial/8250/8250_moxa.c b/drivers/tty/serial/8250/8250_moxa.c new file mode 100644 index 000000000000..26eb5393a263 --- /dev/null +++ b/drivers/tty/serial/8250/8250_moxa.c @@ -0,0 +1,157 @@ +/* + * 8250_moxa.c - MOXA Smartio/Industio MUE multiport serial driver. + * + * Author: Mathieu OTHACEHE <m.othacehe@gmail.com> + * + * 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. + */ + +#include <linux/module.h> +#include <linux/pci.h> + +#include "8250.h" + +#define PCI_DEVICE_ID_MOXA_CP102E 0x1024 +#define PCI_DEVICE_ID_MOXA_CP102EL 0x1025 +#define PCI_DEVICE_ID_MOXA_CP104EL_A 0x1045 +#define PCI_DEVICE_ID_MOXA_CP114EL 0x1144 +#define PCI_DEVICE_ID_MOXA_CP116E_A_A 0x1160 +#define PCI_DEVICE_ID_MOXA_CP116E_A_B 0x1161 +#define PCI_DEVICE_ID_MOXA_CP118EL_A 0x1182 +#define PCI_DEVICE_ID_MOXA_CP118E_A_I 0x1183 +#define PCI_DEVICE_ID_MOXA_CP132EL 0x1322 +#define PCI_DEVICE_ID_MOXA_CP134EL_A 0x1342 +#define PCI_DEVICE_ID_MOXA_CP138E_A 0x1381 +#define PCI_DEVICE_ID_MOXA_CP168EL_A 0x1683 + +#define MOXA_BASE_BAUD 921600 +#define MOXA_UART_OFFSET 0x200 +#define MOXA_BASE_BAR 1 + +struct moxa8250_board { + unsigned int num_ports; + int line[0]; +}; + +enum { + moxa8250_2p = 0, + moxa8250_4p, + moxa8250_8p +}; + +static struct moxa8250_board moxa8250_boards[] = { + [moxa8250_2p] = { .num_ports = 2}, + [moxa8250_4p] = { .num_ports = 4}, + [moxa8250_8p] = { .num_ports = 8}, +}; + +static int moxa8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct uart_8250_port uart; + struct moxa8250_board *brd; + void __iomem *ioaddr; + resource_size_t baseaddr; + unsigned int i, nr_ports; + unsigned int offset; + int ret; + + brd = &moxa8250_boards[id->driver_data]; + nr_ports = brd->num_ports; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + brd = devm_kzalloc(&pdev->dev, sizeof(struct moxa8250_board) + + sizeof(unsigned int) * nr_ports, GFP_KERNEL); + if (!brd) + return -ENOMEM; + + memset(&uart, 0, sizeof(struct uart_8250_port)); + + uart.port.dev = &pdev->dev; + uart.port.irq = pdev->irq; + uart.port.uartclk = MOXA_BASE_BAUD * 16; + uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + + baseaddr = pci_resource_start(pdev, MOXA_BASE_BAR); + ioaddr = pcim_iomap(pdev, MOXA_BASE_BAR, 0); + if (!ioaddr) + return -ENOMEM; + + for (i = 0; i < nr_ports; i++) { + + /* + * MOXA Smartio MUE boards with 4 ports have + * a different offset for port #3 + */ + if (nr_ports == 4 && i == 3) + offset = 7 * MOXA_UART_OFFSET; + else + offset = i * MOXA_UART_OFFSET; + + uart.port.iotype = UPIO_MEM; + uart.port.iobase = 0; + uart.port.mapbase = baseaddr + offset; + uart.port.membase = ioaddr + offset; + uart.port.regshift = 0; + + dev_dbg(&pdev->dev, "Setup PCI port: port %lx, irq %d, type %d\n", + uart.port.iobase, uart.port.irq, uart.port.iotype); + + brd->line[i] = serial8250_register_8250_port(&uart); + if (brd->line[i] < 0) { + dev_err(&pdev->dev, + "Couldn't register serial port %lx, irq %d, type %d, error %d\n", + uart.port.iobase, uart.port.irq, + uart.port.iotype, brd->line[i]); + break; + } + } + + pci_set_drvdata(pdev, brd); + return 0; +} + +static void moxa8250_remove(struct pci_dev *pdev) +{ + struct moxa8250_board *brd = pci_get_drvdata(pdev); + unsigned int i; + + for (i = 0; i < brd->num_ports; i++) + serial8250_unregister_port(brd->line[i]); +} + +#define MOXA_DEVICE(id, data) { PCI_VDEVICE(MOXA, id), (kernel_ulong_t)data } + +static const struct pci_device_id pci_ids[] = { + MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP102E, moxa8250_2p), + MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP102EL, moxa8250_2p), + MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP104EL_A, moxa8250_4p), + MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP114EL, moxa8250_4p), + MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP116E_A_A, moxa8250_8p), + MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP116E_A_B, moxa8250_8p), + MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP118EL_A, moxa8250_8p), + MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP118E_A_I, moxa8250_8p), + MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP132EL, moxa8250_2p), + MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP134EL_A, moxa8250_4p), + MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP138E_A, moxa8250_8p), + MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP168EL_A, moxa8250_8p), + {0} +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver moxa8250_pci_driver = { + .name = "8250_moxa", + .id_table = pci_ids, + .probe = moxa8250_probe, + .remove = moxa8250_remove, +}; + +module_pci_driver(moxa8250_pci_driver); + +MODULE_AUTHOR("Mathieu OTHACEHE"); +MODULE_DESCRIPTION("MOXA SmartIO MUE driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 78883ca64ddd..3489fbcb7313 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -41,12 +41,10 @@ static void mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { + struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; unsigned int baud, quot; - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - serial8250_do_set_termios(port, termios, old); /* @@ -116,7 +114,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, tty_termios_encode_baud_rate(termios, baud, baud); } -static int mtk8250_runtime_suspend(struct device *dev) +static int __maybe_unused mtk8250_runtime_suspend(struct device *dev) { struct mtk8250_data *data = dev_get_drvdata(dev); @@ -126,7 +124,7 @@ static int mtk8250_runtime_suspend(struct device *dev) return 0; } -static int mtk8250_runtime_resume(struct device *dev) +static int __maybe_unused mtk8250_runtime_resume(struct device *dev) { struct mtk8250_data *data = dev_get_drvdata(dev); int err; @@ -262,8 +260,7 @@ static int mtk8250_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int mtk8250_suspend(struct device *dev) +static int __maybe_unused mtk8250_suspend(struct device *dev) { struct mtk8250_data *data = dev_get_drvdata(dev); @@ -272,7 +269,7 @@ static int mtk8250_suspend(struct device *dev) return 0; } -static int mtk8250_resume(struct device *dev) +static int __maybe_unused mtk8250_resume(struct device *dev) { struct mtk8250_data *data = dev_get_drvdata(dev); @@ -280,7 +277,6 @@ static int mtk8250_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops mtk8250_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(mtk8250_suspend, mtk8250_resume) @@ -305,7 +301,7 @@ static struct platform_driver mtk8250_platform_driver = { }; module_platform_driver(mtk8250_platform_driver); -#ifdef CONFIG_SERIAL_8250_CONSOLE +#if defined(CONFIG_SERIAL_8250_CONSOLE) && !defined(MODULE) static int __init early_mtk8250_setup(struct earlycon_device *device, const char *options) { diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/8250/8250_of.c index de5029649795..c7ed3d2bc8b2 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -18,14 +18,9 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> -#include <linux/nwpserial.h> #include <linux/clk.h> -#ifdef CONFIG_SERIAL_8250_MODULE -#define CONFIG_SERIAL_8250 CONFIG_SERIAL_8250_MODULE -#endif - -#include "8250/8250.h" +#include "8250.h" struct of_serial_info { struct clk *clk; @@ -122,6 +117,9 @@ static int of_platform_serial_setup(struct platform_device *ofdev, case 1: port->iotype = UPIO_MEM; break; + case 2: + port->iotype = UPIO_MEM16; + break; case 4: port->iotype = of_device_is_big_endian(np) ? UPIO_MEM32BE : UPIO_MEM32; @@ -195,7 +193,6 @@ static int of_platform_serial_probe(struct platform_device *ofdev) goto out; switch (port_type) { -#ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: { struct uart_8250_port port8250; @@ -212,12 +209,6 @@ static int of_platform_serial_probe(struct platform_device *ofdev) ret = serial8250_register_8250_port(&port8250); break; } -#endif -#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL - case PORT_NWPSERIAL: - ret = nwpserial_register_port(&port); - break; -#endif default: /* need to add code for these */ case PORT_UNKNOWN: @@ -245,16 +236,9 @@ static int of_platform_serial_remove(struct platform_device *ofdev) { struct of_serial_info *info = platform_get_drvdata(ofdev); switch (info->type) { -#ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: serial8250_unregister_port(info->line); break; -#endif -#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL - case PORT_NWPSERIAL: - nwpserial_unregister_port(info->line); - break; -#endif default: /* need to add code for these */ break; @@ -267,7 +251,6 @@ static int of_platform_serial_remove(struct platform_device *ofdev) } #ifdef CONFIG_PM_SLEEP -#ifdef CONFIG_SERIAL_8250 static void of_serial_suspend_8250(struct of_serial_info *info) { struct uart_8250_port *port8250 = serial8250_get_port(info->line); @@ -288,15 +271,6 @@ static void of_serial_resume_8250(struct of_serial_info *info) serial8250_resume_port(info->line); } -#else -static inline void of_serial_suspend_8250(struct of_serial_info *info) -{ -} - -static inline void of_serial_resume_8250(struct of_serial_info *info) -{ -} -#endif static int of_serial_suspend(struct device *dev) { @@ -353,10 +327,6 @@ static const struct of_device_id of_platform_serial_table[] = { .data = (void *)PORT_XSCALE, }, { .compatible = "mrvl,pxa-uart", .data = (void *)PORT_XSCALE, }, -#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL - { .compatible = "ibm,qpace-nwp-serial", - .data = (void *)PORT_NWPSERIAL, }, -#endif { /* end of list */ }, }; MODULE_DEVICE_TABLE(of, of_platform_serial_table); @@ -365,6 +335,7 @@ static struct platform_driver of_platform_serial_driver = { .driver = { .name = "of_serial", .of_match_table = of_platform_serial_table, + .pm = &of_serial_pm_ops, }, .probe = of_platform_serial_probe, .remove = of_platform_serial_remove, diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index a2c0734c76e2..6f760510e46d 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -318,8 +318,7 @@ static void omap_8250_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); struct omap8250_priv *priv = up->port.private_data; unsigned char cval = 0; unsigned int baud; @@ -682,9 +681,8 @@ static void omap_8250_shutdown(struct uart_port *port) static void omap_8250_throttle(struct uart_port *port) { + struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); pm_runtime_get_sync(port->dev); @@ -697,11 +695,40 @@ static void omap_8250_throttle(struct uart_port *port) pm_runtime_put_autosuspend(port->dev); } +static int omap_8250_rs485_config(struct uart_port *port, + struct serial_rs485 *rs485) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + /* Clamp the delays to [0, 100ms] */ + rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U); + rs485->delay_rts_after_send = min(rs485->delay_rts_after_send, 100U); + + port->rs485 = *rs485; + + /* + * Both serial8250_em485_init and serial8250_em485_destroy + * are idempotent + */ + if (rs485->flags & SER_RS485_ENABLED) { + int ret = serial8250_em485_init(up); + + if (ret) { + rs485->flags &= ~SER_RS485_ENABLED; + port->rs485.flags &= ~SER_RS485_ENABLED; + } + return ret; + } + + serial8250_em485_destroy(up); + + return 0; +} + static void omap_8250_unthrottle(struct uart_port *port) { + struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); pm_runtime_get_sync(port->dev); @@ -1146,6 +1173,7 @@ static int omap8250_probe(struct platform_device *pdev) up.port.shutdown = omap_8250_shutdown; up.port.throttle = omap_8250_throttle; up.port.unthrottle = omap_8250_unthrottle; + up.port.rs485_config = omap_8250_rs485_config; if (pdev->dev.of_node) { const struct of_device_id *id; diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 4097f3f65b3b..98862aa5bb58 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -55,7 +55,6 @@ struct pci_serial_quirk { struct serial_private { struct pci_dev *dev; unsigned int nr; - void __iomem *remapped_bar[PCI_NUM_BAR_RESOURCES]; struct pci_serial_quirk *quirk; int line[0]; }; @@ -85,15 +84,13 @@ setup_port(struct serial_private *priv, struct uart_8250_port *port, return -EINVAL; if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { - if (!priv->remapped_bar[bar]) - priv->remapped_bar[bar] = pci_ioremap_bar(dev, bar); - if (!priv->remapped_bar[bar]) + if (!pcim_iomap(dev, bar, 0) && !pcim_iomap_table(dev)) return -ENOMEM; port->port.iotype = UPIO_MEM; port->port.iobase = 0; port->port.mapbase = pci_resource_start(dev, bar) + offset; - port->port.membase = priv->remapped_bar[bar] + offset; + port->port.membase = pcim_iomap_table(dev)[bar] + offset; port->port.regshift = regshift; } else { port->port.iotype = UPIO_PORT; @@ -721,7 +718,7 @@ static int pci_ni8430_init(struct pci_dev *dev) */ pcibios_resource_to_bus(dev->bus, ®ion, &dev->resource[bar]); device_window = ((region.start + MITE_IOWBSR1_WIN_OFFSET) & 0xffffff00) - | MITE_IOWBSR1_WENAB | MITE_IOWBSR1_WSIZE; + | MITE_IOWBSR1_WENAB | MITE_IOWBSR1_WSIZE; writel(device_window, p + MITE_IOWBSR1); /* Set window access to go to RAMSEL IO address space */ @@ -803,12 +800,12 @@ static int pci_netmos_9900_numports(struct pci_dev *dev) unsigned int pi; unsigned short sub_serports; - pi = (c & 0xff); + pi = c & 0xff; - if (pi == 2) { + if (pi == 2) return 1; - } else if ((pi == 0) && - (dev->device == PCI_DEVICE_ID_NETMOS_9900)) { + + if ((pi == 0) && (dev->device == PCI_DEVICE_ID_NETMOS_9900)) { /* two possibilities: 0x30ps encodes number of parallel and * serial ports, or 0x1000 indicates *something*. This is not * immediately obvious, since the 2s1p+4s configuration seems @@ -816,12 +813,12 @@ static int pci_netmos_9900_numports(struct pci_dev *dev) * advertising the same function 3 as the 4s+2s1p config. */ sub_serports = dev->subsystem_device & 0xf; - if (sub_serports > 0) { + if (sub_serports > 0) return sub_serports; - } else { - dev_err(&dev->dev, "NetMos/Mostech serial driver ignoring port on ambiguous config.\n"); - return 0; - } + + dev_err(&dev->dev, + "NetMos/Mostech serial driver ignoring port on ambiguous config.\n"); + return 0; } moan_device("unknown NetMos/Mostech program interface", dev); @@ -842,21 +839,21 @@ static int pci_netmos_init(struct pci_dev *dev) return 0; switch (dev->device) { /* FALLTHROUGH on all */ - case PCI_DEVICE_ID_NETMOS_9904: - case PCI_DEVICE_ID_NETMOS_9912: - case PCI_DEVICE_ID_NETMOS_9922: - case PCI_DEVICE_ID_NETMOS_9900: - num_serial = pci_netmos_9900_numports(dev); - break; + case PCI_DEVICE_ID_NETMOS_9904: + case PCI_DEVICE_ID_NETMOS_9912: + case PCI_DEVICE_ID_NETMOS_9922: + case PCI_DEVICE_ID_NETMOS_9900: + num_serial = pci_netmos_9900_numports(dev); + break; - default: - if (num_serial == 0 ) { - moan_device("unknown NetMos/Mostech device", dev); - } + default: + break; } - if (num_serial == 0) + if (num_serial == 0) { + moan_device("unknown NetMos/Mostech device", dev); return -ENODEV; + } return num_serial; } @@ -1198,8 +1195,9 @@ static int pci_quatech_has_qmcr(struct uart_8250_port *port) static int pci_quatech_test(struct uart_8250_port *port) { - u8 reg; - u8 qopr = pci_quatech_rqopr(port); + u8 reg, qopr; + + qopr = pci_quatech_rqopr(port); pci_quatech_wqopr(port, qopr & QPCR_TEST_FOR1); reg = pci_quatech_rqopr(port) & 0xC0; if (reg != QPCR_TEST_GET1) @@ -1286,6 +1284,7 @@ static int pci_quatech_init(struct pci_dev *dev) unsigned long base = pci_resource_start(dev, 0); if (base) { u32 tmp; + outl(inl(base + 0x38) | 0x00002000, base + 0x38); tmp = inl(base + 0x3c); outl(tmp | 0x01000000, base + 0x3c); @@ -1334,29 +1333,6 @@ static int pci_default_setup(struct serial_private *priv, return setup_port(priv, port, bar, offset, board->reg_shift); } -static int pci_pericom_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_8250_port *port, int idx) -{ - unsigned int bar, offset = board->first_offset, maxnr; - - bar = FL_GET_BASE(board->flags); - if (board->flags & FL_BASE_BARS) - bar += idx; - else - offset += idx * board->uart_offset; - - maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >> - (board->reg_shift + 3); - - if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) - return 1; - - port->port.uartclk = 14745600; - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - static int ce4100_serial_setup(struct serial_private *priv, const struct pciserial_board *board, @@ -1379,6 +1355,9 @@ ce4100_serial_setup(struct serial_private *priv, #define PCI_DEVICE_ID_INTEL_BSW_UART1 0x228a #define PCI_DEVICE_ID_INTEL_BSW_UART2 0x228c +#define PCI_DEVICE_ID_INTEL_BDW_UART1 0x9ce3 +#define PCI_DEVICE_ID_INTEL_BDW_UART2 0x9ce4 + #define BYT_PRV_CLK 0x800 #define BYT_PRV_CLK_EN (1 << 0) #define BYT_PRV_CLK_M_VAL_SHIFT 1 @@ -1461,11 +1440,13 @@ byt_serial_setup(struct serial_private *priv, switch (pdev->device) { case PCI_DEVICE_ID_INTEL_BYT_UART1: case PCI_DEVICE_ID_INTEL_BSW_UART1: + case PCI_DEVICE_ID_INTEL_BDW_UART1: rx_param->src_id = 3; tx_param->dst_id = 2; break; case PCI_DEVICE_ID_INTEL_BYT_UART2: case PCI_DEVICE_ID_INTEL_BSW_UART2: + case PCI_DEVICE_ID_INTEL_BDW_UART2: rx_param->src_id = 5; tx_param->dst_id = 4; break; @@ -1536,10 +1517,9 @@ pci_brcm_trumanage_setup(struct serial_private *priv, static int pci_fintek_rs485_config(struct uart_port *port, struct serial_rs485 *rs485) { + struct pci_dev *pci_dev = to_pci_dev(port->dev); u8 setting; u8 *index = (u8 *) port->private_data; - struct pci_dev *pci_dev = container_of(port->dev, struct pci_dev, - dev); pci_read_config_byte(pci_dev, 0x40 + 8 * *index + 7, &setting); @@ -1761,7 +1741,7 @@ xr17v35x_has_slave(struct serial_private *priv) const int dev_id = priv->dev->device; return ((dev_id == PCI_DEVICE_ID_EXAR_XR17V4358) || - (dev_id == PCI_DEVICE_ID_EXAR_XR17V8358)); + (dev_id == PCI_DEVICE_ID_EXAR_XR17V8358)); } static int @@ -1861,8 +1841,8 @@ pci_fastcom335_setup(struct serial_private *priv, static int pci_wch_ch353_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_8250_port *port, int idx) + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) { port->port.flags |= UPF_FIXED_TYPE; port->port.type = PORT_16550A; @@ -1871,8 +1851,8 @@ pci_wch_ch353_setup(struct serial_private *priv, static int pci_wch_ch38x_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_8250_port *port, int idx) + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) { port->port.flags |= UPF_FIXED_TYPE; port->port.type = PORT_16850; @@ -1936,6 +1916,7 @@ pci_wch_ch38x_setup(struct serial_private *priv, #define PCIE_VENDOR_ID_WCH 0x1c00 #define PCIE_DEVICE_ID_WCH_CH382_2S1P 0x3250 #define PCIE_DEVICE_ID_WCH_CH384_4S 0x3470 +#define PCIE_DEVICE_ID_WCH_CH382_2S 0x3253 #define PCI_VENDOR_ID_PERICOM 0x12D8 #define PCI_DEVICE_ID_PERICOM_PI7C9X7951 0x7951 @@ -2062,6 +2043,20 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = byt_serial_setup, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BDW_UART1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = byt_serial_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BDW_UART2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = byt_serial_setup, + }, /* * ITE */ @@ -2226,16 +2221,6 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .exit = pci_plx9050_exit, }, /* - * Pericom - */ - { - .vendor = PCI_VENDOR_ID_PERICOM, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, - }, - /* * PLX */ { @@ -2618,6 +2603,14 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = pci_wch_ch353_setup, }, + /* WCH CH382 2S card (16850 clone) */ + { + .vendor = PCIE_VENDOR_ID_WCH, + .device = PCIE_DEVICE_ID_WCH_CH382_2S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch38x_setup, + }, /* WCH CH382 2S1P card (16850 clone) */ { .vendor = PCIE_VENDOR_ID_WCH, @@ -2936,6 +2929,7 @@ enum pci_board_num_t { pbn_fintek_4, pbn_fintek_8, pbn_fintek_12, + pbn_wch382_2, pbn_wch384_4, pbn_pericom_PI7C9X7951, pbn_pericom_PI7C9X7952, @@ -3704,15 +3698,10 @@ static struct pciserial_board pci_boards[] = { .base_baud = 921600, .reg_shift = 2, }, - /* - * Intel BayTrail HSUART reference clock is 44.2368 MHz at power-on, - * but is overridden by byt_set_termios. - */ [pbn_byt] = { .flags = FL_BASE0, .num_ports = 1, .base_baud = 2764800, - .uart_offset = 0x80, .reg_shift = 2, }, [pbn_qrk] = { @@ -3756,6 +3745,13 @@ static struct pciserial_board pci_boards[] = { .base_baud = 115200, .first_offset = 0x40, }, + [pbn_wch382_2] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + .first_offset = 0xC0, + }, [pbn_wch384_4] = { .flags = FL_BASE0, .num_ports = 4, @@ -3804,6 +3800,20 @@ static const struct pci_device_id blacklist[] = { { PCI_DEVICE(0x1c00, 0x3250), }, /* WCH CH382 2S1P */ { PCI_DEVICE(0x1c00, 0x3470), }, /* WCH CH384 4S */ + /* Moxa Smartio MUE boards handled by 8250_moxa */ + { PCI_VDEVICE(MOXA, 0x1024), }, + { PCI_VDEVICE(MOXA, 0x1025), }, + { PCI_VDEVICE(MOXA, 0x1045), }, + { PCI_VDEVICE(MOXA, 0x1144), }, + { PCI_VDEVICE(MOXA, 0x1160), }, + { PCI_VDEVICE(MOXA, 0x1161), }, + { PCI_VDEVICE(MOXA, 0x1182), }, + { PCI_VDEVICE(MOXA, 0x1183), }, + { PCI_VDEVICE(MOXA, 0x1322), }, + { PCI_VDEVICE(MOXA, 0x1342), }, + { PCI_VDEVICE(MOXA, 0x1381), }, + { PCI_VDEVICE(MOXA, 0x1683), }, + /* Intel platforms with MID UART */ { PCI_VDEVICE(INTEL, 0x081b), }, { PCI_VDEVICE(INTEL, 0x081c), }, @@ -3991,12 +4001,6 @@ void pciserial_remove_ports(struct serial_private *priv) for (i = 0; i < priv->nr; i++) serial8250_unregister_port(priv->line[i]); - for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { - if (priv->remapped_bar[i]) - iounmap(priv->remapped_bar[i]); - priv->remapped_bar[i] = NULL; - } - /* * Find the exit quirks. */ @@ -4068,7 +4072,7 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) board = &pci_boards[ent->driver_data]; - rc = pci_enable_device(dev); + rc = pcim_enable_device(dev); pci_save_state(dev); if (rc) return rc; @@ -4087,7 +4091,7 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) */ rc = serial_pci_guess_board(dev, &tmp); if (rc) - goto disable; + return rc; } else { /* * We matched an explicit entry. If we are able to @@ -4103,16 +4107,11 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) } priv = pciserial_init_ports(dev, board); - if (!IS_ERR(priv)) { - pci_set_drvdata(dev, priv); - return 0; - } + if (IS_ERR(priv)) + return PTR_ERR(priv); - rc = PTR_ERR(priv); - - disable: - pci_disable_device(dev); - return rc; + pci_set_drvdata(dev, priv); + return 0; } static void pciserial_remove_one(struct pci_dev *dev) @@ -4120,8 +4119,6 @@ static void pciserial_remove_one(struct pci_dev *dev) struct serial_private *priv = pci_get_drvdata(dev); pciserial_remove_ports(priv); - - pci_disable_device(dev); } #ifdef CONFIG_PM_SLEEP @@ -4502,7 +4499,7 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_921600 }, { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958, - PCI_ANY_ID , PCI_ANY_ID, 0, 0, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_8_1152000 }, /* @@ -5506,6 +5503,16 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000, pbn_byt }, + /* Intel Broadwell */ + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BDW_UART1, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000, + pbn_byt }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BDW_UART2, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000, + pbn_byt }, + /* * Intel Quark x1000 */ @@ -5545,6 +5552,10 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, + { PCIE_VENDOR_ID_WCH, PCIE_DEVICE_ID_WCH_CH382_2S, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, pbn_wch382_2 }, + { PCIE_VENDOR_ID_WCH, PCIE_DEVICE_ID_WCH_CH384_4S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_wch384_4 }, diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 658b392d1170..34f05ed78b68 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -357,8 +357,8 @@ static const struct pnp_device_id pnp_dev_table[] = { /* Fujitsu Wacom 1FGT Tablet PC device */ { "FUJ02E9", 0 }, /* - * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 in - * disguise) + * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 + * in disguise). */ { "LTS0001", 0 }, /* Rockwell's (PORALiNK) 33600 INT PNP */ @@ -367,12 +367,14 @@ static const struct pnp_device_id pnp_dev_table[] = { { "PNPCXXX", UNKNOWN_DEV }, /* More unknown PnP modems */ { "PNPDXXX", UNKNOWN_DEV }, - /* Winbond CIR port, should not be probed. We should keep track - of it to prevent the legacy serial driver from probing it */ + /* + * Winbond CIR port, should not be probed. We should keep track of + * it to prevent the legacy serial driver from probing it. + */ { "WEC1022", CIR_PORT }, /* - * SMSC IrCC SIR/FIR port, should not be probed by serial driver - * as well so its own driver can bind to it. + * SMSC IrCC SIR/FIR port, should not be probed by serial driver as + * well so its own driver can bind to it. */ { "SMCF010", CIR_PORT }, { "", 0 } @@ -380,35 +382,35 @@ static const struct pnp_device_id pnp_dev_table[] = { MODULE_DEVICE_TABLE(pnp, pnp_dev_table); -static char *modem_names[] = { +static const char *modem_names[] = { "MODEM", "Modem", "modem", "FAX", "Fax", "fax", "56K", "56k", "K56", "33.6", "28.8", "14.4", "33,600", "28,800", "14,400", "33.600", "28.800", "14.400", "33600", "28800", "14400", "V.90", "V.34", "V.32", NULL }; -static int check_name(char *name) +static bool check_name(const char *name) { - char **tmp; + const char **tmp; for (tmp = modem_names; *tmp; tmp++) if (strstr(name, *tmp)) - return 1; + return true; - return 0; + return false; } -static int check_resources(struct pnp_dev *dev) +static bool check_resources(struct pnp_dev *dev) { - resource_size_t base[] = {0x2f8, 0x3f8, 0x2e8, 0x3e8}; - int i; + static const resource_size_t base[] = {0x2f8, 0x3f8, 0x2e8, 0x3e8}; + unsigned int i; for (i = 0; i < ARRAY_SIZE(base); i++) { if (pnp_possible_config(dev, IORESOURCE_IO, base[i], 8)) - return 1; + return true; } - return 0; + return false; } /* @@ -425,8 +427,8 @@ static int check_resources(struct pnp_dev *dev) static int serial_pnp_guess_board(struct pnp_dev *dev) { if (!(check_name(pnp_dev_name(dev)) || - (dev->card && check_name(dev->card->name)))) - return -ENODEV; + (dev->card && check_name(dev->card->name)))) + return -ENODEV; if (check_resources(dev)) return 0; @@ -462,11 +464,11 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) } else return -ENODEV; -#ifdef SERIAL_DEBUG_PNP - printk(KERN_DEBUG - "Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n", - uart.port.iobase, uart.port.mapbase, uart.port.irq, uart.port.iotype); -#endif + dev_dbg(&dev->dev, + "Setup PNP port: port %lx, mem %pa, irq %d, type %d\n", + uart.port.iobase, &uart.port.mapbase, + uart.port.irq, uart.port.iotype); + if (flags & CIR_PORT) { uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE; uart.port.type = PORT_8250_CIR; diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 52d82d2ac726..e213da01a3d7 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -37,6 +37,7 @@ #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/pm_runtime.h> +#include <linux/timer.h> #include <asm/io.h> #include <asm/irq.h> @@ -52,7 +53,7 @@ #define DEBUG_AUTOCONF(fmt...) do { } while (0) #endif -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) /* * Here we define the default xmit fifo size used for each type of UART. @@ -250,9 +251,11 @@ static const struct serial8250_config uart_config[] = { .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO | UART_CAP_AFE, }, -/* tx_loadsz is set to 63-bytes instead of 64-bytes to implement -workaround of errata A-008006 which states that tx_loadsz should be -configured less than Maximum supported fifo bytes */ + /* + * tx_loadsz is set to 63-bytes instead of 64-bytes to implement + * workaround of errata A-008006 which states that tx_loadsz should + * be configured less than Maximum supported fifo bytes. + */ [PORT_16550A_FSL64] = { .name = "16550A_FSL64", .fifo_size = 64, @@ -368,6 +371,18 @@ static void mem_serial_out(struct uart_port *p, int offset, int value) writeb(value, p->membase + offset); } +static void mem16_serial_out(struct uart_port *p, int offset, int value) +{ + offset = offset << p->regshift; + writew(value, p->membase + offset); +} + +static unsigned int mem16_serial_in(struct uart_port *p, int offset) +{ + offset = offset << p->regshift; + return readw(p->membase + offset); +} + static void mem32_serial_out(struct uart_port *p, int offset, int value) { offset = offset << p->regshift; @@ -425,6 +440,11 @@ static void set_io_from_upio(struct uart_port *p) p->serial_out = mem_serial_out; break; + case UPIO_MEM16: + p->serial_in = mem16_serial_in; + p->serial_out = mem16_serial_out; + break; + case UPIO_MEM32: p->serial_in = mem32_serial_in; p->serial_out = mem32_serial_out; @@ -459,6 +479,7 @@ serial_port_out_sync(struct uart_port *p, int offset, int value) { switch (p->iotype) { case UPIO_MEM: + case UPIO_MEM16: case UPIO_MEM32: case UPIO_MEM32BE: case UPIO_AU: @@ -504,6 +525,20 @@ static void serial8250_clear_fifos(struct uart_8250_port *p) } } +static inline void serial8250_em485_rts_after_send(struct uart_8250_port *p) +{ + unsigned char mcr = serial_in(p, UART_MCR); + + if (p->port.rs485.flags & SER_RS485_RTS_AFTER_SEND) + mcr |= UART_MCR_RTS; + else + mcr &= ~UART_MCR_RTS; + serial_out(p, UART_MCR, mcr); +} + +static void serial8250_em485_handle_start_tx(unsigned long arg); +static void serial8250_em485_handle_stop_tx(unsigned long arg); + void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p) { serial8250_clear_fifos(p); @@ -528,6 +563,73 @@ void serial8250_rpm_put(struct uart_8250_port *p) } EXPORT_SYMBOL_GPL(serial8250_rpm_put); +/** + * serial8250_em485_init() - put uart_8250_port into rs485 emulating + * @p: uart_8250_port port instance + * + * The function is used to start rs485 software emulating on the + * &struct uart_8250_port* @p. Namely, RTS is switched before/after + * transmission. The function is idempotent, so it is safe to call it + * multiple times. + * + * The caller MUST enable interrupt on empty shift register before + * calling serial8250_em485_init(). This interrupt is not a part of + * 8250 standard, but implementation defined. + * + * The function is supposed to be called from .rs485_config callback + * or from any other callback protected with p->port.lock spinlock. + * + * See also serial8250_em485_destroy() + * + * Return 0 - success, -errno - otherwise + */ +int serial8250_em485_init(struct uart_8250_port *p) +{ + if (p->em485 != NULL) + return 0; + + p->em485 = kmalloc(sizeof(struct uart_8250_em485), GFP_ATOMIC); + if (p->em485 == NULL) + return -ENOMEM; + + setup_timer(&p->em485->stop_tx_timer, + serial8250_em485_handle_stop_tx, (unsigned long)p); + setup_timer(&p->em485->start_tx_timer, + serial8250_em485_handle_start_tx, (unsigned long)p); + p->em485->active_timer = NULL; + + serial8250_em485_rts_after_send(p); + + return 0; +} +EXPORT_SYMBOL_GPL(serial8250_em485_init); + +/** + * serial8250_em485_destroy() - put uart_8250_port into normal state + * @p: uart_8250_port port instance + * + * The function is used to stop rs485 software emulating on the + * &struct uart_8250_port* @p. The function is idempotent, so it is safe to + * call it multiple times. + * + * The function is supposed to be called from .rs485_config callback + * or from any other callback protected with p->port.lock spinlock. + * + * See also serial8250_em485_init() + */ +void serial8250_em485_destroy(struct uart_8250_port *p) +{ + if (p->em485 == NULL) + return; + + del_timer(&p->em485->start_tx_timer); + del_timer(&p->em485->stop_tx_timer); + + kfree(p->em485); + p->em485 = NULL; +} +EXPORT_SYMBOL_GPL(serial8250_em485_destroy); + /* * These two wrappers ensure that enable_runtime_pm_tx() can be called more than * once and disable_runtime_pm_tx() will still disable RPM because the fifo is @@ -713,22 +815,16 @@ static int size_fifo(struct uart_8250_port *up) */ static unsigned int autoconfig_read_divisor_id(struct uart_8250_port *p) { - unsigned char old_dll, old_dlm, old_lcr; - unsigned int id; + unsigned char old_lcr; + unsigned int id, old_dl; old_lcr = serial_in(p, UART_LCR); serial_out(p, UART_LCR, UART_LCR_CONF_MODE_A); + old_dl = serial_dl_read(p); + serial_dl_write(p, 0); + id = serial_dl_read(p); + serial_dl_write(p, old_dl); - old_dll = serial_in(p, UART_DLL); - old_dlm = serial_in(p, UART_DLM); - - serial_out(p, UART_DLL, 0); - serial_out(p, UART_DLM, 0); - - id = serial_in(p, UART_DLL) | serial_in(p, UART_DLM) << 8; - - serial_out(p, UART_DLL, old_dll); - serial_out(p, UART_DLM, old_dlm); serial_out(p, UART_LCR, old_lcr); return id; @@ -1220,8 +1316,7 @@ static void autoconfig(struct uart_8250_port *up) out_lock: spin_unlock_irqrestore(&port->lock, flags); if (up->capabilities != old_capabilities) { - printk(KERN_WARNING - "ttyS%d: detected caps %08x should be %08x\n", + pr_warn("ttyS%d: detected caps %08x should be %08x\n", serial_index(port), old_capabilities, up->capabilities); } @@ -1286,7 +1381,69 @@ static void autoconfig_irq(struct uart_8250_port *up) port->irq = (irq > 0) ? irq : 0; } -static inline void __stop_tx(struct uart_8250_port *p) +static void serial8250_stop_rx(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + serial8250_rpm_get(up); + + up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); + up->port.read_status_mask &= ~UART_LSR_DR; + serial_port_out(port, UART_IER, up->ier); + + serial8250_rpm_put(up); +} + +static void __do_stop_tx_rs485(struct uart_8250_port *p) +{ + if (!p->em485) + return; + + serial8250_em485_rts_after_send(p); + /* + * Empty the RX FIFO, we are not interested in anything + * received during the half-duplex transmission. + */ + if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) + serial8250_clear_fifos(p); +} + +static void serial8250_em485_handle_stop_tx(unsigned long arg) +{ + struct uart_8250_port *p = (struct uart_8250_port *)arg; + struct uart_8250_em485 *em485 = p->em485; + unsigned long flags; + + spin_lock_irqsave(&p->port.lock, flags); + if (em485 && + em485->active_timer == &em485->stop_tx_timer) { + __do_stop_tx_rs485(p); + em485->active_timer = NULL; + } + spin_unlock_irqrestore(&p->port.lock, flags); +} + +static void __stop_tx_rs485(struct uart_8250_port *p) +{ + struct uart_8250_em485 *em485 = p->em485; + + if (!em485) + return; + + /* + * __do_stop_tx_rs485 is going to set RTS according to config + * AND flush RX FIFO if required. + */ + if (p->port.rs485.delay_rts_after_send > 0) { + em485->active_timer = &em485->stop_tx_timer; + mod_timer(&em485->stop_tx_timer, jiffies + + p->port.rs485.delay_rts_after_send * HZ / 1000); + } else { + __do_stop_tx_rs485(p); + } +} + +static inline void __do_stop_tx(struct uart_8250_port *p) { if (p->ier & UART_IER_THRI) { p->ier &= ~UART_IER_THRI; @@ -1295,6 +1452,28 @@ static inline void __stop_tx(struct uart_8250_port *p) } } +static inline void __stop_tx(struct uart_8250_port *p) +{ + struct uart_8250_em485 *em485 = p->em485; + + if (em485) { + unsigned char lsr = serial_in(p, UART_LSR); + /* + * To provide required timeing and allow FIFO transfer, + * __stop_tx_rs485 must be called only when both FIFO and + * shift register are empty. It is for device driver to enable + * interrupt on TEMT. + */ + if ((lsr & BOTH_EMPTY) != BOTH_EMPTY) + return; + + del_timer(&em485->start_tx_timer); + em485->active_timer = NULL; + } + __do_stop_tx(p); + __stop_tx_rs485(p); +} + static void serial8250_stop_tx(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); @@ -1312,12 +1491,10 @@ static void serial8250_stop_tx(struct uart_port *port) serial8250_rpm_put(up); } -static void serial8250_start_tx(struct uart_port *port) +static inline void __start_tx(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); - serial8250_rpm_get_tx(up); - if (up->dma && !up->dma->tx_dma(up)) return; @@ -1327,6 +1504,7 @@ static void serial8250_start_tx(struct uart_port *port) if (up->bugs & UART_BUG_TXEN) { unsigned char lsr; + lsr = serial_in(up, UART_LSR); up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; if (lsr & UART_LSR_THRE) @@ -1343,33 +1521,83 @@ static void serial8250_start_tx(struct uart_port *port) } } -static void serial8250_throttle(struct uart_port *port) +static inline void start_tx_rs485(struct uart_port *port) { - port->throttle(port); + struct uart_8250_port *up = up_to_u8250p(port); + struct uart_8250_em485 *em485 = up->em485; + unsigned char mcr; + + if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) + serial8250_stop_rx(&up->port); + + del_timer(&em485->stop_tx_timer); + em485->active_timer = NULL; + + mcr = serial_in(up, UART_MCR); + if (!!(up->port.rs485.flags & SER_RS485_RTS_ON_SEND) != + !!(mcr & UART_MCR_RTS)) { + if (up->port.rs485.flags & SER_RS485_RTS_ON_SEND) + mcr |= UART_MCR_RTS; + else + mcr &= ~UART_MCR_RTS; + serial_out(up, UART_MCR, mcr); + + if (up->port.rs485.delay_rts_before_send > 0) { + em485->active_timer = &em485->start_tx_timer; + mod_timer(&em485->start_tx_timer, jiffies + + up->port.rs485.delay_rts_before_send * HZ / 1000); + return; + } + } + + __start_tx(port); } -static void serial8250_unthrottle(struct uart_port *port) +static void serial8250_em485_handle_start_tx(unsigned long arg) { - port->unthrottle(port); + struct uart_8250_port *p = (struct uart_8250_port *)arg; + struct uart_8250_em485 *em485 = p->em485; + unsigned long flags; + + spin_lock_irqsave(&p->port.lock, flags); + if (em485 && + em485->active_timer == &em485->start_tx_timer) { + __start_tx(&p->port); + em485->active_timer = NULL; + } + spin_unlock_irqrestore(&p->port.lock, flags); } -static void serial8250_stop_rx(struct uart_port *port) +static void serial8250_start_tx(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); + struct uart_8250_em485 *em485 = up->em485; - serial8250_rpm_get(up); + serial8250_rpm_get_tx(up); - up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); - up->port.read_status_mask &= ~UART_LSR_DR; - serial_port_out(port, UART_IER, up->ier); + if (em485 && + em485->active_timer == &em485->start_tx_timer) + return; - serial8250_rpm_put(up); + if (em485) + start_tx_rs485(port); + else + __start_tx(port); +} + +static void serial8250_throttle(struct uart_port *port) +{ + port->throttle(port); +} + +static void serial8250_unthrottle(struct uart_port *port) +{ + port->unthrottle(port); } static void serial8250_disable_ms(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); /* no MSR capabilities */ if (up->bugs & UART_BUG_NOMSR) @@ -1394,81 +1622,85 @@ static void serial8250_enable_ms(struct uart_port *port) serial8250_rpm_put(up); } +static void serial8250_read_char(struct uart_8250_port *up, unsigned char lsr) +{ + struct uart_port *port = &up->port; + unsigned char ch; + char flag = TTY_NORMAL; + + if (likely(lsr & UART_LSR_DR)) + ch = serial_in(up, UART_RX); + else + /* + * Intel 82571 has a Serial Over Lan device that will + * set UART_LSR_BI without setting UART_LSR_DR when + * it receives a break. To avoid reading from the + * receive buffer without UART_LSR_DR bit set, we + * just force the read character to be 0 + */ + ch = 0; + + port->icount.rx++; + + lsr |= up->lsr_saved_flags; + up->lsr_saved_flags = 0; + + if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { + if (lsr & UART_LSR_BI) { + lsr &= ~(UART_LSR_FE | UART_LSR_PE); + port->icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(port)) + return; + } else if (lsr & UART_LSR_PE) + port->icount.parity++; + else if (lsr & UART_LSR_FE) + port->icount.frame++; + if (lsr & UART_LSR_OE) + port->icount.overrun++; + + /* + * Mask off conditions which should be ignored. + */ + lsr &= port->read_status_mask; + + if (lsr & UART_LSR_BI) { + DEBUG_INTR("handling break...."); + flag = TTY_BREAK; + } else if (lsr & UART_LSR_PE) + flag = TTY_PARITY; + else if (lsr & UART_LSR_FE) + flag = TTY_FRAME; + } + if (uart_handle_sysrq_char(port, ch)) + return; + + uart_insert_char(port, lsr, UART_LSR_OE, ch, flag); +} + /* * serial8250_rx_chars: processes according to the passed in LSR * value, and returns the remaining LSR bits not handled * by this Rx routine. */ -unsigned char -serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr) +unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr) { struct uart_port *port = &up->port; - unsigned char ch; int max_count = 256; - char flag; do { - if (likely(lsr & UART_LSR_DR)) - ch = serial_in(up, UART_RX); - else - /* - * Intel 82571 has a Serial Over Lan device that will - * set UART_LSR_BI without setting UART_LSR_DR when - * it receives a break. To avoid reading from the - * receive buffer without UART_LSR_DR bit set, we - * just force the read character to be 0 - */ - ch = 0; - - flag = TTY_NORMAL; - port->icount.rx++; - - lsr |= up->lsr_saved_flags; - up->lsr_saved_flags = 0; - - if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { - if (lsr & UART_LSR_BI) { - lsr &= ~(UART_LSR_FE | UART_LSR_PE); - port->icount.brk++; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(port)) - goto ignore_char; - } else if (lsr & UART_LSR_PE) - port->icount.parity++; - else if (lsr & UART_LSR_FE) - port->icount.frame++; - if (lsr & UART_LSR_OE) - port->icount.overrun++; - - /* - * Mask off conditions which should be ignored. - */ - lsr &= port->read_status_mask; - - if (lsr & UART_LSR_BI) { - DEBUG_INTR("handling break...."); - flag = TTY_BREAK; - } else if (lsr & UART_LSR_PE) - flag = TTY_PARITY; - else if (lsr & UART_LSR_FE) - flag = TTY_FRAME; - } - if (uart_handle_sysrq_char(port, ch)) - goto ignore_char; - - uart_insert_char(port, lsr, UART_LSR_OE, ch, flag); - -ignore_char: + serial8250_read_char(up, lsr); + if (--max_count == 0) + break; lsr = serial_in(up, UART_LSR); - } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (--max_count > 0)); - spin_unlock(&port->lock); + } while (lsr & (UART_LSR_DR | UART_LSR_BI)); + tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); return lsr; } EXPORT_SYMBOL_GPL(serial8250_rx_chars); @@ -1501,11 +1733,9 @@ void serial8250_tx_chars(struct uart_8250_port *up) port->icount.tx++; if (uart_circ_empty(xmit)) break; - if (up->capabilities & UART_CAP_HFIFO) { - if ((serial_port_in(port, UART_LSR) & BOTH_EMPTY) != - BOTH_EMPTY) - break; - } + if ((up->capabilities & UART_CAP_HFIFO) && + (serial_in(up, UART_LSR) & BOTH_EMPTY) != BOTH_EMPTY) + break; } while (--count > 0); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -1734,6 +1964,7 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits) /* Wait up to 1s for flow control if necessary */ if (up->port.flags & UPF_CONS_FLOW) { unsigned int tmout; + for (tmout = 1000000; tmout; tmout--) { unsigned int msr = serial_in(up, UART_MSR); up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; @@ -1967,23 +2198,23 @@ int serial8250_do_startup(struct uart_port *port) serial8250_set_mctrl(port, port->mctrl); - /* Serial over Lan (SoL) hack: - Intel 8257x Gigabit ethernet chips have a - 16550 emulation, to be used for Serial Over Lan. - Those chips take a longer time than a normal - serial device to signalize that a transmission - data was queued. Due to that, the above test generally - fails. One solution would be to delay the reading of - iir. However, this is not reliable, since the timeout - is variable. So, let's just don't test if we receive - TX irq. This way, we'll never enable UART_BUG_TXEN. + /* + * Serial over Lan (SoL) hack: + * Intel 8257x Gigabit ethernet chips have a 16550 emulation, to be + * used for Serial Over Lan. Those chips take a longer time than a + * normal serial device to signalize that a transmission data was + * queued. Due to that, the above test generally fails. One solution + * would be to delay the reading of iir. However, this is not + * reliable, since the timeout is variable. So, let's just don't + * test if we receive TX irq. This way, we'll never enable + * UART_BUG_TXEN. */ if (up->port.flags & UPF_NO_TXEN_TEST) goto dont_test_tx_en; /* - * Do a quick test to see if we receive an - * interrupt when we enable the TX irq. + * Do a quick test to see if we receive an interrupt when we enable + * the TX irq. */ serial_port_out(port, UART_IER, UART_IER_THRI); lsr = serial_port_in(port, UART_LSR); @@ -2066,8 +2297,12 @@ void serial8250_do_shutdown(struct uart_port *port) /* * Disable interrupts from this port */ + spin_lock_irqsave(&port->lock, flags); up->ier = 0; serial_port_out(port, UART_IER, 0); + spin_unlock_irqrestore(&port->lock, flags); + + synchronize_irq(port->irq); if (up->dma) serial8250_release_dma(up); @@ -2233,9 +2468,9 @@ static void serial8250_set_divisor(struct uart_port *port, unsigned int baud, serial_port_out(port, 0x2, quot_frac); } -static unsigned int -serial8250_get_baud_rate(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) +static unsigned int serial8250_get_baud_rate(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) { unsigned int tolerance = port->uartclk / 100; @@ -2252,7 +2487,7 @@ serial8250_get_baud_rate(struct uart_port *port, struct ktermios *termios, void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) + struct ktermios *old) { struct uart_8250_port *up = up_to_u8250p(port); unsigned char cval; @@ -2462,6 +2697,7 @@ static int serial8250_request_std_resource(struct uart_8250_port *up) case UPIO_TSI: case UPIO_MEM32: case UPIO_MEM32BE: + case UPIO_MEM16: case UPIO_MEM: if (!port->mapbase) break; @@ -2499,6 +2735,7 @@ static void serial8250_release_std_resource(struct uart_8250_port *up) case UPIO_TSI: case UPIO_MEM32: case UPIO_MEM32BE: + case UPIO_MEM16: case UPIO_MEM: if (!port->mapbase) break; @@ -2563,8 +2800,7 @@ static int do_get_rxtrig(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = state->uart_port; - struct uart_8250_port *up = - container_of(uport, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(uport); if (!(up->capabilities & UART_CAP_FIFO) || uport->fifosize <= 1) return -EINVAL; @@ -2600,8 +2836,7 @@ static int do_set_rxtrig(struct tty_port *port, unsigned char bytes) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = state->uart_port; - struct uart_8250_port *up = - container_of(uport, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(uport); int rxtrig; if (!(up->capabilities & UART_CAP_FIFO) || uport->fifosize <= 1 || @@ -2725,8 +2960,7 @@ serial8250_verify_port(struct uart_port *port, struct serial_struct *ser) return 0; } -static const char * -serial8250_type(struct uart_port *port) +static const char *serial8250_type(struct uart_port *port) { int type = port->type; diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 245edbb68d4b..1b7bd26555b7 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -13,6 +13,7 @@ */ #include <linux/clk.h> +#include <linux/console.h> #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> @@ -34,6 +35,29 @@ struct uniphier8250_priv { spinlock_t atomic_write_lock; }; +#if defined(CONFIG_SERIAL_8250_CONSOLE) && !defined(MODULE) +static int __init uniphier_early_console_setup(struct earlycon_device *device, + const char *options) +{ + if (!device->port.membase) + return -ENODEV; + + /* This hardware always expects MMIO32 register interface. */ + device->port.iotype = UPIO_MEM32; + device->port.regshift = 2; + + /* + * Do not touch the divisor register in early_serial8250_setup(); + * we assume it has been initialized by a boot loader. + */ + device->baud = 0; + + return early_serial8250_setup(device, options); +} +OF_EARLYCON_DECLARE(uniphier, "socionext,uniphier-uart", + uniphier_early_console_setup); +#endif + /* * The register map is slightly different from that of 8250. * IO callbacks must be overridden for correct access to FCR, LCR, and MCR. diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 6412f1455beb..64742a086ae3 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -262,7 +262,12 @@ config SERIAL_8250_RSA bool "Support RSA serial ports" depends on SERIAL_8250_EXTENDED help - ::: To be written ::: + Say Y here if you have a IODATA RSA-DV II/S ISA card and + would like to use its >115kbps speeds. + You will need to provide module parameter "probe_rsa", or boot-time + parameter 8250.probe_rsa with I/O addresses of this card then. + + If you don't have such card, or if unsure, say N. config SERIAL_8250_ACORN tristate "Acorn expansion card serial port support" @@ -272,6 +277,30 @@ config SERIAL_8250_ACORN system, say Y to this option. The driver can handle 1, 2, or 3 port cards. If unsure, say N. +config SERIAL_8250_BCM2835AUX + tristate "BCM2835 auxiliar mini UART support" + depends on ARCH_BCM2835 || COMPILE_TEST + depends on SERIAL_8250 && SERIAL_8250_SHARE_IRQ + help + Support for the BCM2835 auxiliar mini UART. + + Features and limitations of the UART are + Registers are similar to 16650 registers, + set bits in the control registers that are unsupported + are ignored and read back as 0 + 7/8 bit operation with 1 start and 1 stop bit + 8 symbols deep fifo for rx and tx + SW controlled RTS and SW readable CTS + Clock rate derived from system clock + Uses 8 times oversampling (compared to 16 times for 16650) + Missing break detection (but break generation) + Missing framing error detection + Missing parity bit + Missing receive time-out interrupt + Missing DCD, DSR, DTR and RI signals + + If unsure, say N. + config SERIAL_8250_FSL bool depends on SERIAL_8250_CONSOLE @@ -295,6 +324,7 @@ config SERIAL_8250_EM config SERIAL_8250_RT288X bool "Ralink RT288x/RT305x/RT3662/RT3883 serial port support" depends on SERIAL_8250 + depends on MIPS || COMPILE_TEST default y if MIPS_ALCHEMY || SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620 help Selecting this option will add support for the alternate register @@ -346,7 +376,7 @@ config SERIAL_8250_LPC18XX serial port, say Y to this option. If unsure, say Y. config SERIAL_8250_MT6577 - bool "Mediatek serial port support" + tristate "Mediatek serial port support" depends on SERIAL_8250 && ARCH_MEDIATEK help If you have a Mediatek based board and want to use the @@ -360,10 +390,10 @@ config SERIAL_8250_UNIPHIER serial ports, say Y to this option. If unsure, say N. config SERIAL_8250_INGENIC - bool "Support for Ingenic SoC serial ports" - depends on SERIAL_8250_CONSOLE && OF_FLATTREE - select LIBFDT - select SERIAL_EARLYCON + tristate "Support for Ingenic SoC serial ports" + depends on SERIAL_8250 + depends on (OF_FLATTREE && SERIAL_8250_CONSOLE) || !SERIAL_EARLYCON + depends on MIPS || COMPILE_TEST help If you have a system using an Ingenic SoC and wish to make use of its UARTs, say Y to this option. If unsure, say N. @@ -372,9 +402,28 @@ config SERIAL_8250_MID tristate "Support for serial ports on Intel MID platforms" depends on SERIAL_8250 && PCI select HSU_DMA if SERIAL_8250_DMA - select HSU_DMA_PCI if X86_INTEL_MID + select HSU_DMA_PCI if (HSU_DMA && X86_INTEL_MID) select RATIONAL help Selecting this option will enable handling of the extra features present on the UART found on Intel Medfield SOC and various other Intel platforms. + +config SERIAL_8250_MOXA + tristate "MOXA SmartIO MUE support" + depends on SERIAL_8250 && PCI + help + Say Y here if you have a Moxa SmartIO MUE multiport serial card. + If unsure, say N. + + This driver can also be built as a module. The module will be called + 8250_moxa. If you want to do that, say M here. + +config SERIAL_OF_PLATFORM + tristate "Devicetree based probing for 8250 ports" + depends on SERIAL_8250 && OF + help + This option is used for all 8250 compatible serial ports that + are probed through devicetree, including Open Firmware based + PowerPC systems and embedded systems on architectures using the + flattened device tree format. diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index e177f8681ada..c9a2d6ed87e9 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o +obj-$(CONFIG_SERIAL_8250_BCM2835AUX) += 8250_bcm2835aux.o obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o @@ -28,5 +29,7 @@ obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o +obj-$(CONFIG_SERIAL_8250_MOXA) += 8250_moxa.o +obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c index 4d180c9423ef..933c2688dd7e 100644 --- a/drivers/tty/serial/8250/serial_cs.c +++ b/drivers/tty/serial/8250/serial_cs.c @@ -28,7 +28,7 @@ and other provisions required by the GPL. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the GPL. - + ======================================================================*/ #include <linux/module.h> @@ -257,7 +257,7 @@ static const struct serial_quirk quirks[] = { }; -static int serial_config(struct pcmcia_device * link); +static int serial_config(struct pcmcia_device *link); static void serial_remove(struct pcmcia_device *link) @@ -309,7 +309,7 @@ static int serial_probe(struct pcmcia_device *link) dev_dbg(&link->dev, "serial_attach()\n"); /* Create new serial device */ - info = kzalloc(sizeof (*info), GFP_KERNEL); + info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; info->p_dev = link; @@ -339,7 +339,7 @@ static void serial_detach(struct pcmcia_device *link) /*====================================================================*/ -static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, +static int setup_serial(struct pcmcia_device *handle, struct serial_info *info, unsigned int iobase, int irq) { struct uart_8250_port uart; @@ -441,16 +441,20 @@ static int simple_config(struct pcmcia_device *link) struct serial_info *info = link->priv; int i = -ENODEV, try; - /* First pass: look for a config entry that looks normal. - * Two tries: without IO aliases, then with aliases */ + /* + * First pass: look for a config entry that looks normal. + * Two tries: without IO aliases, then with aliases. + */ link->config_flags |= CONF_AUTO_SET_VPP; for (try = 0; try < 4; try++) if (!pcmcia_loop_config(link, simple_config_check, &try)) goto found_port; - /* Second pass: try to find an entry that isn't picky about - its base address, then try to grab any standard serial port - address, and finally try to get any free port. */ + /* + * Second pass: try to find an entry that isn't picky about + * its base address, then try to grab any standard serial port + * address, and finally try to get any free port. + */ if (!pcmcia_loop_config(link, simple_config_check_notpicky, NULL)) goto found_port; @@ -480,8 +484,10 @@ static int multi_config_check(struct pcmcia_device *p_dev, void *priv_data) if (p_dev->resource[1]->end) return -EINVAL; - /* The quad port cards have bad CIS's, so just look for a - window larger than 8 ports and assume it will be right */ + /* + * The quad port cards have bad CIS's, so just look for a + * window larger than 8 ports and assume it will be right. + */ if (p_dev->resource[0]->end <= 8) return -EINVAL; @@ -527,8 +533,8 @@ static int multi_config(struct pcmcia_device *link) info->multi = 2; if (pcmcia_loop_config(link, multi_config_check_notpicky, &base2)) { - dev_warn(&link->dev, "no usable port range " - "found, giving up\n"); + dev_warn(&link->dev, + "no usable port range found, giving up\n"); return -ENODEV; } } @@ -600,7 +606,7 @@ static int serial_check_for_multi(struct pcmcia_device *p_dev, void *priv_data) } -static int serial_config(struct pcmcia_device * link) +static int serial_config(struct pcmcia_device *link) { struct serial_info *info = link->priv; int i; @@ -623,8 +629,10 @@ static int serial_config(struct pcmcia_device * link) break; } - /* Another check for dual-serial cards: look for either serial or - multifunction cards that ask for appropriate IO port ranges */ + /* + * Another check for dual-serial cards: look for either serial or + * multifunction cards that ask for appropriate IO port ranges. + */ if ((info->multi == 0) && (link->has_func_id) && (link->socket->pcmcia_pfc == 0) && @@ -701,7 +709,7 @@ static const struct pcmcia_device_id serial_ids[] = { PCMCIA_PFC_DEVICE_PROD_ID12(1, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58), PCMCIA_PFC_DEVICE_PROD_ID12(1, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e), PCMCIA_PFC_DEVICE_PROD_ID12(1, "MICRO RESEARCH", "COMBO-L/M-336", 0xb2ced065, 0x3ced0555), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "NEC", "PK-UG-J001" ,0x18df0ba0 ,0x831b1064), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "NEC", "PK-UG-J001", 0x18df0ba0, 0x831b1064), PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9), PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed), PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc), @@ -797,30 +805,30 @@ static const struct pcmcia_device_id serial_ids[] = { PCMCIA_DEVICE_CIS_PROD_ID123("ADVANTECH", "COMpad-32/85", "1.0", 0x96913a85, 0x8fbe92ae, 0x0877b627, "cis/COMpad2.cis"), PCMCIA_DEVICE_CIS_PROD_ID2("RS-COM 2P", 0xad20b156, "cis/RS-COM-2P.cis"), PCMCIA_DEVICE_CIS_MANF_CARD(0x0013, 0x0000, "cis/GLOBETROTTER.cis"), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100 1.00.",0x19ca78af,0xf964f42b), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100",0x19ca78af,0x71d98e83), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232 1.00.",0x19ca78af,0x69fb7490), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232",0x19ca78af,0xb6bc0235), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.","SERIAL CARD: CF232",0x63f2e0bd,0xb9e175d3), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.","SERIAL CARD: CF232-5",0x63f2e0bd,0xfce33442), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF232",0x3beb8cf2,0x171e7190), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF232-5",0x3beb8cf2,0x20da4262), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF428",0x3beb8cf2,0xea5dd57d), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF500",0x3beb8cf2,0xd77255fa), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: IC232",0x3beb8cf2,0x6a709903), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: SL232",0x3beb8cf2,0x18430676), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: XL232",0x3beb8cf2,0x6f933767), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: CF332",0x3beb8cf2,0x16dc1ba7), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL332",0x3beb8cf2,0x19816c41), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL385",0x3beb8cf2,0x64112029), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial+Parallel Port: SP230",0x3beb8cf2,0xdb9e58bc), - PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: CF332",0x3beb8cf2,0x16dc1ba7), - PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL332",0x3beb8cf2,0x19816c41), - PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL385",0x3beb8cf2,0x64112029), - PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), - PCMCIA_MFC_DEVICE_PROD_ID12(2,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), - PCMCIA_MFC_DEVICE_PROD_ID12(3,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.", "SERIAL CARD: SL100 1.00.", 0x19ca78af, 0xf964f42b), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.", "SERIAL CARD: SL100", 0x19ca78af, 0x71d98e83), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.", "SERIAL CARD: SL232 1.00.", 0x19ca78af, 0x69fb7490), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.", "SERIAL CARD: SL232", 0x19ca78af, 0xb6bc0235), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.", "SERIAL CARD: CF232", 0x63f2e0bd, 0xb9e175d3), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.", "SERIAL CARD: CF232-5", 0x63f2e0bd, 0xfce33442), + PCMCIA_DEVICE_PROD_ID12("Elan", "Serial Port: CF232", 0x3beb8cf2, 0x171e7190), + PCMCIA_DEVICE_PROD_ID12("Elan", "Serial Port: CF232-5", 0x3beb8cf2, 0x20da4262), + PCMCIA_DEVICE_PROD_ID12("Elan", "Serial Port: CF428", 0x3beb8cf2, 0xea5dd57d), + PCMCIA_DEVICE_PROD_ID12("Elan", "Serial Port: CF500", 0x3beb8cf2, 0xd77255fa), + PCMCIA_DEVICE_PROD_ID12("Elan", "Serial Port: IC232", 0x3beb8cf2, 0x6a709903), + PCMCIA_DEVICE_PROD_ID12("Elan", "Serial Port: SL232", 0x3beb8cf2, 0x18430676), + PCMCIA_DEVICE_PROD_ID12("Elan", "Serial Port: XL232", 0x3beb8cf2, 0x6f933767), + PCMCIA_MFC_DEVICE_PROD_ID12(0, "Elan", "Serial Port: CF332", 0x3beb8cf2, 0x16dc1ba7), + PCMCIA_MFC_DEVICE_PROD_ID12(0, "Elan", "Serial Port: SL332", 0x3beb8cf2, 0x19816c41), + PCMCIA_MFC_DEVICE_PROD_ID12(0, "Elan", "Serial Port: SL385", 0x3beb8cf2, 0x64112029), + PCMCIA_MFC_DEVICE_PROD_ID12(0, "Elan", "Serial Port: SL432", 0x3beb8cf2, 0x1cce7ac4), + PCMCIA_MFC_DEVICE_PROD_ID12(0, "Elan", "Serial+Parallel Port: SP230", 0x3beb8cf2, 0xdb9e58bc), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "Elan", "Serial Port: CF332", 0x3beb8cf2, 0x16dc1ba7), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "Elan", "Serial Port: SL332", 0x3beb8cf2, 0x19816c41), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "Elan", "Serial Port: SL385", 0x3beb8cf2, 0x64112029), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "Elan", "Serial Port: SL432", 0x3beb8cf2, 0x1cce7ac4), + PCMCIA_MFC_DEVICE_PROD_ID12(2, "Elan", "Serial Port: SL432", 0x3beb8cf2, 0x1cce7ac4), + PCMCIA_MFC_DEVICE_PROD_ID12(3, "Elan", "Serial Port: SL432", 0x3beb8cf2, 0x1cce7ac4), PCMCIA_DEVICE_MANF_CARD(0x0279, 0x950b), /* too generic */ /* PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0160, 0x0002), */ diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index f38beb28e7ae..13d4ed6caac4 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -115,6 +115,7 @@ config SERIAL_SB1250_DUART_CONSOLE config SERIAL_ATMEL bool "AT91 / AT32 on-chip serial port support" + depends on HAS_DMA depends on ARCH_AT91 || AVR32 || COMPILE_TEST select SERIAL_CORE select SERIAL_MCTRL_GPIO if GPIOLIB @@ -571,9 +572,11 @@ config BFIN_UART3_CTSRTS config SERIAL_IMX tristate "IMX serial port support" + depends on HAS_DMA depends on ARCH_MXC || COMPILE_TEST select SERIAL_CORE select RATIONAL + select SERIAL_MCTRL_GPIO if GPIOLIB help If you have a machine based on a Motorola IMX CPU you can enable its onboard serial port by enabling this option. @@ -607,6 +610,7 @@ config SERIAL_UARTLITE_CONSOLE bool "Support for console on Xilinx uartlite serial port" depends on SERIAL_UARTLITE=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON help Say Y here if you wish to use a Xilinx uartlite as the system console (the system console is the device which receives all kernel @@ -729,7 +733,7 @@ config SERIAL_IP22_ZILOG_CONSOLE config SERIAL_SH_SCI tristate "SuperH SCI(F) serial port support" - depends on SUPERH || ARCH_SHMOBILE || H8300 || COMPILE_TEST + depends on SUPERH || ARCH_RENESAS || H8300 || COMPILE_TEST select SERIAL_CORE config SERIAL_SH_SCI_NR_UARTS @@ -742,6 +746,12 @@ config SERIAL_SH_SCI_CONSOLE depends on SERIAL_SH_SCI=y select SERIAL_CORE_CONSOLE +config SERIAL_SH_SCI_EARLYCON + bool "Support for early console on SuperH SCI(F)" + depends on SERIAL_SH_SCI=y + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + config SERIAL_SH_SCI_DMA bool "DMA support" depends on SERIAL_SH_SCI && DMA_ENGINE @@ -790,17 +800,6 @@ config SERIAL_CORE_CONSOLE config CONSOLE_POLL bool -config SERIAL_68328 - bool "68328 serial support" - depends on M68328 || M68EZ328 || M68VZ328 - help - This driver supports the built-in serial port of the Motorola 68328 - (standard, EZ and VZ varieties). - -config SERIAL_68328_RTS_CTS - bool "Support RTS/CTS on 68328 serial port" - depends on SERIAL_68328 - config SERIAL_MCF bool "Coldfire serial support" depends on COLDFIRE @@ -1044,7 +1043,7 @@ config SERIAL_SGI_IOC3 say Y or M. Otherwise, say N. config SERIAL_MSM - bool "MSM on-chip serial port support" + tristate "MSM on-chip serial port support" depends on ARCH_QCOM select SERIAL_CORE @@ -1094,16 +1093,6 @@ config SERIAL_NETX_CONSOLE If you have enabled the serial port on the Hilscher NetX SoC you can make it the console by answering Y to this option. -config SERIAL_OF_PLATFORM - tristate "Serial port on Open Firmware platform bus" - depends on OF - depends on SERIAL_8250 || SERIAL_OF_PLATFORM_NWPSERIAL - help - If you have a PowerPC based system that has serial ports - on a platform specific bus, you should enable this option. - Currently, only 8250 compatible ports are supported, but - others can easily be added. - config SERIAL_OMAP tristate "OMAP serial port support" depends on ARCH_OMAP2PLUS @@ -1131,23 +1120,6 @@ config SERIAL_OMAP_CONSOLE your boot loader about how to pass options to the kernel at boot time.) -config SERIAL_OF_PLATFORM_NWPSERIAL - tristate "NWP serial port driver" - depends on PPC_DCR - select SERIAL_OF_PLATFORM - select SERIAL_CORE_CONSOLE - select SERIAL_CORE - help - This driver supports the cell network processor nwp serial - device. - -config SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE - bool "Console on NWP serial port" - depends on SERIAL_OF_PLATFORM_NWPSERIAL=y - select SERIAL_CORE_CONSOLE - help - Support for Console on the NWP serial ports. - config SERIAL_LANTIQ bool "Lantiq serial driver" depends on LANTIQ @@ -1409,8 +1381,9 @@ config SERIAL_PCH_UART_CONSOLE warnings and which allows logins in single user mode). config SERIAL_MXS_AUART - depends on ARCH_MXS || COMPILE_TEST tristate "MXS AUART support" + depends on HAS_DMA + depends on ARCH_MXS || COMPILE_TEST select SERIAL_CORE select SERIAL_MCTRL_GPIO if GPIOLIB help @@ -1629,6 +1602,28 @@ config SERIAL_STM32_CONSOLE depends on SERIAL_STM32=y select SERIAL_CORE_CONSOLE +config SERIAL_MVEBU_UART + bool "Marvell EBU serial port support" + select SERIAL_CORE + help + This driver is for Marvell EBU SoC's UART. If you have a machine + based on the Armada-3700 SoC and wish to use the on-board serial + port, + say 'Y' here. + Otherwise, say 'N'. + +config SERIAL_MVEBU_CONSOLE + bool "Console on Marvell EBU serial port" + depends on SERIAL_MVEBU_UART + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + default y + help + Say 'Y' here if you wish to use Armada-3700 UART as the system console. + (the system console is the device which receives all kernel messages + and warnings and which allows logins in single user mode) + Otherwise, say 'N'. + endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 5ab41119b3dc..8c261adac04e 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -34,7 +34,6 @@ obj-$(CONFIG_SERIAL_MAX3100) += max3100.o obj-$(CONFIG_SERIAL_MAX310X) += max310x.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o obj-$(CONFIG_SERIAL_MUX) += mux.o -obj-$(CONFIG_SERIAL_68328) += 68328serial.o obj-$(CONFIG_SERIAL_MCF) += mcf.o obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o obj-$(CONFIG_SERIAL_HS_LPC32XX) += lpc32xx_hs.o @@ -63,8 +62,6 @@ obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_MSM) += msm_serial.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o -obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o -obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o @@ -93,6 +90,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR) += digicolor-usart.o 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 # 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 899a77187bde..7c198e0a3178 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -60,6 +60,8 @@ #include <linux/io.h> #include <linux/acpi.h> +#include "amba-pl011.h" + #define UART_NR 14 #define SERIAL_AMBA_MAJOR 204 @@ -71,11 +73,27 @@ #define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) #define UART_DUMMY_DR_RX (1 << 16) +static u16 pl011_std_offsets[REG_ARRAY_SIZE] = { + [REG_DR] = UART01x_DR, + [REG_FR] = UART01x_FR, + [REG_LCRH_RX] = UART011_LCRH, + [REG_LCRH_TX] = UART011_LCRH, + [REG_IBRD] = UART011_IBRD, + [REG_FBRD] = UART011_FBRD, + [REG_CR] = UART011_CR, + [REG_IFLS] = UART011_IFLS, + [REG_IMSC] = UART011_IMSC, + [REG_RIS] = UART011_RIS, + [REG_MIS] = UART011_MIS, + [REG_ICR] = UART011_ICR, + [REG_DMACR] = UART011_DMACR, +}; + /* There is by now at least one vendor with differing details, so handle it */ struct vendor_data { + const u16 *reg_offset; unsigned int ifls; - unsigned int lcrh_tx; - unsigned int lcrh_rx; + bool access_32b; bool oversampling; bool dma_threshold; bool cts_event_workaround; @@ -91,9 +109,8 @@ static unsigned int get_fifosize_arm(struct amba_device *dev) } static struct vendor_data vendor_arm = { + .reg_offset = pl011_std_offsets, .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, - .lcrh_tx = UART011_LCRH, - .lcrh_rx = UART011_LCRH, .oversampling = false, .dma_threshold = false, .cts_event_workaround = false, @@ -103,6 +120,7 @@ static struct vendor_data vendor_arm = { }; static struct vendor_data vendor_sbsa = { + .reg_offset = pl011_std_offsets, .oversampling = false, .dma_threshold = false, .cts_event_workaround = false, @@ -110,15 +128,41 @@ static struct vendor_data vendor_sbsa = { .fixed_options = true, }; +static u16 pl011_st_offsets[REG_ARRAY_SIZE] = { + [REG_DR] = UART01x_DR, + [REG_ST_DMAWM] = ST_UART011_DMAWM, + [REG_ST_TIMEOUT] = ST_UART011_TIMEOUT, + [REG_FR] = UART01x_FR, + [REG_LCRH_RX] = ST_UART011_LCRH_RX, + [REG_LCRH_TX] = ST_UART011_LCRH_TX, + [REG_IBRD] = UART011_IBRD, + [REG_FBRD] = UART011_FBRD, + [REG_CR] = UART011_CR, + [REG_IFLS] = UART011_IFLS, + [REG_IMSC] = UART011_IMSC, + [REG_RIS] = UART011_RIS, + [REG_MIS] = UART011_MIS, + [REG_ICR] = UART011_ICR, + [REG_DMACR] = UART011_DMACR, + [REG_ST_XFCR] = ST_UART011_XFCR, + [REG_ST_XON1] = ST_UART011_XON1, + [REG_ST_XON2] = ST_UART011_XON2, + [REG_ST_XOFF1] = ST_UART011_XOFF1, + [REG_ST_XOFF2] = ST_UART011_XOFF2, + [REG_ST_ITCR] = ST_UART011_ITCR, + [REG_ST_ITIP] = ST_UART011_ITIP, + [REG_ST_ABCR] = ST_UART011_ABCR, + [REG_ST_ABIMSC] = ST_UART011_ABIMSC, +}; + static unsigned int get_fifosize_st(struct amba_device *dev) { return 64; } static struct vendor_data vendor_st = { + .reg_offset = pl011_st_offsets, .ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF, - .lcrh_tx = ST_UART011_LCRH_TX, - .lcrh_rx = ST_UART011_LCRH_RX, .oversampling = true, .dma_threshold = true, .cts_event_workaround = true, @@ -127,6 +171,29 @@ static struct vendor_data vendor_st = { .get_fifosize = get_fifosize_st, }; +static const u16 pl011_zte_offsets[REG_ARRAY_SIZE] = { + [REG_DR] = ZX_UART011_DR, + [REG_FR] = ZX_UART011_FR, + [REG_LCRH_RX] = ZX_UART011_LCRH, + [REG_LCRH_TX] = ZX_UART011_LCRH, + [REG_IBRD] = ZX_UART011_IBRD, + [REG_FBRD] = ZX_UART011_FBRD, + [REG_CR] = ZX_UART011_CR, + [REG_IFLS] = ZX_UART011_IFLS, + [REG_IMSC] = ZX_UART011_IMSC, + [REG_RIS] = ZX_UART011_RIS, + [REG_MIS] = ZX_UART011_MIS, + [REG_ICR] = ZX_UART011_ICR, + [REG_DMACR] = ZX_UART011_DMACR, +}; + +static struct vendor_data vendor_zte __maybe_unused = { + .reg_offset = pl011_zte_offsets, + .access_32b = true, + .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, + .get_fifosize = get_fifosize_arm, +}; + /* Deals with DMA transactions */ struct pl011_sgbuf { @@ -162,14 +229,13 @@ struct pl011_dmatx_data { */ struct uart_amba_port { struct uart_port port; + const u16 *reg_offset; struct clk *clk; const struct vendor_data *vendor; unsigned int dmacr; /* dma control reg */ unsigned int im; /* interrupt mask */ unsigned int old_status; unsigned int fifosize; /* vendor-specific */ - unsigned int lcrh_tx; /* vendor-specific */ - unsigned int lcrh_rx; /* vendor-specific */ unsigned int old_cr; /* state during shutdown */ bool autorts; unsigned int fixed_baud; /* vendor-set fixed baud rate */ @@ -184,6 +250,32 @@ struct uart_amba_port { #endif }; +static unsigned int pl011_reg_to_offset(const struct uart_amba_port *uap, + unsigned int reg) +{ + return uap->reg_offset[reg]; +} + +static unsigned int pl011_read(const struct uart_amba_port *uap, + unsigned int reg) +{ + void __iomem *addr = uap->port.membase + pl011_reg_to_offset(uap, reg); + + return (uap->port.iotype == UPIO_MEM32) ? + readl_relaxed(addr) : readw_relaxed(addr); +} + +static void pl011_write(unsigned int val, const struct uart_amba_port *uap, + unsigned int reg) +{ + void __iomem *addr = uap->port.membase + pl011_reg_to_offset(uap, reg); + + if (uap->port.iotype == UPIO_MEM32) + writel_relaxed(val, addr); + else + writew_relaxed(val, addr); +} + /* * Reads up to 256 characters from the FIFO or until it's empty and * inserts them into the TTY layer. Returns the number of characters @@ -196,13 +288,12 @@ static int pl011_fifo_to_tty(struct uart_amba_port *uap) int fifotaken = 0; while (max_count--) { - status = readw(uap->port.membase + UART01x_FR); + status = pl011_read(uap, REG_FR); if (status & UART01x_FR_RXFE) break; /* Take chars from the FIFO and update status */ - ch = readw(uap->port.membase + UART01x_DR) | - UART_DUMMY_DR_RX; + ch = pl011_read(uap, REG_DR) | UART_DUMMY_DR_RX; flag = TTY_NORMAL; uap->port.icount.rx++; fifotaken++; @@ -284,7 +375,8 @@ static void pl011_dma_probe(struct uart_amba_port *uap) struct amba_pl011_data *plat = dev_get_platdata(uap->port.dev); struct device *dev = uap->port.dev; struct dma_slave_config tx_conf = { - .dst_addr = uap->port.mapbase + UART01x_DR, + .dst_addr = uap->port.mapbase + + pl011_reg_to_offset(uap, REG_DR), .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, .direction = DMA_MEM_TO_DEV, .dst_maxburst = uap->fifosize >> 1, @@ -328,7 +420,7 @@ static void pl011_dma_probe(struct uart_amba_port *uap) /* Optionally make use of an RX channel as well */ chan = dma_request_slave_channel(dev, "rx"); - if (!chan && plat->dma_rx_param) { + if (!chan && plat && plat->dma_rx_param) { chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param); if (!chan) { @@ -339,7 +431,8 @@ static void pl011_dma_probe(struct uart_amba_port *uap) if (chan) { struct dma_slave_config rx_conf = { - .src_addr = uap->port.mapbase + UART01x_DR, + .src_addr = uap->port.mapbase + + pl011_reg_to_offset(uap, REG_DR), .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, .direction = DMA_DEV_TO_MEM, .src_maxburst = uap->fifosize >> 2, @@ -438,7 +531,7 @@ static void pl011_dma_tx_callback(void *data) dmacr = uap->dmacr; uap->dmacr = dmacr & ~UART011_TXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); + pl011_write(uap->dmacr, uap, REG_DMACR); /* * If TX DMA was disabled, it means that we've stopped the DMA for @@ -552,7 +645,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) dma_dev->device_issue_pending(chan); uap->dmacr |= UART011_TXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); + pl011_write(uap->dmacr, uap, REG_DMACR); uap->dmatx.queued = true; /* @@ -588,9 +681,9 @@ static bool pl011_dma_tx_irq(struct uart_amba_port *uap) */ if (uap->dmatx.queued) { uap->dmacr |= UART011_TXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); + pl011_write(uap->dmacr, uap, REG_DMACR); uap->im &= ~UART011_TXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_write(uap->im, uap, REG_IMSC); return true; } @@ -600,7 +693,7 @@ static bool pl011_dma_tx_irq(struct uart_amba_port *uap) */ if (pl011_dma_tx_refill(uap) > 0) { uap->im &= ~UART011_TXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_write(uap->im, uap, REG_IMSC); return true; } return false; @@ -614,7 +707,7 @@ static inline void pl011_dma_tx_stop(struct uart_amba_port *uap) { if (uap->dmatx.queued) { uap->dmacr &= ~UART011_TXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); + pl011_write(uap->dmacr, uap, REG_DMACR); } } @@ -640,14 +733,12 @@ static inline bool pl011_dma_tx_start(struct uart_amba_port *uap) if (!uap->dmatx.queued) { if (pl011_dma_tx_refill(uap) > 0) { uap->im &= ~UART011_TXIM; - writew(uap->im, uap->port.membase + - UART011_IMSC); + pl011_write(uap->im, uap, REG_IMSC); } else ret = false; } else if (!(uap->dmacr & UART011_TXDMAE)) { uap->dmacr |= UART011_TXDMAE; - writew(uap->dmacr, - uap->port.membase + UART011_DMACR); + pl011_write(uap->dmacr, uap, REG_DMACR); } return ret; } @@ -658,9 +749,9 @@ static inline bool pl011_dma_tx_start(struct uart_amba_port *uap) */ dmacr = uap->dmacr; uap->dmacr &= ~UART011_TXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); + pl011_write(uap->dmacr, uap, REG_DMACR); - if (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) { + if (pl011_read(uap, REG_FR) & UART01x_FR_TXFF) { /* * No space in the FIFO, so enable the transmit interrupt * so we know when there is space. Note that once we've @@ -669,13 +760,13 @@ static inline bool pl011_dma_tx_start(struct uart_amba_port *uap) return false; } - writew(uap->port.x_char, uap->port.membase + UART01x_DR); + pl011_write(uap->port.x_char, uap, REG_DR); uap->port.icount.tx++; uap->port.x_char = 0; /* Success - restore the DMA state */ uap->dmacr = dmacr; - writew(dmacr, uap->port.membase + UART011_DMACR); + pl011_write(dmacr, uap, REG_DMACR); return true; } @@ -703,7 +794,7 @@ __acquires(&uap->port.lock) DMA_TO_DEVICE); uap->dmatx.queued = false; uap->dmacr &= ~UART011_TXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); + pl011_write(uap->dmacr, uap, REG_DMACR); } } @@ -743,11 +834,11 @@ static int pl011_dma_rx_trigger_dma(struct uart_amba_port *uap) dma_async_issue_pending(rxchan); uap->dmacr |= UART011_RXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); + pl011_write(uap->dmacr, uap, REG_DMACR); uap->dmarx.running = true; uap->im &= ~UART011_RXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_write(uap->im, uap, REG_IMSC); return 0; } @@ -805,8 +896,8 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap, */ if (dma_count == pending && readfifo) { /* Clear any error flags */ - writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS, - uap->port.membase + UART011_ICR); + pl011_write(UART011_OEIS | UART011_BEIS | UART011_PEIS | + UART011_FEIS, uap, REG_ICR); /* * If we read all the DMA'd characters, and we had an @@ -854,7 +945,7 @@ static void pl011_dma_rx_irq(struct uart_amba_port *uap) /* Disable RX DMA - incoming data will wait in the FIFO */ uap->dmacr &= ~UART011_RXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); + pl011_write(uap->dmacr, uap, REG_DMACR); uap->dmarx.running = false; pending = sgbuf->sg.length - state.residue; @@ -874,7 +965,7 @@ static void pl011_dma_rx_irq(struct uart_amba_port *uap) dev_dbg(uap->port.dev, "could not retrigger RX DMA job " "fall back to interrupt mode\n"); uap->im |= UART011_RXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_write(uap->im, uap, REG_IMSC); } } @@ -922,7 +1013,7 @@ static void pl011_dma_rx_callback(void *data) dev_dbg(uap->port.dev, "could not retrigger RX DMA job " "fall back to interrupt mode\n"); uap->im |= UART011_RXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_write(uap->im, uap, REG_IMSC); } } @@ -935,7 +1026,7 @@ static inline void pl011_dma_rx_stop(struct uart_amba_port *uap) { /* FIXME. Just disable the DMA enable */ uap->dmacr &= ~UART011_RXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); + pl011_write(uap->dmacr, uap, REG_DMACR); } /* @@ -979,7 +1070,7 @@ static void pl011_dma_rx_poll(unsigned long args) spin_lock_irqsave(&uap->port.lock, flags); pl011_dma_rx_stop(uap); uap->im |= UART011_RXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_write(uap->im, uap, REG_IMSC); spin_unlock_irqrestore(&uap->port.lock, flags); uap->dmarx.running = false; @@ -1041,7 +1132,7 @@ static void pl011_dma_startup(struct uart_amba_port *uap) skip_rx: /* Turn on DMA error (RX/TX will be enabled on demand) */ uap->dmacr |= UART011_DMAONERR; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); + pl011_write(uap->dmacr, uap, REG_DMACR); /* * ST Micro variants has some specific dma burst threshold @@ -1049,8 +1140,8 @@ skip_rx: * be issued above/below 16 bytes. */ if (uap->vendor->dma_threshold) - writew(ST_UART011_DMAWM_RX_16 | ST_UART011_DMAWM_TX_16, - uap->port.membase + ST_UART011_DMAWM); + pl011_write(ST_UART011_DMAWM_RX_16 | ST_UART011_DMAWM_TX_16, + uap, REG_ST_DMAWM); if (uap->using_rx_dma) { if (pl011_dma_rx_trigger_dma(uap)) @@ -1075,12 +1166,12 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap) return; /* Disable RX and TX DMA */ - while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY) - barrier(); + while (pl011_read(uap, REG_FR) & UART01x_FR_BUSY) + cpu_relax(); spin_lock_irq(&uap->port.lock); uap->dmacr &= ~(UART011_DMAONERR | UART011_RXDMAE | UART011_TXDMAE); - writew(uap->dmacr, uap->port.membase + UART011_DMACR); + pl011_write(uap->dmacr, uap, REG_DMACR); spin_unlock_irq(&uap->port.lock); if (uap->using_tx_dma) { @@ -1181,7 +1272,7 @@ static void pl011_stop_tx(struct uart_port *port) container_of(port, struct uart_amba_port, port); uap->im &= ~UART011_TXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_write(uap->im, uap, REG_IMSC); pl011_dma_tx_stop(uap); } @@ -1191,7 +1282,7 @@ static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq); static void pl011_start_tx_pio(struct uart_amba_port *uap) { uap->im |= UART011_TXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_write(uap->im, uap, REG_IMSC); pl011_tx_chars(uap, false); } @@ -1211,7 +1302,7 @@ static void pl011_stop_rx(struct uart_port *port) uap->im &= ~(UART011_RXIM|UART011_RTIM|UART011_FEIM| UART011_PEIM|UART011_BEIM|UART011_OEIM); - writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_write(uap->im, uap, REG_IMSC); pl011_dma_rx_stop(uap); } @@ -1222,7 +1313,7 @@ static void pl011_enable_ms(struct uart_port *port) container_of(port, struct uart_amba_port, port); uap->im |= UART011_RIMIM|UART011_CTSMIM|UART011_DCDMIM|UART011_DSRMIM; - writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_write(uap->im, uap, REG_IMSC); } static void pl011_rx_chars(struct uart_amba_port *uap) @@ -1242,7 +1333,7 @@ __acquires(&uap->port.lock) dev_dbg(uap->port.dev, "could not trigger RX DMA job " "fall back to interrupt mode again\n"); uap->im |= UART011_RXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_write(uap->im, uap, REG_IMSC); } else { #ifdef CONFIG_DMA_ENGINE /* Start Rx DMA poll */ @@ -1263,10 +1354,10 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c, bool from_irq) { if (unlikely(!from_irq) && - readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) + pl011_read(uap, REG_FR) & UART01x_FR_TXFF) return false; /* unable to transmit character */ - writew(c, uap->port.membase + UART01x_DR); + pl011_write(c, uap, REG_DR); uap->port.icount.tx++; return true; @@ -1313,7 +1404,7 @@ static void pl011_modem_status(struct uart_amba_port *uap) { unsigned int status, delta; - status = readw(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY; + status = pl011_read(uap, REG_FR) & UART01x_FR_MODEM_ANY; delta = status ^ uap->old_status; uap->old_status = status; @@ -1341,15 +1432,15 @@ static void check_apply_cts_event_workaround(struct uart_amba_port *uap) return; /* workaround to make sure that all bits are unlocked.. */ - writew(0x00, uap->port.membase + UART011_ICR); + pl011_write(0x00, uap, REG_ICR); /* * WA: introduce 26ns(1 uart clk) delay before W1C; * single apb access will incur 2 pclk(133.12Mhz) delay, * so add 2 dummy reads */ - dummy_read = readw(uap->port.membase + UART011_ICR); - dummy_read = readw(uap->port.membase + UART011_ICR); + dummy_read = pl011_read(uap, REG_ICR); + dummy_read = pl011_read(uap, REG_ICR); } static irqreturn_t pl011_int(int irq, void *dev_id) @@ -1361,15 +1452,15 @@ static irqreturn_t pl011_int(int irq, void *dev_id) int handled = 0; spin_lock_irqsave(&uap->port.lock, flags); - imsc = readw(uap->port.membase + UART011_IMSC); - status = readw(uap->port.membase + UART011_RIS) & imsc; + imsc = pl011_read(uap, REG_IMSC); + status = pl011_read(uap, REG_RIS) & imsc; if (status) { do { check_apply_cts_event_workaround(uap); - writew(status & ~(UART011_TXIS|UART011_RTIS| - UART011_RXIS), - uap->port.membase + UART011_ICR); + pl011_write(status & ~(UART011_TXIS|UART011_RTIS| + UART011_RXIS), + uap, REG_ICR); if (status & (UART011_RTIS|UART011_RXIS)) { if (pl011_dma_rx_running(uap)) @@ -1386,7 +1477,7 @@ static irqreturn_t pl011_int(int irq, void *dev_id) if (pass_counter-- == 0) break; - status = readw(uap->port.membase + UART011_RIS) & imsc; + status = pl011_read(uap, REG_RIS) & imsc; } while (status != 0); handled = 1; } @@ -1400,7 +1491,7 @@ static unsigned int pl011_tx_empty(struct uart_port *port) { struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); - unsigned int status = readw(uap->port.membase + UART01x_FR); + unsigned int status = pl011_read(uap, REG_FR); return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT; } @@ -1409,7 +1500,7 @@ static unsigned int pl011_get_mctrl(struct uart_port *port) struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); unsigned int result = 0; - unsigned int status = readw(uap->port.membase + UART01x_FR); + unsigned int status = pl011_read(uap, REG_FR); #define TIOCMBIT(uartbit, tiocmbit) \ if (status & uartbit) \ @@ -1429,7 +1520,7 @@ static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl) container_of(port, struct uart_amba_port, port); unsigned int cr; - cr = readw(uap->port.membase + UART011_CR); + cr = pl011_read(uap, REG_CR); #define TIOCMBIT(tiocmbit, uartbit) \ if (mctrl & tiocmbit) \ @@ -1449,7 +1540,7 @@ static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl) } #undef TIOCMBIT - writew(cr, uap->port.membase + UART011_CR); + pl011_write(cr, uap, REG_CR); } static void pl011_break_ctl(struct uart_port *port, int break_state) @@ -1460,12 +1551,12 @@ static void pl011_break_ctl(struct uart_port *port, int break_state) unsigned int lcr_h; spin_lock_irqsave(&uap->port.lock, flags); - lcr_h = readw(uap->port.membase + uap->lcrh_tx); + lcr_h = pl011_read(uap, REG_LCRH_TX); if (break_state == -1) lcr_h |= UART01x_LCRH_BRK; else lcr_h &= ~UART01x_LCRH_BRK; - writew(lcr_h, uap->port.membase + uap->lcrh_tx); + pl011_write(lcr_h, uap, REG_LCRH_TX); spin_unlock_irqrestore(&uap->port.lock, flags); } @@ -1475,9 +1566,8 @@ static void pl011_quiesce_irqs(struct uart_port *port) { struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); - unsigned char __iomem *regs = uap->port.membase; - writew(readw(regs + UART011_MIS), regs + UART011_ICR); + pl011_write(pl011_read(uap, REG_MIS), uap, REG_ICR); /* * There is no way to clear TXIM as this is "ready to transmit IRQ", so * we simply mask it. start_tx() will unmask it. @@ -1491,7 +1581,8 @@ static void pl011_quiesce_irqs(struct uart_port *port) * (including tx queue), so we're also fine with start_tx()'s caller * side. */ - writew(readw(regs + UART011_IMSC) & ~UART011_TXIM, regs + UART011_IMSC); + pl011_write(pl011_read(uap, REG_IMSC) & ~UART011_TXIM, uap, + REG_IMSC); } static int pl011_get_poll_char(struct uart_port *port) @@ -1506,11 +1597,11 @@ static int pl011_get_poll_char(struct uart_port *port) */ pl011_quiesce_irqs(port); - status = readw(uap->port.membase + UART01x_FR); + status = pl011_read(uap, REG_FR); if (status & UART01x_FR_RXFE) return NO_POLL_CHAR; - return readw(uap->port.membase + UART01x_DR); + return pl011_read(uap, REG_DR); } static void pl011_put_poll_char(struct uart_port *port, @@ -1519,10 +1610,10 @@ static void pl011_put_poll_char(struct uart_port *port, struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); - while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) - barrier(); + while (pl011_read(uap, REG_FR) & UART01x_FR_TXFF) + cpu_relax(); - writew(ch, uap->port.membase + UART01x_DR); + pl011_write(ch, uap, REG_DR); } #endif /* CONFIG_CONSOLE_POLL */ @@ -1546,15 +1637,16 @@ static int pl011_hwinit(struct uart_port *port) uap->port.uartclk = clk_get_rate(uap->clk); /* Clear pending error and receive interrupts */ - writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS | - UART011_RTIS | UART011_RXIS, uap->port.membase + UART011_ICR); + pl011_write(UART011_OEIS | UART011_BEIS | UART011_PEIS | + UART011_FEIS | UART011_RTIS | UART011_RXIS, + uap, REG_ICR); /* * Save interrupts enable mask, and enable RX interrupts in case if * the interrupt is used for NMI entry. */ - uap->im = readw(uap->port.membase + UART011_IMSC); - writew(UART011_RTIM | UART011_RXIM, uap->port.membase + UART011_IMSC); + uap->im = pl011_read(uap, REG_IMSC); + pl011_write(UART011_RTIM | UART011_RXIM, uap, REG_IMSC); if (dev_get_platdata(uap->port.dev)) { struct amba_pl011_data *plat; @@ -1566,24 +1658,30 @@ static int pl011_hwinit(struct uart_port *port) return 0; } +static bool pl011_split_lcrh(const struct uart_amba_port *uap) +{ + return pl011_reg_to_offset(uap, REG_LCRH_RX) != + pl011_reg_to_offset(uap, REG_LCRH_TX); +} + static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h) { - writew(lcr_h, uap->port.membase + uap->lcrh_rx); - if (uap->lcrh_rx != uap->lcrh_tx) { + pl011_write(lcr_h, uap, REG_LCRH_RX); + if (pl011_split_lcrh(uap)) { int i; /* * Wait 10 PCLKs before writing LCRH_TX register, * to get this delay write read only register 10 times */ for (i = 0; i < 10; ++i) - writew(0xff, uap->port.membase + UART011_MIS); - writew(lcr_h, uap->port.membase + uap->lcrh_tx); + pl011_write(0xff, uap, REG_MIS); + pl011_write(lcr_h, uap, REG_LCRH_TX); } } static int pl011_allocate_irq(struct uart_amba_port *uap) { - writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_write(uap->im, uap, REG_IMSC); return request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap); } @@ -1598,12 +1696,11 @@ static void pl011_enable_interrupts(struct uart_amba_port *uap) spin_lock_irq(&uap->port.lock); /* Clear out any spuriously appearing RX interrupts */ - writew(UART011_RTIS | UART011_RXIS, - uap->port.membase + UART011_ICR); + pl011_write(UART011_RTIS | UART011_RXIS, uap, REG_ICR); uap->im = UART011_RTIM; if (!pl011_dma_rx_running(uap)) uap->im |= UART011_RXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_write(uap->im, uap, REG_IMSC); spin_unlock_irq(&uap->port.lock); } @@ -1622,21 +1719,21 @@ static int pl011_startup(struct uart_port *port) if (retval) goto clk_dis; - writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS); + pl011_write(uap->vendor->ifls, uap, REG_IFLS); spin_lock_irq(&uap->port.lock); /* restore RTS and DTR */ cr = uap->old_cr & (UART011_CR_RTS | UART011_CR_DTR); cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE; - writew(cr, uap->port.membase + UART011_CR); + pl011_write(cr, uap, REG_CR); spin_unlock_irq(&uap->port.lock); /* * initialise the old status of the modem signals */ - uap->old_status = readw(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY; + uap->old_status = pl011_read(uap, REG_FR) & UART01x_FR_MODEM_ANY; /* Startup DMA */ pl011_dma_startup(uap); @@ -1677,9 +1774,9 @@ static void pl011_shutdown_channel(struct uart_amba_port *uap, { unsigned long val; - val = readw(uap->port.membase + lcrh); + val = pl011_read(uap, lcrh); val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN); - writew(val, uap->port.membase + lcrh); + pl011_write(val, uap, lcrh); } /* @@ -1693,19 +1790,19 @@ static void pl011_disable_uart(struct uart_amba_port *uap) uap->autorts = false; spin_lock_irq(&uap->port.lock); - cr = readw(uap->port.membase + UART011_CR); + cr = pl011_read(uap, REG_CR); uap->old_cr = cr; cr &= UART011_CR_RTS | UART011_CR_DTR; cr |= UART01x_CR_UARTEN | UART011_CR_TXE; - writew(cr, uap->port.membase + UART011_CR); + pl011_write(cr, uap, REG_CR); spin_unlock_irq(&uap->port.lock); /* * disable break condition and fifos */ - pl011_shutdown_channel(uap, uap->lcrh_rx); - if (uap->lcrh_rx != uap->lcrh_tx) - pl011_shutdown_channel(uap, uap->lcrh_tx); + pl011_shutdown_channel(uap, REG_LCRH_RX); + if (pl011_split_lcrh(uap)) + pl011_shutdown_channel(uap, REG_LCRH_TX); } static void pl011_disable_interrupts(struct uart_amba_port *uap) @@ -1714,8 +1811,8 @@ static void pl011_disable_interrupts(struct uart_amba_port *uap) /* mask all interrupts and clear all pending ones */ uap->im = 0; - writew(uap->im, uap->port.membase + UART011_IMSC); - writew(0xffff, uap->port.membase + UART011_ICR); + pl011_write(uap->im, uap, REG_IMSC); + pl011_write(0xffff, uap, REG_ICR); spin_unlock_irq(&uap->port.lock); } @@ -1850,6 +1947,8 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, lcr_h |= UART01x_LCRH_PEN; if (!(termios->c_cflag & PARODD)) lcr_h |= UART01x_LCRH_EPS; + if (termios->c_cflag & CMSPAR) + lcr_h |= UART011_LCRH_SPS; } if (uap->fifosize > 1) lcr_h |= UART01x_LCRH_FEN; @@ -1867,8 +1966,8 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, pl011_enable_ms(port); /* first, disable everything */ - old_cr = readw(port->membase + UART011_CR); - writew(0, port->membase + UART011_CR); + old_cr = pl011_read(uap, REG_CR); + pl011_write(0, uap, REG_CR); if (termios->c_cflag & CRTSCTS) { if (old_cr & UART011_CR_RTS) @@ -1901,17 +2000,17 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, quot -= 2; } /* Set baud rate */ - writew(quot & 0x3f, port->membase + UART011_FBRD); - writew(quot >> 6, port->membase + UART011_IBRD); + pl011_write(quot & 0x3f, uap, REG_FBRD); + pl011_write(quot >> 6, uap, REG_IBRD); /* * ----------v----------v----------v----------v----- - * NOTE: lcrh_tx and lcrh_rx MUST BE WRITTEN AFTER - * UART011_FBRD & UART011_IBRD. + * NOTE: REG_LCRH_TX and REG_LCRH_RX MUST BE WRITTEN AFTER + * REG_FBRD & REG_IBRD. * ----------^----------^----------^----------^----- */ pl011_write_lcr_h(uap, lcr_h); - writew(old_cr, port->membase + UART011_CR); + pl011_write(old_cr, uap, REG_CR); spin_unlock_irqrestore(&port->lock, flags); } @@ -2052,16 +2151,16 @@ static void pl011_console_putchar(struct uart_port *port, int ch) struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); - while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) - barrier(); - writew(ch, uap->port.membase + UART01x_DR); + while (pl011_read(uap, REG_FR) & UART01x_FR_TXFF) + cpu_relax(); + pl011_write(ch, uap, REG_DR); } static void pl011_console_write(struct console *co, const char *s, unsigned int count) { struct uart_amba_port *uap = amba_ports[co->index]; - unsigned int status, old_cr = 0, new_cr; + unsigned int old_cr = 0, new_cr; unsigned long flags; int locked = 1; @@ -2079,10 +2178,10 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) * First save the CR then disable the interrupts */ if (!uap->vendor->always_enabled) { - old_cr = readw(uap->port.membase + UART011_CR); + old_cr = pl011_read(uap, REG_CR); new_cr = old_cr & ~UART011_CR_CTSEN; new_cr |= UART01x_CR_UARTEN | UART011_CR_TXE; - writew(new_cr, uap->port.membase + UART011_CR); + pl011_write(new_cr, uap, REG_CR); } uart_console_write(&uap->port, s, count, pl011_console_putchar); @@ -2091,11 +2190,10 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) * Finally, wait for transmitter to become empty * and restore the TCR */ - do { - status = readw(uap->port.membase + UART01x_FR); - } while (status & UART01x_FR_BUSY); + while (pl011_read(uap, REG_FR) & UART01x_FR_BUSY) + cpu_relax(); if (!uap->vendor->always_enabled) - writew(old_cr, uap->port.membase + UART011_CR); + pl011_write(old_cr, uap, REG_CR); if (locked) spin_unlock(&uap->port.lock); @@ -2108,10 +2206,10 @@ static void __init pl011_console_get_options(struct uart_amba_port *uap, int *baud, int *parity, int *bits) { - if (readw(uap->port.membase + UART011_CR) & UART01x_CR_UARTEN) { + if (pl011_read(uap, REG_CR) & UART01x_CR_UARTEN) { unsigned int lcr_h, ibrd, fbrd; - lcr_h = readw(uap->port.membase + uap->lcrh_tx); + lcr_h = pl011_read(uap, REG_LCRH_TX); *parity = 'n'; if (lcr_h & UART01x_LCRH_PEN) { @@ -2126,13 +2224,13 @@ pl011_console_get_options(struct uart_amba_port *uap, int *baud, else *bits = 8; - ibrd = readw(uap->port.membase + UART011_IBRD); - fbrd = readw(uap->port.membase + UART011_FBRD); + ibrd = pl011_read(uap, REG_IBRD); + fbrd = pl011_read(uap, REG_FBRD); *baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd); if (uap->vendor->oversampling) { - if (readw(uap->port.membase + UART011_CR) + if (pl011_read(uap, REG_CR) & ST_UART011_CR_OVSFACT) *baud *= 2; } @@ -2205,10 +2303,13 @@ static struct console amba_console = { static void pl011_putc(struct uart_port *port, int c) { while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF) - ; - writeb(c, port->membase + UART01x_DR); + cpu_relax(); + if (port->iotype == UPIO_MEM32) + writel(c, port->membase + UART01x_DR); + else + writeb(c, port->membase + UART01x_DR); while (readl(port->membase + UART01x_FR) & UART01x_FR_BUSY) - ; + cpu_relax(); } static void pl011_early_write(struct console *con, const char *s, unsigned n) @@ -2227,7 +2328,6 @@ static int __init pl011_early_console_setup(struct earlycon_device *device, device->con->write = pl011_early_write; return 0; } -EARLYCON_DECLARE(pl011, pl011_early_console_setup); OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup); #else @@ -2319,7 +2419,6 @@ static int pl011_setup_port(struct device *dev, struct uart_amba_port *uap, uap->port.dev = dev; uap->port.mapbase = mmiobase->start; uap->port.membase = base; - uap->port.iotype = UPIO_MEM; uap->port.fifosize = uap->fifosize; uap->port.flags = UPF_BOOT_AUTOCONF; uap->port.line = index; @@ -2334,8 +2433,8 @@ static int pl011_register_port(struct uart_amba_port *uap) int ret; /* Ensure interrupts from this UART are masked and cleared */ - writew(0, uap->port.membase + UART011_IMSC); - writew(0xffff, uap->port.membase + UART011_ICR); + pl011_write(0, uap, REG_IMSC); + pl011_write(0xffff, uap, REG_ICR); if (!amba_reg.state) { ret = uart_register_driver(&amba_reg); @@ -2372,10 +2471,10 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) if (IS_ERR(uap->clk)) return PTR_ERR(uap->clk); + uap->reg_offset = vendor->reg_offset; uap->vendor = vendor; - uap->lcrh_rx = vendor->lcrh_rx; - uap->lcrh_tx = vendor->lcrh_tx; uap->fifosize = vendor->get_fifosize(dev); + uap->port.iotype = vendor->access_32b ? UPIO_MEM32 : UPIO_MEM; uap->port.irq = dev->irq[0]; uap->port.ops = &amba_pl011_pops; @@ -2453,8 +2552,10 @@ static int sbsa_uart_probe(struct platform_device *pdev) if (!uap) return -ENOMEM; + uap->reg_offset = vendor_sbsa.reg_offset; uap->vendor = &vendor_sbsa; uap->fifosize = 32; + uap->port.iotype = vendor_sbsa.access_32b ? UPIO_MEM32 : UPIO_MEM; uap->port.irq = platform_get_irq(pdev, 0); uap->port.ops = &sbsa_uart_pops; uap->fixed_baud = baudrate; diff --git a/drivers/tty/serial/amba-pl011.h b/drivers/tty/serial/amba-pl011.h new file mode 100644 index 000000000000..411c60e1f9a4 --- /dev/null +++ b/drivers/tty/serial/amba-pl011.h @@ -0,0 +1,34 @@ +#ifndef AMBA_PL011_H +#define AMBA_PL011_H + +enum { + REG_DR, + REG_ST_DMAWM, + REG_ST_TIMEOUT, + REG_FR, + REG_LCRH_RX, + REG_LCRH_TX, + REG_IBRD, + REG_FBRD, + REG_CR, + REG_IFLS, + REG_IMSC, + REG_RIS, + REG_MIS, + REG_ICR, + REG_DMACR, + REG_ST_XFCR, + REG_ST_XON1, + REG_ST_XON2, + REG_ST_XOFF1, + REG_ST_XOFF2, + REG_ST_ITCR, + REG_ST_ITIP, + REG_ST_ABCR, + REG_ST_ABIMSC, + + /* The size of the array - must be last */ + REG_ARRAY_SIZE, +}; + +#endif diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index 03ebe401fff7..3a1de5c87cb4 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -576,7 +576,6 @@ static int __init arc_early_console_setup(struct earlycon_device *dev, dev->con->write = arc_early_serial_write; return 0; } -EARLYCON_DECLARE(arc_uart, arc_early_console_setup); OF_EARLYCON_DECLARE(arc_uart, "snps,arc-uart", arc_early_console_setup); #endif /* CONFIG_SERIAL_ARC_CONSOLE */ diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 94294558943c..d9439e6ab719 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -22,7 +22,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include <linux/module.h> #include <linux/tty.h> #include <linux/ioport.h> #include <linux/slab.h> @@ -155,14 +154,14 @@ struct atmel_uart_port { struct circ_buf rx_ring; struct mctrl_gpios *gpios; - int gpio_irq[UART_GPIO_MAX]; unsigned int tx_done_mask; u32 fifo_size; u32 rts_high; u32 rts_low; bool ms_irq_enabled; - bool is_usart; /* usart or uart */ - struct timer_list uart_timer; /* uart timer */ + u32 rtor; /* address of receiver timeout register if it exists */ + bool has_hw_timer; + struct timer_list uart_timer; bool suspended; unsigned int pending; @@ -190,8 +189,6 @@ static const struct of_device_id atmel_serial_dt_ids[] = { { .compatible = "atmel,at91sam9260-usart" }, { /* sentinel */ } }; - -MODULE_DEVICE_TABLE(of, atmel_serial_dt_ids); #endif static inline struct atmel_uart_port * @@ -550,27 +547,21 @@ static void atmel_enable_ms(struct uart_port *port) atmel_port->ms_irq_enabled = true; - if (atmel_port->gpio_irq[UART_GPIO_CTS] >= 0) - enable_irq(atmel_port->gpio_irq[UART_GPIO_CTS]); - else + if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS)) ier |= ATMEL_US_CTSIC; - if (atmel_port->gpio_irq[UART_GPIO_DSR] >= 0) - enable_irq(atmel_port->gpio_irq[UART_GPIO_DSR]); - else + if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_DSR)) ier |= ATMEL_US_DSRIC; - if (atmel_port->gpio_irq[UART_GPIO_RI] >= 0) - enable_irq(atmel_port->gpio_irq[UART_GPIO_RI]); - else + if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_RI)) ier |= ATMEL_US_RIIC; - if (atmel_port->gpio_irq[UART_GPIO_DCD] >= 0) - enable_irq(atmel_port->gpio_irq[UART_GPIO_DCD]); - else + if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_DCD)) ier |= ATMEL_US_DCDIC; atmel_uart_writel(port, ATMEL_US_IER, ier); + + mctrl_gpio_enable_ms(atmel_port->gpios); } /* @@ -589,24 +580,18 @@ static void atmel_disable_ms(struct uart_port *port) atmel_port->ms_irq_enabled = false; - if (atmel_port->gpio_irq[UART_GPIO_CTS] >= 0) - disable_irq(atmel_port->gpio_irq[UART_GPIO_CTS]); - else + mctrl_gpio_disable_ms(atmel_port->gpios); + + if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS)) idr |= ATMEL_US_CTSIC; - if (atmel_port->gpio_irq[UART_GPIO_DSR] >= 0) - disable_irq(atmel_port->gpio_irq[UART_GPIO_DSR]); - else + if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_DSR)) idr |= ATMEL_US_DSRIC; - if (atmel_port->gpio_irq[UART_GPIO_RI] >= 0) - disable_irq(atmel_port->gpio_irq[UART_GPIO_RI]); - else + if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_RI)) idr |= ATMEL_US_RIIC; - if (atmel_port->gpio_irq[UART_GPIO_DCD] >= 0) - disable_irq(atmel_port->gpio_irq[UART_GPIO_DCD]); - else + if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_DCD)) idr |= ATMEL_US_DCDIC; atmel_uart_writel(port, ATMEL_US_IDR, idr); @@ -1264,7 +1249,6 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id) struct uart_port *port = dev_id; struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsigned int status, pending, mask, pass_counter = 0; - bool gpio_handled = false; spin_lock(&atmel_port->lock_suspended); @@ -1272,24 +1256,6 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id) status = atmel_get_lines_status(port); mask = atmel_uart_readl(port, ATMEL_US_IMR); pending = status & mask; - if (!gpio_handled) { - /* - * Dealing with GPIO interrupt - */ - if (irq == atmel_port->gpio_irq[UART_GPIO_CTS]) - pending |= ATMEL_US_CTSIC; - - if (irq == atmel_port->gpio_irq[UART_GPIO_DSR]) - pending |= ATMEL_US_DSRIC; - - if (irq == atmel_port->gpio_irq[UART_GPIO_RI]) - pending |= ATMEL_US_RIIC; - - if (irq == atmel_port->gpio_irq[UART_GPIO_DCD]) - pending |= ATMEL_US_DCDIC; - - gpio_handled = true; - } if (!pending) break; @@ -1745,19 +1711,24 @@ static void atmel_get_ip_name(struct uart_port *port) struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); int name = atmel_uart_readl(port, ATMEL_US_NAME); u32 version; - int usart, uart; - /* usart and uart ascii */ - usart = 0x55534152; - uart = 0x44424755; - - atmel_port->is_usart = false; - - if (name == usart) { - dev_dbg(port->dev, "This is usart\n"); - atmel_port->is_usart = true; - } else if (name == uart) { - dev_dbg(port->dev, "This is uart\n"); - atmel_port->is_usart = false; + u32 usart, dbgu_uart, new_uart; + /* ASCII decoding for IP version */ + usart = 0x55534152; /* USAR(T) */ + dbgu_uart = 0x44424755; /* DBGU */ + new_uart = 0x55415254; /* UART */ + + atmel_port->has_hw_timer = false; + + if (name == new_uart) { + dev_dbg(port->dev, "Uart with hw timer"); + atmel_port->has_hw_timer = true; + atmel_port->rtor = ATMEL_UA_RTOR; + } else if (name == usart) { + dev_dbg(port->dev, "Usart\n"); + atmel_port->has_hw_timer = true; + atmel_port->rtor = ATMEL_US_RTOR; + } else if (name == dbgu_uart) { + dev_dbg(port->dev, "Dbgu or uart without hw timer\n"); } else { /* fallback for older SoCs: use version field */ version = atmel_uart_readl(port, ATMEL_US_VERSION); @@ -1765,12 +1736,12 @@ static void atmel_get_ip_name(struct uart_port *port) case 0x302: case 0x10213: dev_dbg(port->dev, "This version is usart\n"); - atmel_port->is_usart = true; + atmel_port->has_hw_timer = true; + atmel_port->rtor = ATMEL_US_RTOR; break; case 0x203: case 0x10202: dev_dbg(port->dev, "This version is uart\n"); - atmel_port->is_usart = false; break; default: dev_err(port->dev, "Not supported ip name nor version, set to uart\n"); @@ -1778,45 +1749,6 @@ static void atmel_get_ip_name(struct uart_port *port) } } -static void atmel_free_gpio_irq(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - enum mctrl_gpio_idx i; - - for (i = 0; i < UART_GPIO_MAX; i++) - if (atmel_port->gpio_irq[i] >= 0) - free_irq(atmel_port->gpio_irq[i], port); -} - -static int atmel_request_gpio_irq(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - int *irq = atmel_port->gpio_irq; - enum mctrl_gpio_idx i; - int err = 0; - - for (i = 0; (i < UART_GPIO_MAX) && !err; i++) { - if (irq[i] < 0) - continue; - - irq_set_status_flags(irq[i], IRQ_NOAUTOEN); - err = request_irq(irq[i], atmel_interrupt, IRQ_TYPE_EDGE_BOTH, - "atmel_serial", port); - if (err) - dev_err(port->dev, "atmel_startup - Can't get %d irq\n", - irq[i]); - } - - /* - * If something went wrong, rollback. - */ - while (err && (--i >= 0)) - if (irq[i] >= 0) - free_irq(irq[i], port); - - return err; -} - /* * Perform initialization and enable port for reception */ @@ -1846,13 +1778,6 @@ static int atmel_startup(struct uart_port *port) return retval; } - /* - * Get the GPIO lines IRQ - */ - retval = atmel_request_gpio_irq(port); - if (retval) - goto free_irq; - tasklet_enable(&atmel_port->tasklet); /* @@ -1916,12 +1841,13 @@ static int atmel_startup(struct uart_port *port) if (atmel_use_pdc_rx(port)) { /* set UART timeout */ - if (!atmel_port->is_usart) { + if (!atmel_port->has_hw_timer) { mod_timer(&atmel_port->uart_timer, jiffies + uart_poll_timeout(port)); /* set USART timeout */ } else { - atmel_uart_writel(port, ATMEL_US_RTOR, PDC_RX_TIMEOUT); + atmel_uart_writel(port, atmel_port->rtor, + PDC_RX_TIMEOUT); atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_STTTO); atmel_uart_writel(port, ATMEL_US_IER, @@ -1931,12 +1857,13 @@ static int atmel_startup(struct uart_port *port) atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); } else if (atmel_use_dma_rx(port)) { /* set UART timeout */ - if (!atmel_port->is_usart) { + if (!atmel_port->has_hw_timer) { mod_timer(&atmel_port->uart_timer, jiffies + uart_poll_timeout(port)); /* set USART timeout */ } else { - atmel_uart_writel(port, ATMEL_US_RTOR, PDC_RX_TIMEOUT); + atmel_uart_writel(port, atmel_port->rtor, + PDC_RX_TIMEOUT); atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_STTTO); atmel_uart_writel(port, ATMEL_US_IER, @@ -1948,11 +1875,6 @@ static int atmel_startup(struct uart_port *port) } return 0; - -free_irq: - free_irq(port->irq, port); - - return retval; } /* @@ -2018,7 +1940,6 @@ static void atmel_shutdown(struct uart_port *port) * Free the interrupts */ free_irq(port->irq, port); - atmel_free_gpio_irq(port); atmel_port->ms_irq_enabled = false; @@ -2565,13 +2486,13 @@ static int __init atmel_console_init(void) struct atmel_uart_data *pdata = dev_get_platdata(&atmel_default_console_device->dev); int id = pdata->num; - struct atmel_uart_port *port = &atmel_ports[id]; + struct atmel_uart_port *atmel_port = &atmel_ports[id]; - port->backup_imr = 0; - port->uart.line = id; + atmel_port->backup_imr = 0; + atmel_port->uart.line = id; add_preferred_console(ATMEL_DEVICENAME, id, NULL); - ret = atmel_init_port(port, atmel_default_console_device); + ret = atmel_init_port(atmel_port, atmel_default_console_device); if (ret) return ret; register_console(&atmel_console); @@ -2686,43 +2607,23 @@ static int atmel_serial_resume(struct platform_device *pdev) #define atmel_serial_resume NULL #endif -static int atmel_init_gpios(struct atmel_uart_port *p, struct device *dev) -{ - enum mctrl_gpio_idx i; - struct gpio_desc *gpiod; - - p->gpios = mctrl_gpio_init_noauto(dev, 0); - if (IS_ERR(p->gpios)) - return PTR_ERR(p->gpios); - - for (i = 0; i < UART_GPIO_MAX; i++) { - gpiod = mctrl_gpio_to_gpiod(p->gpios, i); - if (gpiod && (gpiod_get_direction(gpiod) == GPIOF_DIR_IN)) - p->gpio_irq[i] = gpiod_to_irq(gpiod); - else - p->gpio_irq[i] = -EINVAL; - } - - return 0; -} - -static void atmel_serial_probe_fifos(struct atmel_uart_port *port, +static void atmel_serial_probe_fifos(struct atmel_uart_port *atmel_port, struct platform_device *pdev) { - port->fifo_size = 0; - port->rts_low = 0; - port->rts_high = 0; + atmel_port->fifo_size = 0; + atmel_port->rts_low = 0; + atmel_port->rts_high = 0; if (of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size", - &port->fifo_size)) + &atmel_port->fifo_size)) return; - if (!port->fifo_size) + if (!atmel_port->fifo_size) return; - if (port->fifo_size < ATMEL_MIN_FIFO_SIZE) { - port->fifo_size = 0; + if (atmel_port->fifo_size < ATMEL_MIN_FIFO_SIZE) { + atmel_port->fifo_size = 0; dev_err(&pdev->dev, "Invalid FIFO size\n"); return; } @@ -2735,22 +2636,22 @@ static void atmel_serial_probe_fifos(struct atmel_uart_port *port, * Threshold to a reasonably high value respecting this 16 data * empirical rule when possible. */ - port->rts_high = max_t(int, port->fifo_size >> 1, - port->fifo_size - ATMEL_RTS_HIGH_OFFSET); - port->rts_low = max_t(int, port->fifo_size >> 2, - port->fifo_size - ATMEL_RTS_LOW_OFFSET); + atmel_port->rts_high = max_t(int, atmel_port->fifo_size >> 1, + atmel_port->fifo_size - ATMEL_RTS_HIGH_OFFSET); + atmel_port->rts_low = max_t(int, atmel_port->fifo_size >> 2, + atmel_port->fifo_size - ATMEL_RTS_LOW_OFFSET); dev_info(&pdev->dev, "Using FIFO (%u data)\n", - port->fifo_size); + atmel_port->fifo_size); dev_dbg(&pdev->dev, "RTS High Threshold : %2u data\n", - port->rts_high); + atmel_port->rts_high); dev_dbg(&pdev->dev, "RTS Low Threshold : %2u data\n", - port->rts_low); + atmel_port->rts_low); } static int atmel_serial_probe(struct platform_device *pdev) { - struct atmel_uart_port *port; + struct atmel_uart_port *atmel_port; struct device_node *np = pdev->dev.of_node; struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); void *data; @@ -2781,91 +2682,101 @@ static int atmel_serial_probe(struct platform_device *pdev) goto err; } - port = &atmel_ports[ret]; - port->backup_imr = 0; - port->uart.line = ret; - atmel_serial_probe_fifos(port, pdev); + atmel_port = &atmel_ports[ret]; + atmel_port->backup_imr = 0; + atmel_port->uart.line = ret; + atmel_serial_probe_fifos(atmel_port, pdev); - spin_lock_init(&port->lock_suspended); + spin_lock_init(&atmel_port->lock_suspended); - ret = atmel_init_gpios(port, &pdev->dev); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to initialize GPIOs."); + ret = atmel_init_port(atmel_port, pdev); + if (ret) goto err_clear_bit; - } - ret = atmel_init_port(port, pdev); - if (ret) + atmel_port->gpios = mctrl_gpio_init(&atmel_port->uart, 0); + if (IS_ERR(atmel_port->gpios)) { + ret = PTR_ERR(atmel_port->gpios); goto err_clear_bit; + } - if (!atmel_use_pdc_rx(&port->uart)) { + if (!atmel_use_pdc_rx(&atmel_port->uart)) { ret = -ENOMEM; data = kmalloc(sizeof(struct atmel_uart_char) * ATMEL_SERIAL_RINGSIZE, GFP_KERNEL); if (!data) goto err_alloc_ring; - port->rx_ring.buf = data; + atmel_port->rx_ring.buf = data; } - rs485_enabled = port->uart.rs485.flags & SER_RS485_ENABLED; + rs485_enabled = atmel_port->uart.rs485.flags & SER_RS485_ENABLED; - ret = uart_add_one_port(&atmel_uart, &port->uart); + ret = uart_add_one_port(&atmel_uart, &atmel_port->uart); if (ret) goto err_add_port; #ifdef CONFIG_SERIAL_ATMEL_CONSOLE - if (atmel_is_console_port(&port->uart) + if (atmel_is_console_port(&atmel_port->uart) && ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) { /* * The serial core enabled the clock for us, so undo * the clk_prepare_enable() in atmel_console_setup() */ - clk_disable_unprepare(port->clk); + clk_disable_unprepare(atmel_port->clk); } #endif device_init_wakeup(&pdev->dev, 1); - platform_set_drvdata(pdev, port); + platform_set_drvdata(pdev, atmel_port); /* * The peripheral clock has been disabled by atmel_init_port(): * enable it before accessing I/O registers */ - clk_prepare_enable(port->clk); + clk_prepare_enable(atmel_port->clk); if (rs485_enabled) { - atmel_uart_writel(&port->uart, ATMEL_US_MR, + atmel_uart_writel(&atmel_port->uart, ATMEL_US_MR, ATMEL_US_USMODE_NORMAL); - atmel_uart_writel(&port->uart, ATMEL_US_CR, ATMEL_US_RTSEN); + atmel_uart_writel(&atmel_port->uart, ATMEL_US_CR, + ATMEL_US_RTSEN); } /* * Get port name of usart or uart */ - atmel_get_ip_name(&port->uart); + atmel_get_ip_name(&atmel_port->uart); /* * The peripheral clock can now safely be disabled till the port * is used */ - clk_disable_unprepare(port->clk); + clk_disable_unprepare(atmel_port->clk); return 0; err_add_port: - kfree(port->rx_ring.buf); - port->rx_ring.buf = NULL; + kfree(atmel_port->rx_ring.buf); + atmel_port->rx_ring.buf = NULL; err_alloc_ring: - if (!atmel_is_console_port(&port->uart)) { - clk_put(port->clk); - port->clk = NULL; + if (!atmel_is_console_port(&atmel_port->uart)) { + clk_put(atmel_port->clk); + atmel_port->clk = NULL; } err_clear_bit: - clear_bit(port->uart.line, atmel_ports_in_use); + clear_bit(atmel_port->uart.line, atmel_ports_in_use); err: return ret; } +/* + * Even if the driver is not modular, it makes sense to be able to + * unbind a device: there can be many bound devices, and there are + * situations where dynamic binding and unbinding can be useful. + * + * For example, a connected device can require a specific firmware update + * protocol that needs bitbanging on IO lines, but use the regular serial + * port in the normal case. + */ static int atmel_serial_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); @@ -2885,6 +2796,7 @@ static int atmel_serial_remove(struct platform_device *pdev) clear_bit(port->line, atmel_ports_in_use); clk_put(atmel_port->clk); + atmel_port->clk = NULL; return ret; } @@ -2895,8 +2807,8 @@ static struct platform_driver atmel_serial_driver = { .suspend = atmel_serial_suspend, .resume = atmel_serial_resume, .driver = { - .name = "atmel_usart", - .of_match_table = of_match_ptr(atmel_serial_dt_ids), + .name = "atmel_usart", + .of_match_table = of_match_ptr(atmel_serial_dt_ids), }, }; @@ -2914,17 +2826,4 @@ static int __init atmel_serial_init(void) return ret; } - -static void __exit atmel_serial_exit(void) -{ - platform_driver_unregister(&atmel_serial_driver); - uart_unregister_driver(&atmel_uart); -} - -module_init(atmel_serial_init); -module_exit(atmel_serial_exit); - -MODULE_AUTHOR("Rick Bronson"); -MODULE_DESCRIPTION("Atmel AT91 / AT32 serial port driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:atmel_usart"); +device_initcall(atmel_serial_init); diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index a1c0a89d9c7f..c28e5c24da16 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -653,7 +653,7 @@ static struct uart_ops bcm_uart_ops = { #ifdef CONFIG_SERIAL_BCM63XX_CONSOLE -static inline void wait_for_xmitr(struct uart_port *port) +static void wait_for_xmitr(struct uart_port *port) { unsigned int tmout; diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index ae3cf94b146b..293ecbb00684 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -213,7 +213,7 @@ static void bfin_serial_stop_rx(struct uart_port *port) static void bfin_serial_rx_chars(struct bfin_serial_port *uart) { unsigned int status, ch, flg; - static struct timeval anomaly_start = { .tv_sec = 0 }; + static u64 anomaly_start; status = UART_GET_LSR(uart); UART_CLEAR_LSR(uart); @@ -246,27 +246,24 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart) * character time +/- some percent. So 1.5 sounds good. All other * Blackfin families operate properly. Woo. */ - if (anomaly_start.tv_sec) { - struct timeval curr; - suseconds_t usecs; + if (anomaly_start > 0) { + u64 curr, nsecs, threshold_ns; if ((~ch & (~ch + 1)) & 0xff) goto known_good_char; - do_gettimeofday(&curr); - if (curr.tv_sec - anomaly_start.tv_sec > 1) + curr = ktime_get_ns(); + nsecs = curr - anomaly_start; + if (nsecs >> 32) goto known_good_char; - usecs = 0; - if (curr.tv_sec != anomaly_start.tv_sec) - usecs += USEC_PER_SEC; - usecs += curr.tv_usec - anomaly_start.tv_usec; - - if (usecs > UART_GET_ANOMALY_THRESHOLD(uart)) + threshold_ns = UART_GET_ANOMALY_THRESHOLD(uart) + * NSEC_PER_USEC; + if (nsecs > threshold_ns) goto known_good_char; if (ch) - anomaly_start.tv_sec = 0; + anomaly_start = 0; else anomaly_start = curr; @@ -274,14 +271,14 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart) known_good_char: status &= ~BI; - anomaly_start.tv_sec = 0; + anomaly_start = 0; } } if (status & BI) { if (ANOMALY_05000363) if (bfin_revid() < 5) - do_gettimeofday(&anomaly_start); + anomaly_start = ktime_get_ns(); uart->port.icount.brk++; if (uart_handle_break(&uart->port)) goto ignore_char; diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index b3a4e0cdddaa..5beafd2d2218 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -450,6 +450,7 @@ static int uart_clps711x_probe(struct platform_device *pdev) struct clps711x_port *s; struct resource *res; struct clk *uart_clk; + int irq; if (index < 0 || index >= UART_CLPS711X_NR) return -EINVAL; @@ -467,12 +468,13 @@ static int uart_clps711x_probe(struct platform_device *pdev) if (IS_ERR(s->port.membase)) return PTR_ERR(s->port.membase); - s->port.irq = platform_get_irq(pdev, 0); - if (IS_ERR_VALUE(s->port.irq)) - return s->port.irq; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + s->port.irq = irq; s->rx_irq = platform_get_irq(pdev, 1); - if (IS_ERR_VALUE(s->rx_irq)) + if (s->rx_irq < 0) return s->rx_irq; if (!np) { diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index f13f2ebd215b..c0172bf54a9b 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -1413,9 +1413,8 @@ rs_stop(struct tty_struct *tty) xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->port.tty)); xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, stop); - if (tty->termios.c_iflag & IXON ) { + if (I_IXON(tty)) xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); - } *((unsigned long *)&info->ioport[REG_XOFF]) = xoff; local_irq_restore(flags); @@ -1436,9 +1435,8 @@ rs_start(struct tty_struct *tty) info->xmit.tail,SERIAL_XMIT_SIZE))); xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty)); xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable); - if (tty->termios.c_iflag & IXON ) { + if (I_IXON(tty)) xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); - } *((unsigned long *)&info->ioport[REG_XOFF]) = xoff; if (!info->uses_dma_out && @@ -2968,7 +2966,7 @@ static int rs_raw_write(struct tty_struct *tty, local_save_flags(flags); DFLOW(DEBUG_LOG(info->line, "write count %i ", count)); - DFLOW(DEBUG_LOG(info->line, "ldisc %i\n", tty->ldisc.chars_in_buffer(tty))); + DFLOW(DEBUG_LOG(info->line, "ldisc\n")); /* The local_irq_disable/restore_flags pairs below are needed @@ -3161,13 +3159,12 @@ rs_throttle(struct tty_struct * tty) { struct e100_serial *info = (struct e100_serial *)tty->driver_data; #ifdef SERIAL_DEBUG_THROTTLE - printk("throttle %s: %lu....\n", tty_name(tty), - (unsigned long)tty->ldisc.chars_in_buffer(tty)); + printk("throttle %s ....\n", tty_name(tty)); #endif - DFLOW(DEBUG_LOG(info->line,"rs_throttle %lu\n", tty->ldisc.chars_in_buffer(tty))); + DFLOW(DEBUG_LOG(info->line,"rs_throttle\n")); /* Do RTS before XOFF since XOFF might take some time */ - if (tty->termios.c_cflag & CRTSCTS) { + if (C_CRTSCTS(tty)) { /* Turn off RTS line */ e100_rts(info, 0); } @@ -3181,13 +3178,12 @@ rs_unthrottle(struct tty_struct * tty) { struct e100_serial *info = (struct e100_serial *)tty->driver_data; #ifdef SERIAL_DEBUG_THROTTLE - printk("unthrottle %s: %lu....\n", tty_name(tty), - (unsigned long)tty->ldisc.chars_in_buffer(tty)); + printk("unthrottle %s ....\n", tty_name(tty)); #endif - DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc %d\n", tty->ldisc.chars_in_buffer(tty))); + DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc\n")); DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count)); /* Do RTS before XOFF since XOFF might take some time */ - if (tty->termios.c_cflag & CRTSCTS) { + if (C_CRTSCTS(tty)) { /* Assert RTS line */ e100_rts(info, 1); } @@ -3555,8 +3551,7 @@ rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) change_speed(info); /* Handle turning off CRTSCTS */ - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios.c_cflag & CRTSCTS)) + if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) rs_start(tty); } @@ -3615,7 +3610,6 @@ rs_close(struct tty_struct *tty, struct file * filp) local_irq_restore(flags); return; } - info->port.flags |= ASYNC_CLOSING; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. @@ -3654,7 +3648,7 @@ 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|ASYNC_CLOSING); + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; local_irq_restore(flags); /* port closed */ @@ -3767,9 +3761,8 @@ block_til_ready(struct tty_struct *tty, struct file * filp, return 0; } - if (tty->termios.c_cflag & CLOCAL) { - do_clocal = 1; - } + if (C_CLOCAL(tty)) + do_clocal = 1; /* * Block waiting for the carrier detect and the line to become diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c index a80cdad114f3..02ad6953b167 100644 --- a/drivers/tty/serial/digicolor-usart.c +++ b/drivers/tty/serial/digicolor-usart.c @@ -453,7 +453,7 @@ static struct uart_driver digicolor_uart = { static int digicolor_uart_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - int ret, index; + int irq, ret, index; struct digicolor_port *dp; struct resource *res; struct clk *uart_clk; @@ -481,9 +481,10 @@ static int digicolor_uart_probe(struct platform_device *pdev) if (IS_ERR(dp->port.membase)) return PTR_ERR(dp->port.membase); - dp->port.irq = platform_get_irq(pdev, 0); - if (IS_ERR_VALUE(dp->port.irq)) - return dp->port.irq; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + dp->port.irq = irq; dp->port.iotype = UPIO_MEM; dp->port.uartclk = clk_get_rate(uart_clk); diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index b5b2f2be6be7..067783f0523c 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -19,7 +19,8 @@ #include <linux/io.h> #include <linux/serial_core.h> #include <linux/sizes.h> -#include <linux/mod_devicetable.h> +#include <linux/of.h> +#include <linux/of_fdt.h> #ifdef CONFIG_FIX_EARLYCON_MEM #include <asm/fixmap.h> @@ -28,22 +29,15 @@ #include <asm/serial.h> static struct console early_con = { - .name = "uart", /* 8250 console switch requires this name */ + .name = "uart", /* fixed up at earlycon registration */ .flags = CON_PRINTBUFFER | CON_BOOT, - .index = -1, + .index = 0, }; static struct earlycon_device early_console_dev = { .con = &early_con, }; -extern struct earlycon_id __earlycon_table[]; -static const struct earlycon_id __earlycon_table_sentinel - __used __section(__earlycon_table_end); - -static const struct of_device_id __earlycon_of_table_sentinel - __used __section(__earlycon_of_table_end); - static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) { void __iomem *base; @@ -61,6 +55,39 @@ static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) return base; } +static void __init earlycon_init(struct earlycon_device *device, + const char *name) +{ + struct console *earlycon = device->con; + struct uart_port *port = &device->port; + const char *s; + size_t len; + + /* scan backwards from end of string for first non-numeral */ + for (s = name + strlen(name); + s > name && s[-1] >= '0' && s[-1] <= '9'; + s--) + ; + if (*s) + earlycon->index = simple_strtoul(s, NULL, 10); + len = s - name; + strlcpy(earlycon->name, name, min(len + 1, sizeof(earlycon->name))); + earlycon->data = &early_console_dev; + + if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM16 || + port->iotype == UPIO_MEM32 || port->iotype == UPIO_MEM32BE) + pr_info("%s%d at MMIO%s %pa (options '%s')\n", + earlycon->name, earlycon->index, + (port->iotype == UPIO_MEM) ? "" : + (port->iotype == UPIO_MEM16) ? "16" : + (port->iotype == UPIO_MEM32) ? "32" : "32be", + &port->mapbase, device->options); + else + pr_info("%s%d at I/O port 0x%lx (options '%s')\n", + earlycon->name, earlycon->index, + port->iobase, device->options); +} + static int __init parse_options(struct earlycon_device *device, char *options) { struct uart_port *port = &device->port; @@ -71,10 +98,16 @@ static int __init parse_options(struct earlycon_device *device, char *options) return -EINVAL; switch (port->iotype) { + case UPIO_MEM: + port->mapbase = addr; + break; + case UPIO_MEM16: + port->regshift = 1; + port->mapbase = addr; + break; case UPIO_MEM32: case UPIO_MEM32BE: - port->regshift = 2; /* fall-through */ - case UPIO_MEM: + port->regshift = 2; port->mapbase = addr; break; case UPIO_PORT: @@ -91,18 +124,6 @@ static int __init parse_options(struct earlycon_device *device, char *options) strlcpy(device->options, options, length); } - if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM32 || - port->iotype == UPIO_MEM32BE) - pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n", - (port->iotype == UPIO_MEM) ? "" : - (port->iotype == UPIO_MEM32) ? "32" : "32be", - (unsigned long long)port->mapbase, - device->options); - else - pr_info("Early serial console at I/O port 0x%lx (options '%s')\n", - port->iobase, - device->options); - return 0; } @@ -120,7 +141,7 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match) if (port->mapbase) port->membase = earlycon_map(port->mapbase, 64); - early_console_dev.con->data = &early_console_dev; + earlycon_init(&early_console_dev, match->name); err = match->setup(&early_console_dev, buf); if (err < 0) return err; @@ -159,7 +180,7 @@ int __init setup_earlycon(char *buf) if (early_con.flags & CON_ENABLED) return -EALREADY; - for (match = __earlycon_table; match->name[0]; match++) { + for (match = __earlycon_table; match < __earlycon_table_end; match++) { size_t len = strlen(match->name); if (strncmp(buf, match->name, len)) @@ -197,20 +218,62 @@ static int __init param_setup_earlycon(char *buf) } early_param("earlycon", param_setup_earlycon); -int __init of_setup_earlycon(unsigned long addr, - int (*setup)(struct earlycon_device *, const char *)) +#ifdef CONFIG_OF_EARLY_FLATTREE + +int __init of_setup_earlycon(const struct earlycon_id *match, + unsigned long node, + const char *options) { int err; struct uart_port *port = &early_console_dev.port; + const __be32 *val; + bool big_endian; + u64 addr; spin_lock_init(&port->lock); port->iotype = UPIO_MEM; + addr = of_flat_dt_translate_address(node); + if (addr == OF_BAD_ADDR) { + pr_warn("[%s] bad address\n", match->name); + return -ENXIO; + } port->mapbase = addr; port->uartclk = BASE_BAUD * 16; - port->membase = earlycon_map(addr, SZ_4K); + port->membase = earlycon_map(port->mapbase, SZ_4K); + + val = of_get_flat_dt_prop(node, "reg-offset", NULL); + if (val) + port->mapbase += be32_to_cpu(*val); + val = of_get_flat_dt_prop(node, "reg-shift", NULL); + if (val) + port->regshift = be32_to_cpu(*val); + big_endian = of_get_flat_dt_prop(node, "big-endian", NULL) != NULL || + (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) && + of_get_flat_dt_prop(node, "native-endian", NULL) != NULL); + val = of_get_flat_dt_prop(node, "reg-io-width", NULL); + if (val) { + switch (be32_to_cpu(*val)) { + case 1: + port->iotype = UPIO_MEM; + break; + case 2: + port->iotype = UPIO_MEM16; + break; + case 4: + port->iotype = (big_endian) ? UPIO_MEM32BE : UPIO_MEM32; + break; + default: + pr_warn("[%s] unsupported reg-io-width\n", match->name); + return -EINVAL; + } + } - early_console_dev.con->data = &early_console_dev; - err = setup(&early_console_dev, NULL); + if (options) { + strlcpy(early_console_dev.options, options, + sizeof(early_console_dev.options)); + } + earlycon_init(&early_console_dev, match->name); + err = match->setup(&early_console_dev, options); if (err < 0) return err; if (!early_console_dev.con->write) @@ -220,3 +283,5 @@ int __init of_setup_earlycon(unsigned long addr, register_console(early_console_dev.con); return 0; } + +#endif /* CONFIG_OF_EARLY_FLATTREE */ diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index ffc7cb2585a6..c60a8d5e4020 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -22,7 +22,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#define SERIAL_DO_RESTART #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 88246f7e435a..2085a6cfa44b 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -395,8 +395,10 @@ static int ifx_spi_decode_spi_header(unsigned char *buffer, int *length, if (h1 == 0 && h2 == 0) { *received_cts = 0; + *more = 0; return IFX_SPI_HEADER_0; } else if (h1 == 0xffff && h2 == 0xffff) { + *more = 0; /* spi_slave_cts remains as it was */ return IFX_SPI_HEADER_F; } @@ -688,6 +690,7 @@ static void ifx_spi_complete(void *ctx) ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD, (size_t)actual_length); } else { + more = 0; dev_dbg(&ifx_dev->spi_dev->dev, "SPI transfer error %d", ifx_dev->spi_msg.status); } diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 016e4be05cec..231e7d5caf6c 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -44,6 +44,8 @@ #include <linux/platform_data/serial-imx.h> #include <linux/platform_data/dma-imx.h> +#include "serial_mctrl_gpio.h" + /* Register definitions */ #define URXD0 0x0 /* Receiver Register */ #define URTX0 0x40 /* Transmitter Register */ @@ -148,8 +150,11 @@ #define USR2_TXFE (1<<14) /* Transmit buffer FIFO empty */ #define USR2_DTRF (1<<13) /* DTR edge interrupt flag */ #define USR2_IDLE (1<<12) /* Idle condition */ +#define USR2_RIDELT (1<<10) /* Ring Interrupt Delta */ +#define USR2_RIIN (1<<9) /* Ring Indicator Input */ #define USR2_IRINT (1<<8) /* Serial infrared interrupt flag */ #define USR2_WAKE (1<<7) /* Wake */ +#define USR2_DCDIN (1<<5) /* Data Carrier Detect Input */ #define USR2_RTSF (1<<4) /* RTS edge interrupt flag */ #define USR2_TXDC (1<<3) /* Transmitter complete */ #define USR2_BRCD (1<<2) /* Break condition */ @@ -206,6 +211,8 @@ struct imx_port { struct clk *clk_per; const struct imx_uart_data *devdata; + struct mctrl_gpios *gpios; + /* DMA fields */ unsigned int dma_is_inited:1; unsigned int dma_is_enabled:1; @@ -308,49 +315,24 @@ static void imx_port_ucrs_restore(struct uart_port *port, } #endif -/* - * Handle any change of modem status signal since we were last called. - */ -static void imx_mctrl_check(struct imx_port *sport) +static void imx_port_rts_active(struct imx_port *sport, unsigned long *ucr2) { - unsigned int status, changed; - - status = sport->port.ops->get_mctrl(&sport->port); - changed = status ^ sport->old_status; - - if (changed == 0) - return; - - sport->old_status = status; + *ucr2 &= ~UCR2_CTSC; + *ucr2 |= UCR2_CTS; - 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); + mctrl_gpio_set(sport->gpios, sport->port.mctrl | TIOCM_RTS); } -/* - * This is our per-port timeout handler, for checking the - * modem status signals. - */ -static void imx_timeout(unsigned long data) +static void imx_port_rts_inactive(struct imx_port *sport, unsigned long *ucr2) { - struct imx_port *sport = (struct imx_port *)data; - unsigned long flags; + *ucr2 &= ~(UCR2_CTSC | UCR2_CTS); - if (sport->port.state) { - spin_lock_irqsave(&sport->port.lock, flags); - imx_mctrl_check(sport); - spin_unlock_irqrestore(&sport->port.lock, flags); + mctrl_gpio_set(sport->gpios, sport->port.mctrl & ~TIOCM_RTS); +} - mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); - } +static void imx_port_rts_auto(struct imx_port *sport, unsigned long *ucr2) +{ + *ucr2 |= UCR2_CTSC; } /* @@ -376,9 +358,9 @@ static void imx_stop_tx(struct uart_port *port) readl(port->membase + USR2) & USR2_TXDC) { temp = readl(port->membase + UCR2); if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) - temp &= ~UCR2_CTS; + imx_port_rts_inactive(sport, &temp); else - temp |= UCR2_CTS; + imx_port_rts_active(sport, &temp); writel(temp, port->membase + UCR2); temp = readl(port->membase + UCR4); @@ -420,6 +402,8 @@ static void imx_enable_ms(struct uart_port *port) struct imx_port *sport = (struct imx_port *)port; mod_timer(&sport->timer, jiffies); + + mctrl_gpio_enable_ms(sport->gpios); } static void imx_dma_tx(struct imx_port *sport); @@ -579,14 +563,14 @@ static void imx_start_tx(struct uart_port *port) unsigned long temp; if (port->rs485.flags & SER_RS485_ENABLED) { - /* enable transmitter and shifter empty irq */ temp = readl(port->membase + UCR2); if (port->rs485.flags & SER_RS485_RTS_ON_SEND) - temp &= ~UCR2_CTS; + imx_port_rts_inactive(sport, &temp); else - temp |= UCR2_CTS; + imx_port_rts_active(sport, &temp); writel(temp, port->membase + UCR2); + /* enable transmitter and shifter empty irq */ temp = readl(port->membase + UCR4); temp |= UCR4_TCEN; writel(temp, port->membase + UCR4); @@ -801,23 +785,35 @@ static unsigned int imx_tx_empty(struct uart_port *port) /* * We have a modem side uart, so the meanings of RTS and CTS are inverted. */ -static unsigned int imx_get_mctrl(struct uart_port *port) +static unsigned int imx_get_hwmctrl(struct imx_port *sport) { - struct imx_port *sport = (struct imx_port *)port; - unsigned int tmp = TIOCM_DSR | TIOCM_CAR; + unsigned int tmp = TIOCM_DSR; + unsigned usr1 = readl(sport->port.membase + USR1); - if (readl(sport->port.membase + USR1) & USR1_RTSS) + if (usr1 & USR1_RTSS) tmp |= TIOCM_CTS; - if (readl(sport->port.membase + UCR2) & UCR2_CTS) - tmp |= TIOCM_RTS; + /* in DCE mode DCDIN is always 0 */ + if (!(usr1 & USR2_DCDIN)) + tmp |= TIOCM_CAR; - if (readl(sport->port.membase + uts_reg(sport)) & UTS_LOOP) - tmp |= TIOCM_LOOP; + /* 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; + unsigned int ret = imx_get_hwmctrl(sport); + + mctrl_gpio_get(sport->gpios, &ret); + + return ret; +} + static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct imx_port *sport = (struct imx_port *)port; @@ -831,10 +827,17 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl) writel(temp, sport->port.membase + UCR2); } + temp = readl(sport->port.membase + UCR3) & ~UCR3_DSR; + if (!(mctrl & TIOCM_DTR)) + temp |= UCR3_DSR; + writel(temp, sport->port.membase + UCR3); + temp = readl(sport->port.membase + uts_reg(sport)) & ~UTS_LOOP; if (mctrl & TIOCM_LOOP) temp |= UTS_LOOP; writel(temp, sport->port.membase + uts_reg(sport)); + + mctrl_gpio_set(sport->gpios, mctrl); } /* @@ -857,6 +860,51 @@ 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. + */ +static void imx_timeout(unsigned long data) +{ + struct imx_port *sport = (struct imx_port *)data; + unsigned long flags; + + if (sport->port.state) { + spin_lock_irqsave(&sport->port.lock, flags); + imx_mctrl_check(sport); + spin_unlock_irqrestore(&sport->port.lock, flags); + + mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); + } +} + #define RX_BUF_SIZE (PAGE_SIZE) static void imx_rx_dma_done(struct imx_port *sport) { @@ -1207,6 +1255,8 @@ static void imx_shutdown(struct uart_port *port) imx_uart_dma_exit(sport); } + mctrl_gpio_disable_ms(sport->gpios); + spin_lock_irqsave(&sport->port.lock, flags); temp = readl(sport->port.membase + UCR2); temp &= ~(UCR2_TXEN); @@ -1284,9 +1334,10 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, { struct imx_port *sport = (struct imx_port *)port; unsigned long flags; - unsigned int ucr2, old_ucr1, old_ucr2, baud, quot; + unsigned long ucr2, old_ucr1, old_ucr2; + unsigned int baud, quot; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; - unsigned int div, ufcr; + unsigned long div, ufcr; unsigned long num, denom; uint64_t tdiv64; @@ -1315,19 +1366,25 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, * it under manual control and keep transmitter * disabled. */ - if (!(port->rs485.flags & - SER_RS485_RTS_AFTER_SEND)) - ucr2 |= UCR2_CTS; + if (port->rs485.flags & + SER_RS485_RTS_AFTER_SEND) + imx_port_rts_inactive(sport, &ucr2); + else + imx_port_rts_active(sport, &ucr2); } else { - ucr2 |= UCR2_CTSC; + imx_port_rts_auto(sport, &ucr2); } } else { termios->c_cflag &= ~CRTSCTS; } - } else if (port->rs485.flags & SER_RS485_ENABLED) + } else if (port->rs485.flags & SER_RS485_ENABLED) { /* disable transmitter */ - if (!(port->rs485.flags & SER_RS485_RTS_AFTER_SEND)) - ucr2 |= UCR2_CTS; + if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) + imx_port_rts_inactive(sport, &ucr2); + else + imx_port_rts_active(sport, &ucr2); + } + if (termios->c_cflag & CSTOPB) ucr2 |= UCR2_STPB; @@ -1568,11 +1625,10 @@ static int imx_rs485_config(struct uart_port *port, /* disable transmitter */ temp = readl(sport->port.membase + UCR2); - temp &= ~UCR2_CTSC; if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND) - temp &= ~UCR2_CTS; + imx_port_rts_inactive(sport, &temp); else - temp |= UCR2_CTS; + imx_port_rts_active(sport, &temp); writel(temp, sport->port.membase + UCR2); } @@ -1857,11 +1913,10 @@ static int serial_imx_probe_dt(struct imx_port *sport, struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - const struct of_device_id *of_id = - of_match_device(imx_uart_dt_ids, &pdev->dev); int ret; - if (!np) + sport->devdata = of_device_get_match_data(&pdev->dev); + if (!sport->devdata) /* no device tree device */ return 1; @@ -1878,8 +1933,6 @@ static int serial_imx_probe_dt(struct imx_port *sport, if (of_get_property(np, "fsl,dte-mode", NULL)) sport->dte_mode = 1; - sport->devdata = of_id->data; - return 0; } #else @@ -1948,6 +2001,10 @@ static int serial_imx_probe(struct platform_device *pdev) sport->timer.function = imx_timeout; sport->timer.data = (unsigned long)sport; + sport->gpios = mctrl_gpio_init(&sport->port, 0); + if (IS_ERR(sport->gpios)) + return PTR_ERR(sport->gpios); + sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(sport->clk_ipg)) { ret = PTR_ERR(sport->clk_ipg); @@ -2109,7 +2166,8 @@ static int imx_serial_port_suspend(struct device *dev) uart_suspend_port(&imx_reg, &sport->port); - return 0; + /* Needed to enable clock in suspend_noirq */ + return clk_prepare(sport->clk_ipg); } static int imx_serial_port_resume(struct device *dev) @@ -2122,6 +2180,8 @@ static int imx_serial_port_resume(struct device *dev) uart_resume_port(&imx_reg, &sport->port); + clk_unprepare(sport->clk_ipg); + return 0; } diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index efbd87a76656..a119f11bf2f4 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -70,7 +70,7 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto out; } - rc = pci_request_regions(pdev, "jsm"); + rc = pci_request_regions(pdev, JSM_DRIVER_NAME); if (rc) { dev_err(&pdev->dev, "pci_request_region FAILED\n"); goto out_disable_device; @@ -328,7 +328,7 @@ static struct pci_device_id jsm_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, jsm_pci_tbl); static struct pci_driver jsm_driver = { - .name = "jsm", + .name = JSM_DRIVER_NAME, .id_table = jsm_pci_tbl, .probe = jsm_probe_one, .remove = jsm_remove_one, diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index 932b2accd06f..c6fdd6369534 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -714,7 +714,7 @@ static void neo_clear_break(struct jsm_channel *ch) /* * Parse the ISR register. */ -static inline void neo_parse_isr(struct jsm_board *brd, u32 port) +static void neo_parse_isr(struct jsm_board *brd, u32 port) { struct jsm_channel *ch; u8 isr; diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 524e86ab3cae..c5ddfe542451 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -529,7 +529,6 @@ void jsm_input(struct jsm_channel *ch) int data_len; unsigned long lock_flags; int len = 0; - int n = 0; int s = 0; int i = 0; @@ -569,8 +568,7 @@ void jsm_input(struct jsm_channel *ch) *If the device is not open, or CREAD is off, flush *input data and return immediately. */ - if (!tp || - !(tp->termios.c_cflag & CREAD) ) { + if (!tp || !C_CREAD(tp)) { jsm_dbg(READ, &ch->ch_bd->pci_dev, "input. dropping %d bytes on port %d...\n", @@ -598,16 +596,15 @@ void jsm_input(struct jsm_channel *ch) jsm_dbg(READ, &ch->ch_bd->pci_dev, "start 2\n"); len = tty_buffer_request_room(port, data_len); - n = len; /* - * n now contains the most amount of data we can copy, + * len now contains the most amount of data we can copy, * bounded either by the flip buffer size or the amount * of data the card actually has pending... */ - while (n) { + while (len) { s = ((head >= tail) ? head : RQUEUESIZE) - tail; - s = min(s, n); + s = min(s, len); if (s <= 0) break; @@ -638,7 +635,7 @@ void jsm_input(struct jsm_channel *ch) tty_insert_flip_string(port, ch->ch_rqueue + tail, s); } tail += s; - n -= s; + len -= s; /* Flip queue if needed */ tail &= rmask; } diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c index 8f7f83a14c93..68765f7c2645 100644 --- a/drivers/tty/serial/m32r_sio.c +++ b/drivers/tty/serial/m32r_sio.c @@ -47,59 +47,26 @@ #define BAUD_RATE 115200 #include <linux/serial_core.h> -#include "m32r_sio.h" #include "m32r_sio_reg.h" -/* - * Debugging. - */ -#if 0 -#define DEBUG_AUTOCONF(fmt...) printk(fmt) -#else -#define DEBUG_AUTOCONF(fmt...) do { } while (0) -#endif - -#if 0 -#define DEBUG_INTR(fmt...) printk(fmt) -#else -#define DEBUG_INTR(fmt...) do { } while (0) -#endif - #define PASS_LIMIT 256 -#define BASE_BAUD 115200 - /* Standard COM flags */ #define STD_COM_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST) -/* - * SERIAL_PORT_DFNS tells us about built-in ports that have no - * standard enumeration mechanism. Platforms that can find all - * serial ports via mechanisms like ACPI or PCI need not supply it. - */ +static const struct { + unsigned int port; + unsigned int irq; +} old_serial_port[] = { #if defined(CONFIG_PLAT_USRV) - -#define SERIAL_PORT_DFNS \ - /* UART CLK PORT IRQ FLAGS */ \ - { 0, BASE_BAUD, 0x3F8, PLD_IRQ_UART0, STD_COM_FLAGS }, /* ttyS0 */ \ - { 0, BASE_BAUD, 0x2F8, PLD_IRQ_UART1, STD_COM_FLAGS }, /* ttyS1 */ - -#else /* !CONFIG_PLAT_USRV */ - -#if defined(CONFIG_SERIAL_M32R_PLDSIO) -#define SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, ((unsigned long)PLD_ESIO0CR), PLD_IRQ_SIO0_RCV, \ - STD_COM_FLAGS }, /* ttyS0 */ + /* PORT IRQ FLAGS */ + { 0x3F8, PLD_IRQ_UART0 }, /* ttyS0 */ + { 0x2F8, PLD_IRQ_UART1 }, /* ttyS1 */ +#elif defined(CONFIG_SERIAL_M32R_PLDSIO) + { ((unsigned long)PLD_ESIO0CR), PLD_IRQ_SIO0_RCV }, /* ttyS0 */ #else -#define SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, M32R_SIO_OFFSET, M32R_IRQ_SIO0_R, \ - STD_COM_FLAGS }, /* ttyS0 */ + { M32R_SIO_OFFSET, M32R_IRQ_SIO0_R }, /* ttyS0 */ #endif - -#endif /* !CONFIG_PLAT_USRV */ - -static struct old_serial_port old_serial_port[] = { - SERIAL_PORT_DFNS }; #define UART_NR ARRAY_SIZE(old_serial_port) @@ -108,19 +75,7 @@ struct uart_sio_port { struct uart_port port; struct timer_list timer; /* "no irq" timer */ struct list_head list; /* ports on this IRQ */ - unsigned short rev; - unsigned char acr; unsigned char ier; - unsigned char lcr; - unsigned char mcr_mask; /* mask of user bits */ - unsigned char mcr_force; /* mask of forced bits */ - unsigned char lsr_break_flag; - - /* - * We provide a per-port pm hook. - */ - void (*pm)(struct uart_port *port, - unsigned int state, unsigned int old); }; struct irq_info { @@ -345,14 +300,8 @@ static void receive_chars(struct uart_sio_port *up, int *status) */ *status &= up->port.read_status_mask; - if (up->port.line == up->port.cons->index) { - /* Recover the break flag from console xmit */ - *status |= up->lsr_break_flag; - up->lsr_break_flag = 0; - } - if (*status & UART_LSR_BI) { - DEBUG_INTR("handling break...."); + pr_debug("handling break....\n"); flag = TTY_BREAK; } else if (*status & UART_LSR_PE) flag = TTY_PARITY; @@ -413,7 +362,7 @@ static void transmit_chars(struct uart_sio_port *up) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&up->port); - DEBUG_INTR("THRE..."); + pr_debug("THRE...\n"); if (uart_circ_empty(xmit)) m32r_sio_stop_tx(&up->port); @@ -425,7 +374,7 @@ static void transmit_chars(struct uart_sio_port *up) static inline void m32r_sio_handle_port(struct uart_sio_port *up, unsigned int status) { - DEBUG_INTR("status = %x...", status); + pr_debug("status = %x...\n", status); if (status & 0x04) receive_chars(up, &status); @@ -453,7 +402,7 @@ static irqreturn_t m32r_sio_interrupt(int irq, void *dev_id) struct list_head *l, *end = NULL; int pass_counter = 0; - DEBUG_INTR("m32r_sio_interrupt(%d)...", irq); + pr_debug("m32r_sio_interrupt(%d)...\n", irq); #ifdef CONFIG_SERIAL_M32R_PLDSIO // if (irq == PLD_IRQ_SIO0_SND) @@ -493,7 +442,7 @@ static irqreturn_t m32r_sio_interrupt(int irq, void *dev_id) spin_unlock(&i->lock); - DEBUG_INTR("end.\n"); + pr_debug("end.\n"); return IRQ_HANDLED; } @@ -782,20 +731,9 @@ static void m32r_sio_set_termios(struct uart_port *port, serial_out(up, UART_IER, up->ier); - up->lcr = cval; /* Save LCR */ spin_unlock_irqrestore(&up->port.lock, flags); } -static void m32r_sio_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - struct uart_sio_port *up = - container_of(port, struct uart_sio_port, port); - - if (up->pm) - up->pm(port, state, oldstate); -} - /* * Resource handling. This is complicated by the fact that resources * depend on the port type. Maybe we should be claiming the standard @@ -932,7 +870,6 @@ static struct uart_ops m32r_sio_pops = { .startup = m32r_sio_startup, .shutdown = m32r_sio_shutdown, .set_termios = m32r_sio_set_termios, - .pm = m32r_sio_pm, .release_port = m32r_sio_release_port, .request_port = m32r_sio_request_port, .config_port = m32r_sio_config_port, @@ -951,15 +888,14 @@ static void __init m32r_sio_init_ports(void) return; first = 0; - for (i = 0, up = m32r_sio_ports; i < ARRAY_SIZE(old_serial_port); - i++, up++) { + for (i = 0, up = m32r_sio_ports; i < UART_NR; i++, up++) { up->port.iobase = old_serial_port[i].port; up->port.irq = irq_canonicalize(old_serial_port[i].irq); - up->port.uartclk = old_serial_port[i].baud_base * 16; - up->port.flags = old_serial_port[i].flags; - up->port.membase = old_serial_port[i].iomem_base; - up->port.iotype = old_serial_port[i].io_type; - up->port.regshift = old_serial_port[i].iomem_reg_shift; + up->port.uartclk = BAUD_RATE * 16; + up->port.flags = STD_COM_FLAGS; + up->port.membase = 0; + up->port.iotype = 0; + up->port.regshift = 0; up->port.ops = &m32r_sio_pops; } } @@ -978,9 +914,6 @@ static void __init m32r_sio_register_ports(struct uart_driver *drv) init_timer(&up->timer); up->timer.function = m32r_sio_timeout; - up->mcr_mask = ~0; - up->mcr_force = 0; - uart_add_one_port(drv, &up->port); } } @@ -990,7 +923,7 @@ static void __init m32r_sio_register_ports(struct uart_driver *drv) /* * Wait for transmitter & holding register to empty */ -static inline void wait_for_xmitr(struct uart_sio_port *up) +static void wait_for_xmitr(struct uart_sio_port *up) { unsigned int status, tmout = 10000; @@ -1112,28 +1045,6 @@ static struct uart_driver m32r_sio_reg = { .cons = M32R_SIO_CONSOLE, }; -/** - * m32r_sio_suspend_port - suspend one serial port - * @line: serial line number - * - * Suspend one serial port. - */ -void m32r_sio_suspend_port(int line) -{ - uart_suspend_port(&m32r_sio_reg, &m32r_sio_ports[line].port); -} - -/** - * m32r_sio_resume_port - resume one serial port - * @line: serial line number - * - * Resume one serial port. - */ -void m32r_sio_resume_port(int line) -{ - uart_resume_port(&m32r_sio_reg, &m32r_sio_ports[line].port); -} - static int __init m32r_sio_init(void) { int ret, i; @@ -1163,8 +1074,5 @@ static void __exit m32r_sio_exit(void) module_init(m32r_sio_init); module_exit(m32r_sio_exit); -EXPORT_SYMBOL(m32r_sio_suspend_port); -EXPORT_SYMBOL(m32r_sio_resume_port); - MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic M32R SIO serial driver"); diff --git a/drivers/tty/serial/m32r_sio.h b/drivers/tty/serial/m32r_sio.h deleted file mode 100644 index 8129824496c6..000000000000 --- a/drivers/tty/serial/m32r_sio.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * m32r_sio.h - * - * Driver for M32R serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * Based on drivers/serial/8250.h. - * - * Copyright (C) 2001 Russell King. - * Copyright (C) 2004 Hirokazu Takata <takata at linux-m32r.org> - * - * 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. - */ - -#include <linux/pci.h> - -struct m32r_sio_probe { - struct module *owner; - int (*pci_init_one)(struct pci_dev *dev); - void (*pci_remove_one)(struct pci_dev *dev); - void (*pnp_init)(void); -}; - -int m32r_sio_register_probe(struct m32r_sio_probe *probe); -void m32r_sio_unregister_probe(struct m32r_sio_probe *probe); -void m32r_sio_get_irq_map(unsigned int *map); -void m32r_sio_suspend_port(int line); -void m32r_sio_resume_port(int line); - -struct old_serial_port { - unsigned int uart; - unsigned int baud_base; - unsigned int port; - unsigned int irq; - unsigned int flags; - unsigned char io_type; - unsigned char __iomem *iomem_base; - unsigned short iomem_reg_shift; -}; - -#define _INLINE_ inline - -#define PROBE_RSA (1 << 0) -#define PROBE_ANY (~0) - -#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index d45133056f51..3f98165b479c 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1174,7 +1174,7 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, #ifdef CONFIG_GPIOLIB /* Setup GPIO cotroller */ s->gpio.owner = THIS_MODULE; - s->gpio.dev = dev; + s->gpio.parent = dev; s->gpio.label = dev_name(dev); s->gpio.direction_input = max310x_gpio_direction_input; s->gpio.get = max310x_gpio_get; diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c index 3141aa20843d..a44290e9b5a8 100644 --- a/drivers/tty/serial/men_z135_uart.c +++ b/drivers/tty/serial/men_z135_uart.c @@ -158,7 +158,7 @@ static inline void men_z135_reg_set(struct men_z135_port *uart, * @addr: Register address * @val: value to clear */ -static inline void men_z135_reg_clr(struct men_z135_port *uart, +static void men_z135_reg_clr(struct men_z135_port *uart, u32 addr, u32 val) { struct uart_port *port = &uart->port; diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 0fc83c962d10..024445aa0521 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -57,6 +57,7 @@ #define AML_UART_RX_EMPTY BIT(20) #define AML_UART_TX_FULL BIT(21) #define AML_UART_TX_EMPTY BIT(22) +#define AML_UART_XMIT_BUSY BIT(25) #define AML_UART_ERR (AML_UART_PARITY_ERR | \ AML_UART_FRAME_ERR | \ AML_UART_TX_FIFO_WERR) @@ -77,6 +78,7 @@ /* AML_UART_REG5 bits */ #define AML_UART_BAUD_MASK 0x7fffff #define AML_UART_BAUD_USE BIT(23) +#define AML_UART_BAUD_XTAL BIT(24) #define AML_UART_PORT_NUM 6 #define AML_UART_DEV_NAME "ttyAML" @@ -100,7 +102,8 @@ static unsigned int meson_uart_tx_empty(struct uart_port *port) u32 val; val = readl(port->membase + AML_UART_STATUS); - return (val & AML_UART_TX_EMPTY) ? TIOCSER_TEMT : 0; + val &= (AML_UART_TX_EMPTY | AML_UART_XMIT_BUSY); + return (val == AML_UART_TX_EMPTY) ? TIOCSER_TEMT : 0; } static void meson_uart_stop_tx(struct uart_port *port) @@ -108,7 +111,7 @@ static void meson_uart_stop_tx(struct uart_port *port) u32 val; val = readl(port->membase + AML_UART_CONTROL); - val &= ~AML_UART_TX_EN; + val &= ~AML_UART_TX_INT_EN; writel(val, port->membase + AML_UART_CONTROL); } @@ -131,7 +134,7 @@ static void meson_uart_shutdown(struct uart_port *port) spin_lock_irqsave(&port->lock, flags); val = readl(port->membase + AML_UART_CONTROL); - val &= ~(AML_UART_RX_EN | AML_UART_TX_EN); + val &= ~AML_UART_RX_EN; val &= ~(AML_UART_RX_INT_EN | AML_UART_TX_INT_EN); writel(val, port->membase + AML_UART_CONTROL); @@ -142,6 +145,7 @@ static void meson_uart_start_tx(struct uart_port *port) { struct circ_buf *xmit = &port->state->xmit; unsigned int ch; + u32 val; if (uart_tx_stopped(port)) { meson_uart_stop_tx(port); @@ -165,6 +169,12 @@ static void meson_uart_start_tx(struct uart_port *port) port->icount.tx++; } + if (!uart_circ_empty(xmit)) { + val = readl(port->membase + AML_UART_CONTROL); + val |= AML_UART_TX_INT_EN; + writel(val, port->membase + AML_UART_CONTROL); + } + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); } @@ -228,8 +238,10 @@ static irqreturn_t meson_uart_interrupt(int irq, void *dev_id) if (!(readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY)) meson_receive_chars(port); - if (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_FULL)) - meson_uart_start_tx(port); + if (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_FULL)) { + if (readl(port->membase + AML_UART_CONTROL) & AML_UART_TX_INT_EN) + meson_uart_start_tx(port); + } spin_unlock(&port->lock); @@ -241,10 +253,9 @@ static const char *meson_uart_type(struct uart_port *port) return (port->type == PORT_MESON) ? "meson_uart" : NULL; } -static int meson_uart_startup(struct uart_port *port) +static void meson_uart_reset(struct uart_port *port) { u32 val; - int ret = 0; val = readl(port->membase + AML_UART_CONTROL); val |= (AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR); @@ -252,6 +263,18 @@ static int meson_uart_startup(struct uart_port *port) val &= ~(AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR); writel(val, port->membase + AML_UART_CONTROL); +} + +static int meson_uart_startup(struct uart_port *port) +{ + u32 val; + int ret = 0; + + val = readl(port->membase + AML_UART_CONTROL); + val |= AML_UART_CLR_ERR; + writel(val, port->membase + AML_UART_CONTROL); + val &= ~AML_UART_CLR_ERR; + writel(val, port->membase + AML_UART_CONTROL); val |= (AML_UART_RX_EN | AML_UART_TX_EN); writel(val, port->membase + AML_UART_CONTROL); @@ -272,12 +295,17 @@ static void meson_uart_change_speed(struct uart_port *port, unsigned long baud) { u32 val; - while (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_EMPTY)) + while (!meson_uart_tx_empty(port)) cpu_relax(); val = readl(port->membase + AML_UART_REG5); val &= ~AML_UART_BAUD_MASK; - val = ((port->uartclk * 10 / (baud * 4) + 5) / 10) - 1; + if (port->uartclk == 24000000) { + val = ((port->uartclk / 3) / baud) - 1; + val |= AML_UART_BAUD_XTAL; + } else { + val = ((port->uartclk * 10 / (baud * 4) + 5) / 10) - 1; + } val |= AML_UART_BAUD_USE; writel(val, port->membase + AML_UART_REG5); } @@ -367,9 +395,26 @@ static int meson_uart_verify_port(struct uart_port *port, return ret; } +static int meson_uart_res_size(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(port->dev, "cannot obtain I/O memory region"); + return -ENODEV; + } + + return resource_size(res); +} + static void meson_uart_release_port(struct uart_port *port) { + int size = meson_uart_res_size(port); + if (port->flags & UPF_IOREMAP) { + devm_release_mem_region(port->dev, port->mapbase, size); devm_iounmap(port->dev, port->membase); port->membase = NULL; } @@ -377,16 +422,10 @@ static void meson_uart_release_port(struct uart_port *port) static int meson_uart_request_port(struct uart_port *port) { - struct platform_device *pdev = to_platform_device(port->dev); - struct resource *res; - int size; + int size = meson_uart_res_size(port); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "cannot obtain I/O memory region"); - return -ENODEV; - } - size = resource_size(res); + if (size < 0) + return size; if (!devm_request_mem_region(port->dev, port->mapbase, size, dev_name(port->dev))) { @@ -448,6 +487,7 @@ static void meson_serial_console_write(struct console *co, const char *s, struct uart_port *port; unsigned long flags; int locked; + u32 val, tmp; port = meson_ports[co->index]; if (!port) @@ -463,7 +503,13 @@ static void meson_serial_console_write(struct console *co, const char *s, locked = 1; } + val = readl(port->membase + AML_UART_CONTROL); + val |= AML_UART_TX_EN; + tmp = val & ~(AML_UART_TX_INT_EN | AML_UART_RX_INT_EN); + writel(tmp, port->membase + AML_UART_CONTROL); + uart_console_write(port, s, count, meson_console_putchar); + writel(val, port->membase + AML_UART_CONTROL); if (locked) spin_unlock(&port->lock); @@ -570,6 +616,12 @@ static int meson_uart_probe(struct platform_device *pdev) meson_ports[pdev->id] = port; platform_set_drvdata(pdev, port); + /* reset port before registering (and possibly registering console) */ + if (meson_uart_request_port(port) >= 0) { + meson_uart_reset(port); + meson_uart_release_port(port); + } + ret = uart_add_one_port(&meson_uart_driver, port); if (ret) meson_ports[pdev->id] = NULL; diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 8c3e51314470..3970d6a9aaca 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -346,7 +346,7 @@ static irqreturn_t mpc52xx_psc_handle_irq(struct uart_port *port) return mpc5xxx_uart_process_int(port); } -static struct psc_ops mpc52xx_psc_ops = { +static const struct psc_ops mpc52xx_psc_ops = { .fifo_init = mpc52xx_psc_fifo_init, .raw_rx_rdy = mpc52xx_psc_raw_rx_rdy, .raw_tx_rdy = mpc52xx_psc_raw_tx_rdy, @@ -376,7 +376,7 @@ static struct psc_ops mpc52xx_psc_ops = { .get_mr1 = mpc52xx_psc_get_mr1, }; -static struct psc_ops mpc5200b_psc_ops = { +static const struct psc_ops mpc5200b_psc_ops = { .fifo_init = mpc52xx_psc_fifo_init, .raw_rx_rdy = mpc52xx_psc_raw_rx_rdy, .raw_tx_rdy = mpc52xx_psc_raw_tx_rdy, @@ -969,7 +969,7 @@ static u8 mpc5125_psc_get_mr1(struct uart_port *port) return in_8(&PSC_5125(port)->mr1); } -static struct psc_ops mpc5125_psc_ops = { +static const struct psc_ops mpc5125_psc_ops = { .fifo_init = mpc5125_psc_fifo_init, .raw_rx_rdy = mpc5125_psc_raw_rx_rdy, .raw_tx_rdy = mpc5125_psc_raw_tx_rdy, @@ -1004,7 +1004,7 @@ static struct psc_ops mpc5125_psc_ops = { .get_mr1 = mpc5125_psc_get_mr1, }; -static struct psc_ops mpc512x_psc_ops = { +static const struct psc_ops mpc512x_psc_ops = { .fifo_init = mpc512x_psc_fifo_init, .raw_rx_rdy = mpc512x_psc_raw_rx_rdy, .raw_tx_rdy = mpc512x_psc_raw_tx_rdy, diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c index cadfd1cfae2b..4a3021bcc859 100644 --- a/drivers/tty/serial/mpsc.c +++ b/drivers/tty/serial/mpsc.c @@ -137,8 +137,6 @@ struct mpsc_port_info { /* Internal driver state for this ctlr */ u8 ready; u8 rcv_data; - tcflag_t c_iflag; /* save termios->c_iflag */ - tcflag_t c_cflag; /* save termios->c_cflag */ /* Info passed in from platform */ u8 mirror_regs; /* Need to mirror regs? */ @@ -1407,9 +1405,6 @@ static void mpsc_set_termios(struct uart_port *port, struct ktermios *termios, ulong flags; u32 chr_bits, stop_bits, par; - pi->c_iflag = termios->c_iflag; - pi->c_cflag = termios->c_cflag; - switch (termios->c_cflag & CSIZE) { case CS5: chr_bits = MPSC_MPCR_CL_5; @@ -1870,12 +1865,12 @@ static int mpsc_shared_map_regs(struct platform_device *pd) static void mpsc_shared_unmap_regs(void) { - if (!mpsc_shared_regs.mpsc_routing_base) { + if (mpsc_shared_regs.mpsc_routing_base) { iounmap(mpsc_shared_regs.mpsc_routing_base); release_mem_region(mpsc_shared_regs.mpsc_routing_base_p, MPSC_ROUTING_REG_BLOCK_SIZE); } - if (!mpsc_shared_regs.sdma_intr_base) { + if (mpsc_shared_regs.sdma_intr_base) { iounmap(mpsc_shared_regs.sdma_intr_base); release_mem_region(mpsc_shared_regs.sdma_intr_base_p, MPSC_SDMA_INTR_REG_BLOCK_SIZE); @@ -1891,44 +1886,39 @@ static void mpsc_shared_unmap_regs(void) static int mpsc_shared_drv_probe(struct platform_device *dev) { struct mpsc_shared_pdata *pdata; - int rc = -ENODEV; - - if (dev->id == 0) { - rc = mpsc_shared_map_regs(dev); - if (!rc) { - pdata = (struct mpsc_shared_pdata *) - dev_get_platdata(&dev->dev); - - mpsc_shared_regs.MPSC_MRR_m = pdata->mrr_val; - mpsc_shared_regs.MPSC_RCRR_m= pdata->rcrr_val; - mpsc_shared_regs.MPSC_TCRR_m= pdata->tcrr_val; - mpsc_shared_regs.SDMA_INTR_CAUSE_m = - pdata->intr_cause_val; - mpsc_shared_regs.SDMA_INTR_MASK_m = - pdata->intr_mask_val; - - rc = 0; - } - } + int rc; - return rc; + if (dev->id != 0) + return -ENODEV; + + rc = mpsc_shared_map_regs(dev); + if (rc) + return rc; + + pdata = dev_get_platdata(&dev->dev); + + mpsc_shared_regs.MPSC_MRR_m = pdata->mrr_val; + mpsc_shared_regs.MPSC_RCRR_m= pdata->rcrr_val; + mpsc_shared_regs.MPSC_TCRR_m= pdata->tcrr_val; + mpsc_shared_regs.SDMA_INTR_CAUSE_m = pdata->intr_cause_val; + mpsc_shared_regs.SDMA_INTR_MASK_m = pdata->intr_mask_val; + + return 0; } static int mpsc_shared_drv_remove(struct platform_device *dev) { - int rc = -ENODEV; + if (dev->id != 0) + return -ENODEV; - if (dev->id == 0) { - mpsc_shared_unmap_regs(); - mpsc_shared_regs.MPSC_MRR_m = 0; - mpsc_shared_regs.MPSC_RCRR_m = 0; - mpsc_shared_regs.MPSC_TCRR_m = 0; - mpsc_shared_regs.SDMA_INTR_CAUSE_m = 0; - mpsc_shared_regs.SDMA_INTR_MASK_m = 0; - rc = 0; - } + mpsc_shared_unmap_regs(); + mpsc_shared_regs.MPSC_MRR_m = 0; + mpsc_shared_regs.MPSC_RCRR_m = 0; + mpsc_shared_regs.MPSC_TCRR_m = 0; + mpsc_shared_regs.SDMA_INTR_CAUSE_m = 0; + mpsc_shared_regs.SDMA_INTR_MASK_m = 0; - return rc; + return 0; } static struct platform_driver mpsc_shared_driver = { @@ -1979,10 +1969,6 @@ static int mpsc_drv_map_regs(struct mpsc_port_info *pi, pi->sdma_base_p = r->start; } else { mpsc_resource_err("SDMA base"); - if (pi->mpsc_base) { - iounmap(pi->mpsc_base); - pi->mpsc_base = NULL; - } goto err; } @@ -1993,33 +1979,33 @@ static int mpsc_drv_map_regs(struct mpsc_port_info *pi, pi->brg_base_p = r->start; } else { mpsc_resource_err("BRG base"); - if (pi->mpsc_base) { - iounmap(pi->mpsc_base); - pi->mpsc_base = NULL; - } - if (pi->sdma_base) { - iounmap(pi->sdma_base); - pi->sdma_base = NULL; - } goto err; } return 0; err: + if (pi->sdma_base) { + iounmap(pi->sdma_base); + pi->sdma_base = NULL; + } + if (pi->mpsc_base) { + iounmap(pi->mpsc_base); + pi->mpsc_base = NULL; + } return -ENOMEM; } static void mpsc_drv_unmap_regs(struct mpsc_port_info *pi) { - if (!pi->mpsc_base) { + if (pi->mpsc_base) { iounmap(pi->mpsc_base); release_mem_region(pi->mpsc_base_p, MPSC_REG_BLOCK_SIZE); } - if (!pi->sdma_base) { + if (pi->sdma_base) { iounmap(pi->sdma_base); release_mem_region(pi->sdma_base_p, MPSC_SDMA_REG_BLOCK_SIZE); } - if (!pi->brg_base) { + if (pi->brg_base) { iounmap(pi->brg_base); release_mem_region(pi->brg_base_p, MPSC_BRG_REG_BLOCK_SIZE); } @@ -2073,36 +2059,37 @@ static void mpsc_drv_get_platform_data(struct mpsc_port_info *pi, static int mpsc_drv_probe(struct platform_device *dev) { - struct mpsc_port_info *pi; - int rc = -ENODEV; - - pr_debug("mpsc_drv_probe: Adding MPSC %d\n", dev->id); - - if (dev->id < MPSC_NUM_CTLRS) { - pi = &mpsc_ports[dev->id]; - - rc = mpsc_drv_map_regs(pi, dev); - if (!rc) { - mpsc_drv_get_platform_data(pi, dev, dev->id); - pi->port.dev = &dev->dev; - - rc = mpsc_make_ready(pi); - if (!rc) { - spin_lock_init(&pi->tx_lock); - rc = uart_add_one_port(&mpsc_reg, &pi->port); - if (!rc) { - rc = 0; - } else { - mpsc_release_port((struct uart_port *) - pi); - mpsc_drv_unmap_regs(pi); - } - } else { - mpsc_drv_unmap_regs(pi); - } - } - } + struct mpsc_port_info *pi; + int rc; + + dev_dbg(&dev->dev, "mpsc_drv_probe: Adding MPSC %d\n", dev->id); + + if (dev->id >= MPSC_NUM_CTLRS) + return -ENODEV; + + pi = &mpsc_ports[dev->id]; + + rc = mpsc_drv_map_regs(pi, dev); + if (rc) + return rc; + mpsc_drv_get_platform_data(pi, dev, dev->id); + pi->port.dev = &dev->dev; + + rc = mpsc_make_ready(pi); + if (rc) + goto err_unmap; + + spin_lock_init(&pi->tx_lock); + rc = uart_add_one_port(&mpsc_reg, &pi->port); + if (rc) + goto err_relport; + + return 0; +err_relport: + mpsc_release_port(&pi->port); +err_unmap: + mpsc_drv_unmap_regs(pi); return rc; } @@ -2124,19 +2111,22 @@ static int __init mpsc_drv_init(void) memset(&mpsc_shared_regs, 0, sizeof(mpsc_shared_regs)); rc = uart_register_driver(&mpsc_reg); - if (!rc) { - rc = platform_driver_register(&mpsc_shared_driver); - if (!rc) { - rc = platform_driver_register(&mpsc_driver); - if (rc) { - platform_driver_unregister(&mpsc_shared_driver); - uart_unregister_driver(&mpsc_reg); - } - } else { - uart_unregister_driver(&mpsc_reg); - } - } + if (rc) + return rc; + + rc = platform_driver_register(&mpsc_shared_driver); + if (rc) + goto err_unreg_uart; + rc = platform_driver_register(&mpsc_driver); + if (rc) + goto err_unreg_plat; + + return 0; +err_unreg_plat: + platform_driver_unregister(&mpsc_shared_driver); +err_unreg_uart: + uart_unregister_driver(&mpsc_reg); return rc; } device_initcall(mpsc_drv_init); diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index dcde955475dc..96d3ce8dc2dc 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1478,7 +1478,6 @@ msm_serial_early_console_setup(struct earlycon_device *device, const char *opt) device->con->write = msm_serial_early_write; return 0; } -EARLYCON_DECLARE(msm_serial, msm_serial_early_console_setup); OF_EARLYCON_DECLARE(msm_serial, "qcom,msm-uart", msm_serial_early_console_setup); @@ -1500,7 +1499,6 @@ msm_serial_early_console_setup_dm(struct earlycon_device *device, device->con->write = msm_serial_early_write_dm; return 0; } -EARLYCON_DECLARE(msm_serial_dm, msm_serial_early_console_setup_dm); OF_EARLYCON_DECLARE(msm_serial_dm, "qcom,msm-uartdm", msm_serial_early_console_setup_dm); diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c new file mode 100644 index 000000000000..0ff27818bb87 --- /dev/null +++ b/drivers/tty/serial/mvebu-uart.c @@ -0,0 +1,650 @@ +/* +* *************************************************************************** +* Copyright (C) 2015 Marvell International Ltd. +* *************************************************************************** +* 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 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, see <http://www.gnu.org/licenses/>. +* *************************************************************************** +*/ + +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> + +/* Register Map */ +#define UART_RBR 0x00 +#define RBR_BRK_DET BIT(15) +#define RBR_FRM_ERR_DET BIT(14) +#define RBR_PAR_ERR_DET BIT(13) +#define RBR_OVR_ERR_DET BIT(12) + +#define UART_TSH 0x04 + +#define UART_CTRL 0x08 +#define CTRL_SOFT_RST BIT(31) +#define CTRL_TXFIFO_RST BIT(15) +#define CTRL_RXFIFO_RST BIT(14) +#define CTRL_ST_MIRR_EN BIT(13) +#define CTRL_LPBK_EN BIT(12) +#define CTRL_SND_BRK_SEQ BIT(11) +#define CTRL_PAR_EN BIT(10) +#define CTRL_TWO_STOP BIT(9) +#define CTRL_TX_HFL_INT BIT(8) +#define CTRL_RX_HFL_INT BIT(7) +#define CTRL_TX_EMP_INT BIT(6) +#define CTRL_TX_RDY_INT BIT(5) +#define CTRL_RX_RDY_INT BIT(4) +#define CTRL_BRK_DET_INT BIT(3) +#define CTRL_FRM_ERR_INT BIT(2) +#define CTRL_PAR_ERR_INT BIT(1) +#define CTRL_OVR_ERR_INT BIT(0) +#define CTRL_RX_INT (CTRL_RX_RDY_INT | CTRL_BRK_DET_INT |\ + CTRL_FRM_ERR_INT | CTRL_PAR_ERR_INT | CTRL_OVR_ERR_INT) + +#define UART_STAT 0x0c +#define STAT_TX_FIFO_EMP BIT(13) +#define STAT_RX_FIFO_EMP BIT(12) +#define STAT_TX_FIFO_FUL BIT(11) +#define STAT_TX_FIFO_HFL BIT(10) +#define STAT_RX_TOGL BIT(9) +#define STAT_RX_FIFO_FUL BIT(8) +#define STAT_RX_FIFO_HFL BIT(7) +#define STAT_TX_EMP BIT(6) +#define STAT_TX_RDY BIT(5) +#define STAT_RX_RDY BIT(4) +#define STAT_BRK_DET BIT(3) +#define STAT_FRM_ERR BIT(2) +#define STAT_PAR_ERR BIT(1) +#define STAT_OVR_ERR BIT(0) +#define STAT_BRK_ERR (STAT_BRK_DET | STAT_FRM_ERR | STAT_FRM_ERR\ + | STAT_PAR_ERR | STAT_OVR_ERR) + +#define UART_BRDV 0x10 + +#define MVEBU_NR_UARTS 1 + +#define MVEBU_UART_TYPE "mvebu-uart" + +static struct uart_port mvebu_uart_ports[MVEBU_NR_UARTS]; + +struct mvebu_uart_data { + struct uart_port *port; + struct clk *clk; +}; + +/* Core UART Driver Operations */ +static unsigned int mvebu_uart_tx_empty(struct uart_port *port) +{ + unsigned long flags; + unsigned int st; + + spin_lock_irqsave(&port->lock, flags); + st = readl(port->membase + UART_STAT); + spin_unlock_irqrestore(&port->lock, flags); + + return (st & STAT_TX_FIFO_EMP) ? TIOCSER_TEMT : 0; +} + +static unsigned int mvebu_uart_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void mvebu_uart_set_mctrl(struct uart_port *port, + unsigned int mctrl) +{ +/* + * Even if we do not support configuring the modem control lines, this + * function must be proided to the serial core + */ +} + +static void mvebu_uart_stop_tx(struct uart_port *port) +{ + unsigned int ctl = readl(port->membase + UART_CTRL); + + ctl &= ~CTRL_TX_RDY_INT; + writel(ctl, port->membase + UART_CTRL); +} + +static void mvebu_uart_start_tx(struct uart_port *port) +{ + unsigned int ctl = readl(port->membase + UART_CTRL); + + ctl |= CTRL_TX_RDY_INT; + writel(ctl, port->membase + UART_CTRL); +} + +static void mvebu_uart_stop_rx(struct uart_port *port) +{ + unsigned int ctl = readl(port->membase + UART_CTRL); + + ctl &= ~CTRL_RX_INT; + writel(ctl, port->membase + UART_CTRL); +} + +static void mvebu_uart_break_ctl(struct uart_port *port, int brk) +{ + unsigned int ctl; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + ctl = readl(port->membase + UART_CTRL); + if (brk == -1) + ctl |= CTRL_SND_BRK_SEQ; + else + ctl &= ~CTRL_SND_BRK_SEQ; + writel(ctl, port->membase + UART_CTRL); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void mvebu_uart_rx_chars(struct uart_port *port, unsigned int status) +{ + struct tty_port *tport = &port->state->port; + unsigned char ch = 0; + char flag = 0; + + do { + if (status & STAT_RX_RDY) { + ch = readl(port->membase + UART_RBR); + ch &= 0xff; + flag = TTY_NORMAL; + port->icount.rx++; + + if (status & STAT_PAR_ERR) + port->icount.parity++; + } + + if (status & STAT_BRK_DET) { + port->icount.brk++; + status &= ~(STAT_FRM_ERR | STAT_PAR_ERR); + if (uart_handle_break(port)) + goto ignore_char; + } + + if (status & STAT_OVR_ERR) + port->icount.overrun++; + + if (status & STAT_FRM_ERR) + port->icount.frame++; + + if (uart_handle_sysrq_char(port, ch)) + goto ignore_char; + + if (status & port->ignore_status_mask & STAT_PAR_ERR) + status &= ~STAT_RX_RDY; + + status &= port->read_status_mask; + + if (status & STAT_PAR_ERR) + flag = TTY_PARITY; + + status &= ~port->ignore_status_mask; + + if (status & STAT_RX_RDY) + tty_insert_flip_char(tport, ch, flag); + + if (status & STAT_BRK_DET) + tty_insert_flip_char(tport, 0, TTY_BREAK); + + if (status & STAT_FRM_ERR) + tty_insert_flip_char(tport, 0, TTY_FRAME); + + if (status & STAT_OVR_ERR) + tty_insert_flip_char(tport, 0, TTY_OVERRUN); + +ignore_char: + status = readl(port->membase + UART_STAT); + } while (status & (STAT_RX_RDY | STAT_BRK_DET)); + + tty_flip_buffer_push(tport); +} + +static void mvebu_uart_tx_chars(struct uart_port *port, unsigned int status) +{ + struct circ_buf *xmit = &port->state->xmit; + unsigned int count; + unsigned int st; + + if (port->x_char) { + writel(port->x_char, port->membase + UART_TSH); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + mvebu_uart_stop_tx(port); + return; + } + + for (count = 0; count < port->fifosize; count++) { + writel(xmit->buf[xmit->tail], port->membase + UART_TSH); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + + if (uart_circ_empty(xmit)) + break; + + st = readl(port->membase + UART_STAT); + if (st & STAT_TX_FIFO_FUL) + break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + mvebu_uart_stop_tx(port); +} + +static irqreturn_t mvebu_uart_isr(int irq, void *dev_id) +{ + struct uart_port *port = (struct uart_port *)dev_id; + unsigned int st = readl(port->membase + UART_STAT); + + if (st & (STAT_RX_RDY | STAT_OVR_ERR | STAT_FRM_ERR | STAT_BRK_DET)) + mvebu_uart_rx_chars(port, st); + + if (st & STAT_TX_RDY) + mvebu_uart_tx_chars(port, st); + + return IRQ_HANDLED; +} + +static int mvebu_uart_startup(struct uart_port *port) +{ + int ret; + + writel(CTRL_TXFIFO_RST | CTRL_RXFIFO_RST, + port->membase + UART_CTRL); + udelay(1); + writel(CTRL_RX_INT, port->membase + UART_CTRL); + + ret = request_irq(port->irq, mvebu_uart_isr, port->irqflags, "serial", + port); + if (ret) { + dev_err(port->dev, "failed to request irq\n"); + return ret; + } + + return 0; +} + +static void mvebu_uart_shutdown(struct uart_port *port) +{ + writel(0, port->membase + UART_CTRL); +} + +static void mvebu_uart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud; + + spin_lock_irqsave(&port->lock, flags); + + port->read_status_mask = STAT_RX_RDY | STAT_OVR_ERR | + STAT_TX_RDY | STAT_TX_FIFO_FUL; + + if (termios->c_iflag & INPCK) + port->read_status_mask |= STAT_FRM_ERR | STAT_PAR_ERR; + + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= + STAT_FRM_ERR | STAT_PAR_ERR | STAT_OVR_ERR; + + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= STAT_RX_RDY | STAT_BRK_ERR; + + if (old) + tty_termios_copy_hw(termios, old); + + baud = uart_get_baud_rate(port, termios, old, 0, 460800); + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *mvebu_uart_type(struct uart_port *port) +{ + return MVEBU_UART_TYPE; +} + +static void mvebu_uart_release_port(struct uart_port *port) +{ + /* Nothing to do here */ +} + +static int mvebu_uart_request_port(struct uart_port *port) +{ + return 0; +} + +#ifdef CONFIG_CONSOLE_POLL +static int mvebu_uart_get_poll_char(struct uart_port *port) +{ + unsigned int st = readl(port->membase + UART_STAT); + + if (!(st & STAT_RX_RDY)) + return NO_POLL_CHAR; + + return readl(port->membase + UART_RBR); +} + +static void mvebu_uart_put_poll_char(struct uart_port *port, unsigned char c) +{ + unsigned int st; + + for (;;) { + st = readl(port->membase + UART_STAT); + + if (!(st & STAT_TX_FIFO_FUL)) + break; + + udelay(1); + } + + writel(c, port->membase + UART_TSH); +} +#endif + +static const struct uart_ops mvebu_uart_ops = { + .tx_empty = mvebu_uart_tx_empty, + .set_mctrl = mvebu_uart_set_mctrl, + .get_mctrl = mvebu_uart_get_mctrl, + .stop_tx = mvebu_uart_stop_tx, + .start_tx = mvebu_uart_start_tx, + .stop_rx = mvebu_uart_stop_rx, + .break_ctl = mvebu_uart_break_ctl, + .startup = mvebu_uart_startup, + .shutdown = mvebu_uart_shutdown, + .set_termios = mvebu_uart_set_termios, + .type = mvebu_uart_type, + .release_port = mvebu_uart_release_port, + .request_port = mvebu_uart_request_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = mvebu_uart_get_poll_char, + .poll_put_char = mvebu_uart_put_poll_char, +#endif +}; + +/* Console Driver Operations */ + +#ifdef CONFIG_SERIAL_MVEBU_CONSOLE +/* Early Console */ +static void mvebu_uart_putc(struct uart_port *port, int c) +{ + unsigned int st; + + for (;;) { + st = readl(port->membase + UART_STAT); + if (!(st & STAT_TX_FIFO_FUL)) + break; + } + + writel(c, port->membase + UART_TSH); + + for (;;) { + st = readl(port->membase + UART_STAT); + if (st & STAT_TX_FIFO_EMP) + break; + } +} + +static void mvebu_uart_putc_early_write(struct console *con, + const char *s, + unsigned n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, mvebu_uart_putc); +} + +static int __init +mvebu_uart_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = mvebu_uart_putc_early_write; + + return 0; +} + +EARLYCON_DECLARE(ar3700_uart, mvebu_uart_early_console_setup); +OF_EARLYCON_DECLARE(ar3700_uart, "marvell,armada-3700-uart", + mvebu_uart_early_console_setup); + +static void wait_for_xmitr(struct uart_port *port) +{ + u32 val; + + readl_poll_timeout_atomic(port->membase + UART_STAT, val, + (val & STAT_TX_EMP), 1, 10000); +} + +static void mvebu_uart_console_putchar(struct uart_port *port, int ch) +{ + wait_for_xmitr(port); + writel(ch, port->membase + UART_TSH); +} + +static void mvebu_uart_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = &mvebu_uart_ports[co->index]; + unsigned long flags; + unsigned int ier; + int locked = 1; + + if (oops_in_progress) + locked = spin_trylock_irqsave(&port->lock, flags); + else + spin_lock_irqsave(&port->lock, flags); + + ier = readl(port->membase + UART_CTRL) & + (CTRL_RX_INT | CTRL_TX_RDY_INT); + writel(0, port->membase + UART_CTRL); + + uart_console_write(port, s, count, mvebu_uart_console_putchar); + + wait_for_xmitr(port); + + if (ier) + writel(ier, port->membase + UART_CTRL); + + if (locked) + spin_unlock_irqrestore(&port->lock, flags); +} + +static int mvebu_uart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= MVEBU_NR_UARTS) + return -EINVAL; + + port = &mvebu_uart_ports[co->index]; + + if (!port->mapbase || !port->membase) { + pr_debug("console on ttyMV%i not present\n", co->index); + return -ENODEV; + } + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver mvebu_uart_driver; + +static struct console mvebu_uart_console = { + .name = "ttyMV", + .write = mvebu_uart_console_write, + .device = uart_console_device, + .setup = mvebu_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &mvebu_uart_driver, +}; + +static int __init mvebu_uart_console_init(void) +{ + register_console(&mvebu_uart_console); + return 0; +} + +console_initcall(mvebu_uart_console_init); + + +#endif /* CONFIG_SERIAL_MVEBU_CONSOLE */ + +static struct uart_driver mvebu_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "mvebu_serial", + .dev_name = "ttyMV", + .nr = MVEBU_NR_UARTS, +#ifdef CONFIG_SERIAL_MVEBU_CONSOLE + .cons = &mvebu_uart_console, +#endif +}; + +static int mvebu_uart_probe(struct platform_device *pdev) +{ + struct resource *reg = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + struct uart_port *port; + struct mvebu_uart_data *data; + int ret; + + if (!reg || !irq) { + dev_err(&pdev->dev, "no registers/irq defined\n"); + return -EINVAL; + } + + port = &mvebu_uart_ports[0]; + + spin_lock_init(&port->lock); + + port->dev = &pdev->dev; + port->type = PORT_MVEBU; + port->ops = &mvebu_uart_ops; + port->regshift = 0; + + port->fifosize = 32; + port->iotype = UPIO_MEM32; + port->flags = UPF_FIXED_PORT; + port->line = 0; /* single port: force line number to 0 */ + + port->irq = irq->start; + port->irqflags = 0; + port->mapbase = reg->start; + + port->membase = devm_ioremap_resource(&pdev->dev, reg); + if (IS_ERR(port->membase)) + return -PTR_ERR(port->membase); + + data = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_uart_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->port = port; + + port->private_data = data; + platform_set_drvdata(pdev, data); + + ret = uart_add_one_port(&mvebu_uart_driver, port); + if (ret) + return ret; + 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), + }, +}; + +static int __init mvebu_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&mvebu_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&mvebu_uart_platform_driver); + if (ret) + uart_unregister_driver(&mvebu_uart_driver); + + 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 <dingwei@marvell.com>"); +MODULE_DESCRIPTION("Marvell Armada-3700 Serial Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/nwpserial.c b/drivers/tty/serial/nwpserial.c deleted file mode 100644 index 5da7622e88c3..000000000000 --- a/drivers/tty/serial/nwpserial.c +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Serial Port driver for a NWP uart device - * - * Copyright (C) 2008 IBM Corp., Benjamin Krill <ben@codiert.org> - * - * 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. - * - */ -#include <linux/init.h> -#include <linux/export.h> -#include <linux/console.h> -#include <linux/serial.h> -#include <linux/serial_reg.h> -#include <linux/serial_core.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/irqreturn.h> -#include <linux/mutex.h> -#include <linux/of_platform.h> -#include <linux/of_device.h> -#include <linux/nwpserial.h> -#include <linux/delay.h> -#include <asm/prom.h> -#include <asm/dcr.h> - -#define NWPSERIAL_NR 2 - -#define NWPSERIAL_STATUS_RXVALID 0x1 -#define NWPSERIAL_STATUS_TXFULL 0x2 - -struct nwpserial_port { - struct uart_port port; - dcr_host_t dcr_host; - unsigned int ier; - unsigned int mcr; -}; - -static DEFINE_MUTEX(nwpserial_mutex); -static struct nwpserial_port nwpserial_ports[NWPSERIAL_NR]; - -static void wait_for_bits(struct nwpserial_port *up, int bits) -{ - unsigned int status, tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - do { - status = dcr_read(up->dcr_host, UART_LSR); - - if (--tmout == 0) - break; - udelay(1); - } while ((status & bits) != bits); -} - -#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE -static void nwpserial_console_putchar(struct uart_port *port, int c) -{ - struct nwpserial_port *up; - up = container_of(port, struct nwpserial_port, port); - /* check if tx buffer is full */ - wait_for_bits(up, UART_LSR_THRE); - dcr_write(up->dcr_host, UART_TX, c); - up->port.icount.tx++; -} - -static void -nwpserial_console_write(struct console *co, const char *s, unsigned int count) -{ - struct nwpserial_port *up = &nwpserial_ports[co->index]; - unsigned long flags; - int locked = 1; - - if (oops_in_progress) - locked = spin_trylock_irqsave(&up->port.lock, flags); - else - spin_lock_irqsave(&up->port.lock, flags); - - /* save and disable interrupt */ - up->ier = dcr_read(up->dcr_host, UART_IER); - dcr_write(up->dcr_host, UART_IER, up->ier & ~UART_IER_RDI); - - uart_console_write(&up->port, s, count, nwpserial_console_putchar); - - /* wait for transmitter to become empty */ - while ((dcr_read(up->dcr_host, UART_LSR) & UART_LSR_THRE) == 0) - cpu_relax(); - - /* restore interrupt state */ - dcr_write(up->dcr_host, UART_IER, up->ier); - - if (locked) - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static struct uart_driver nwpserial_reg; -static struct console nwpserial_console = { - .name = "ttySQ", - .write = nwpserial_console_write, - .device = uart_console_device, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &nwpserial_reg, -}; -#define NWPSERIAL_CONSOLE (&nwpserial_console) -#else -#define NWPSERIAL_CONSOLE NULL -#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */ - -/**************************************************************************/ - -static int nwpserial_request_port(struct uart_port *port) -{ - return 0; -} - -static void nwpserial_release_port(struct uart_port *port) -{ - /* N/A */ -} - -static void nwpserial_config_port(struct uart_port *port, int flags) -{ - port->type = PORT_NWPSERIAL; -} - -static irqreturn_t nwpserial_interrupt(int irq, void *dev_id) -{ - struct nwpserial_port *up = dev_id; - struct tty_port *port = &up->port.state->port; - irqreturn_t ret; - unsigned int iir; - unsigned char ch; - - spin_lock(&up->port.lock); - - /* check if the uart was the interrupt source. */ - iir = dcr_read(up->dcr_host, UART_IIR); - if (!iir) { - ret = IRQ_NONE; - goto out; - } - - do { - up->port.icount.rx++; - ch = dcr_read(up->dcr_host, UART_RX); - if (up->port.ignore_status_mask != NWPSERIAL_STATUS_RXVALID) - tty_insert_flip_char(port, ch, TTY_NORMAL); - } while (dcr_read(up->dcr_host, UART_LSR) & UART_LSR_DR); - - spin_unlock(&up->port.lock); - tty_flip_buffer_push(port); - spin_lock(&up->port.lock); - - ret = IRQ_HANDLED; - - /* clear interrupt */ - dcr_write(up->dcr_host, UART_IIR, 1); -out: - spin_unlock(&up->port.lock); - return ret; -} - -static int nwpserial_startup(struct uart_port *port) -{ - struct nwpserial_port *up; - int err; - - up = container_of(port, struct nwpserial_port, port); - - /* disable flow control by default */ - up->mcr = dcr_read(up->dcr_host, UART_MCR) & ~UART_MCR_AFE; - dcr_write(up->dcr_host, UART_MCR, up->mcr); - - /* register interrupt handler */ - err = request_irq(up->port.irq, nwpserial_interrupt, - IRQF_SHARED, "nwpserial", up); - if (err) - return err; - - /* enable interrupts */ - up->ier = UART_IER_RDI; - dcr_write(up->dcr_host, UART_IER, up->ier); - - /* enable receiving */ - up->port.ignore_status_mask &= ~NWPSERIAL_STATUS_RXVALID; - - return 0; -} - -static void nwpserial_shutdown(struct uart_port *port) -{ - struct nwpserial_port *up; - up = container_of(port, struct nwpserial_port, port); - - /* disable receiving */ - up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID; - - /* disable interrupts from this port */ - up->ier = 0; - dcr_write(up->dcr_host, UART_IER, up->ier); - - /* free irq */ - free_irq(up->port.irq, up); -} - -static int nwpserial_verify_port(struct uart_port *port, - struct serial_struct *ser) -{ - return -EINVAL; -} - -static const char *nwpserial_type(struct uart_port *port) -{ - return port->type == PORT_NWPSERIAL ? "nwpserial" : NULL; -} - -static void nwpserial_set_termios(struct uart_port *port, - struct ktermios *termios, struct ktermios *old) -{ - struct nwpserial_port *up; - up = container_of(port, struct nwpserial_port, port); - - up->port.read_status_mask = NWPSERIAL_STATUS_RXVALID - | NWPSERIAL_STATUS_TXFULL; - - up->port.ignore_status_mask = 0; - /* ignore all characters if CREAD is not set */ - if ((termios->c_cflag & CREAD) == 0) - up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID; - - /* Copy back the old hardware settings */ - if (old) - tty_termios_copy_hw(termios, old); -} - -static void nwpserial_break_ctl(struct uart_port *port, int ctl) -{ - /* N/A */ -} - -static void nwpserial_stop_rx(struct uart_port *port) -{ - struct nwpserial_port *up; - up = container_of(port, struct nwpserial_port, port); - /* don't forward any more data (like !CREAD) */ - up->port.ignore_status_mask = NWPSERIAL_STATUS_RXVALID; -} - -static void nwpserial_putchar(struct nwpserial_port *up, unsigned char c) -{ - /* check if tx buffer is full */ - wait_for_bits(up, UART_LSR_THRE); - dcr_write(up->dcr_host, UART_TX, c); - up->port.icount.tx++; -} - -static void nwpserial_start_tx(struct uart_port *port) -{ - struct nwpserial_port *up; - struct circ_buf *xmit; - up = container_of(port, struct nwpserial_port, port); - xmit = &up->port.state->xmit; - - if (port->x_char) { - nwpserial_putchar(up, up->port.x_char); - port->x_char = 0; - } - - while (!(uart_circ_empty(xmit) || uart_tx_stopped(&up->port))) { - nwpserial_putchar(up, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); - } -} - -static unsigned int nwpserial_get_mctrl(struct uart_port *port) -{ - return 0; -} - -static void nwpserial_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - /* N/A */ -} - -static void nwpserial_stop_tx(struct uart_port *port) -{ - /* N/A */ -} - -static unsigned int nwpserial_tx_empty(struct uart_port *port) -{ - struct nwpserial_port *up; - unsigned long flags; - int ret; - up = container_of(port, struct nwpserial_port, port); - - spin_lock_irqsave(&up->port.lock, flags); - ret = dcr_read(up->dcr_host, UART_LSR); - spin_unlock_irqrestore(&up->port.lock, flags); - - return ret & UART_LSR_TEMT ? TIOCSER_TEMT : 0; -} - -static struct uart_ops nwpserial_pops = { - .tx_empty = nwpserial_tx_empty, - .set_mctrl = nwpserial_set_mctrl, - .get_mctrl = nwpserial_get_mctrl, - .stop_tx = nwpserial_stop_tx, - .start_tx = nwpserial_start_tx, - .stop_rx = nwpserial_stop_rx, - .break_ctl = nwpserial_break_ctl, - .startup = nwpserial_startup, - .shutdown = nwpserial_shutdown, - .set_termios = nwpserial_set_termios, - .type = nwpserial_type, - .release_port = nwpserial_release_port, - .request_port = nwpserial_request_port, - .config_port = nwpserial_config_port, - .verify_port = nwpserial_verify_port, -}; - -static struct uart_driver nwpserial_reg = { - .owner = THIS_MODULE, - .driver_name = "nwpserial", - .dev_name = "ttySQ", - .major = TTY_MAJOR, - .minor = 68, - .nr = NWPSERIAL_NR, - .cons = NWPSERIAL_CONSOLE, -}; - -int nwpserial_register_port(struct uart_port *port) -{ - struct nwpserial_port *up = NULL; - int ret = -1; - int i; - static int first = 1; - int dcr_len; - int dcr_base; - struct device_node *dn; - - mutex_lock(&nwpserial_mutex); - - dn = port->dev->of_node; - if (dn == NULL) - goto out; - - /* get dcr base. */ - dcr_base = dcr_resource_start(dn, 0); - - /* find matching entry */ - for (i = 0; i < NWPSERIAL_NR; i++) - if (nwpserial_ports[i].port.iobase == dcr_base) { - up = &nwpserial_ports[i]; - break; - } - - /* we didn't find a mtching entry, search for a free port */ - if (up == NULL) - for (i = 0; i < NWPSERIAL_NR; i++) - if (nwpserial_ports[i].port.type == PORT_UNKNOWN && - nwpserial_ports[i].port.iobase == 0) { - up = &nwpserial_ports[i]; - break; - } - - if (up == NULL) { - ret = -EBUSY; - goto out; - } - - if (first) - uart_register_driver(&nwpserial_reg); - first = 0; - - up->port.membase = port->membase; - up->port.irq = port->irq; - up->port.uartclk = port->uartclk; - up->port.fifosize = port->fifosize; - up->port.regshift = port->regshift; - up->port.iotype = port->iotype; - up->port.flags = port->flags; - up->port.mapbase = port->mapbase; - up->port.private_data = port->private_data; - - if (port->dev) - up->port.dev = port->dev; - - if (up->port.iobase != dcr_base) { - up->port.ops = &nwpserial_pops; - up->port.fifosize = 16; - - spin_lock_init(&up->port.lock); - - up->port.iobase = dcr_base; - dcr_len = dcr_resource_len(dn, 0); - - up->dcr_host = dcr_map(dn, dcr_base, dcr_len); - if (!DCR_MAP_OK(up->dcr_host)) { - printk(KERN_ERR "Cannot map DCR resources for NWPSERIAL"); - goto out; - } - } - - ret = uart_add_one_port(&nwpserial_reg, &up->port); - if (ret == 0) - ret = up->port.line; - -out: - mutex_unlock(&nwpserial_mutex); - - return ret; -} -EXPORT_SYMBOL(nwpserial_register_port); - -void nwpserial_unregister_port(int line) -{ - struct nwpserial_port *up = &nwpserial_ports[line]; - mutex_lock(&nwpserial_mutex); - uart_remove_one_port(&nwpserial_reg, &up->port); - - up->port.type = PORT_UNKNOWN; - - mutex_unlock(&nwpserial_mutex); -} -EXPORT_SYMBOL(nwpserial_unregister_port); - -#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE -static int __init nwpserial_console_init(void) -{ - struct nwpserial_port *up = NULL; - struct device_node *dn; - const char *name; - int dcr_base; - int dcr_len; - int i; - - /* search for a free port */ - for (i = 0; i < NWPSERIAL_NR; i++) - if (nwpserial_ports[i].port.type == PORT_UNKNOWN) { - up = &nwpserial_ports[i]; - break; - } - - if (up == NULL) - return -1; - - name = of_get_property(of_chosen, "linux,stdout-path", NULL); - if (name == NULL) - return -1; - - dn = of_find_node_by_path(name); - if (!dn) - return -1; - - spin_lock_init(&up->port.lock); - up->port.ops = &nwpserial_pops; - up->port.type = PORT_NWPSERIAL; - up->port.fifosize = 16; - - dcr_base = dcr_resource_start(dn, 0); - dcr_len = dcr_resource_len(dn, 0); - up->port.iobase = dcr_base; - - up->dcr_host = dcr_map(dn, dcr_base, dcr_len); - if (!DCR_MAP_OK(up->dcr_host)) { - printk("Cannot map DCR resources for SERIAL"); - return -1; - } - register_console(&nwpserial_console); - return 0; -} -console_initcall(nwpserial_console_init); -#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */ diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 9d4c84f7485f..a2a529994ba5 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1165,7 +1165,7 @@ serial_omap_type(struct uart_port *port) #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) -static inline void wait_for_xmitr(struct uart_omap_port *up) +static void __maybe_unused wait_for_xmitr(struct uart_omap_port *up) { unsigned int status, tmout = 10000; @@ -1343,7 +1343,7 @@ static inline void serial_omap_add_console_port(struct uart_omap_port *up) /* Enable or disable the rs485 support */ static int -serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) { struct uart_omap_port *up = to_uart_omap_port(port); unsigned int mode; @@ -1356,8 +1356,12 @@ serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) up->ier = 0; serial_out(up, UART_IER, 0); + /* Clamp the delays to [0, 100ms] */ + rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U); + rs485->delay_rts_after_send = min(rs485->delay_rts_after_send, 100U); + /* store new config */ - port->rs485 = *rs485conf; + port->rs485 = *rs485; /* * Just as a precaution, only allow rs485 @@ -1866,7 +1870,7 @@ static struct platform_driver serial_omap_driver = { .probe = serial_omap_probe, .remove = serial_omap_remove, .driver = { - .name = DRIVER_NAME, + .name = OMAP_SERIAL_DRIVER_NAME, .pm = &serial_omap_dev_pm_ops, .of_match_table = of_match_ptr(omap_serial_of_match), }, diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 9becba654892..41eab75ba2af 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -603,7 +603,7 @@ static struct uart_driver serial_pxa_reg; /* * Wait for transmitter & holding register to empty */ -static inline void wait_for_xmitr(struct uart_pxa_port *up) +static void wait_for_xmitr(struct uart_pxa_port *up) { unsigned int status, tmout = 10000; diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index d72cd736bdc6..ac7f8df54406 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -601,14 +601,21 @@ static void s3c24xx_serial_rx_drain_fifo(struct s3c24xx_uart_port *ourport) { struct uart_port *port = &ourport->port; unsigned int ufcon, ch, flag, ufstat, uerstat; + unsigned int fifocnt = 0; int max_count = port->fifosize; while (max_count-- > 0) { - ufcon = rd_regl(port, S3C2410_UFCON); - ufstat = rd_regl(port, S3C2410_UFSTAT); - - if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0) - break; + /* + * Receive all characters known to be in FIFO + * before reading FIFO level again + */ + if (fifocnt == 0) { + ufstat = rd_regl(port, S3C2410_UFSTAT); + fifocnt = s3c24xx_serial_rx_fifocnt(ourport, ufstat); + if (fifocnt == 0) + break; + } + fifocnt--; uerstat = rd_regl(port, S3C2410_UERSTAT); ch = rd_regb(port, S3C2410_URXH); @@ -623,6 +630,7 @@ static void s3c24xx_serial_rx_drain_fifo(struct s3c24xx_uart_port *ourport) } } else { if (txe) { + ufcon = rd_regl(port, S3C2410_UFCON); ufcon |= S3C2410_UFCON_RESETRX; wr_regl(port, S3C2410_UFCON, ufcon); rx_enabled(port) = 1; @@ -2451,7 +2459,6 @@ static int __init s3c2410_early_console_setup(struct earlycon_device *device, } OF_EARLYCON_DECLARE(s3c2410, "samsung,s3c2410-uart", s3c2410_early_console_setup); -EARLYCON_DECLARE(s3c2410, s3c2410_early_console_setup); /* S3C2412, S3C2440, S3C64xx */ static struct samsung_early_console_data s3c2440_early_console_data = { @@ -2470,9 +2477,6 @@ OF_EARLYCON_DECLARE(s3c2440, "samsung,s3c2440-uart", s3c2440_early_console_setup); OF_EARLYCON_DECLARE(s3c6400, "samsung,s3c6400-uart", s3c2440_early_console_setup); -EARLYCON_DECLARE(s3c2412, s3c2440_early_console_setup); -EARLYCON_DECLARE(s3c2440, s3c2440_early_console_setup); -EARLYCON_DECLARE(s3c6400, s3c2440_early_console_setup); /* S5PV210, EXYNOS */ static struct samsung_early_console_data s5pv210_early_console_data = { @@ -2489,8 +2493,6 @@ OF_EARLYCON_DECLARE(s5pv210, "samsung,s5pv210-uart", s5pv210_early_console_setup); OF_EARLYCON_DECLARE(exynos4210, "samsung,exynos4210-uart", s5pv210_early_console_setup); -EARLYCON_DECLARE(s5pv210, s5pv210_early_console_setup); -EARLYCON_DECLARE(exynos4210, s5pv210_early_console_setup); #endif MODULE_ALIAS("platform:samsung-uart"); diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index edb5305b9d4d..025a4264430e 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -196,14 +196,14 @@ * or (IO6) * - only on 75x/76x */ -#define SC16IS7XX_MSR_CTS_BIT (1 << 0) /* CTS */ -#define SC16IS7XX_MSR_DSR_BIT (1 << 1) /* DSR (IO4) +#define SC16IS7XX_MSR_CTS_BIT (1 << 4) /* CTS */ +#define SC16IS7XX_MSR_DSR_BIT (1 << 5) /* DSR (IO4) * - only on 75x/76x */ -#define SC16IS7XX_MSR_RI_BIT (1 << 2) /* RI (IO7) +#define SC16IS7XX_MSR_RI_BIT (1 << 6) /* RI (IO7) * - only on 75x/76x */ -#define SC16IS7XX_MSR_CD_BIT (1 << 3) /* CD (IO6) +#define SC16IS7XX_MSR_CD_BIT (1 << 7) /* CD (IO6) * - only on 75x/76x */ #define SC16IS7XX_MSR_DELTA_MASK 0x0F /* Any of the delta bits! */ @@ -240,7 +240,7 @@ /* IOControl register bits (Only 750/760) */ #define SC16IS7XX_IOCONTROL_LATCH_BIT (1 << 0) /* Enable input latching */ -#define SC16IS7XX_IOCONTROL_GPIO_BIT (1 << 1) /* Enable GPIO[7:4] */ +#define SC16IS7XX_IOCONTROL_MODEM_BIT (1 << 1) /* Enable GPIO[7:4] as modem pins */ #define SC16IS7XX_IOCONTROL_SRESET_BIT (1 << 3) /* Software Reset */ /* EFCR register bits */ @@ -389,6 +389,13 @@ static void sc16is7xx_fifo_write(struct uart_port *port, u8 to_send) const u8 line = sc16is7xx_line(port); u8 addr = (SC16IS7XX_THR_REG << SC16IS7XX_REG_SHIFT) | line; + /* + * Don't send zero-length data, at least on SPI it confuses the chip + * delivering wrong TXLVL data. + */ + if (unlikely(!to_send)) + return; + regcache_cache_bypass(s->regmap, true); regmap_raw_write(s->regmap, addr, s->buf, to_send); regcache_cache_bypass(s->regmap, false); @@ -630,6 +637,12 @@ static void sc16is7xx_handle_tx(struct uart_port *port) if (likely(to_send)) { /* Limit to size of TX FIFO */ txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); + if (txlen > SC16IS7XX_FIFO_SIZE) { + dev_err_ratelimited(port->dev, + "chip reports %d free bytes in TX fifo, but it only has %d", + txlen, SC16IS7XX_FIFO_SIZE); + txlen = 0; + } to_send = (to_send > txlen) ? txlen : to_send; /* Add data to send */ @@ -674,7 +687,7 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) case SC16IS7XX_IIR_CTSRTS_SRC: msr = sc16is7xx_port_read(port, SC16IS7XX_MSR_REG); uart_handle_cts_change(port, - !!(msr & SC16IS7XX_MSR_CTS_BIT)); + !!(msr & SC16IS7XX_MSR_DCTS_BIT)); break; case SC16IS7XX_IIR_THRI_SRC: sc16is7xx_handle_tx(port); @@ -748,12 +761,20 @@ static void sc16is7xx_reg_proc(struct kthread_work *ws) memset(&one->config, 0, sizeof(one->config)); spin_unlock_irqrestore(&one->port.lock, irqflags); - if (config.flags & SC16IS7XX_RECONF_MD) + if (config.flags & SC16IS7XX_RECONF_MD) { sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG, SC16IS7XX_MCR_LOOP_BIT, (one->port.mctrl & TIOCM_LOOP) ? SC16IS7XX_MCR_LOOP_BIT : 0); - + sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG, + SC16IS7XX_MCR_RTS_BIT, + (one->port.mctrl & TIOCM_RTS) ? + SC16IS7XX_MCR_RTS_BIT : 0); + sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG, + SC16IS7XX_MCR_DTR_BIT, + (one->port.mctrl & TIOCM_DTR) ? + SC16IS7XX_MCR_DTR_BIT : 0); + } if (config.flags & SC16IS7XX_RECONF_IER) sc16is7xx_port_update(&one->port, SC16IS7XX_IER_REG, config.ier_clear, 0); @@ -1180,7 +1201,7 @@ static int sc16is7xx_probe(struct device *dev, if (devtype->nr_gpio) { /* Setup GPIO cotroller */ s->gpio.owner = THIS_MODULE; - s->gpio.dev = dev; + s->gpio.parent = dev; s->gpio.label = dev_name(dev); s->gpio.direction_input = sc16is7xx_gpio_direction_input; s->gpio.get = sc16is7xx_gpio_get; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index def5199ca004..a126a603b083 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -110,7 +110,7 @@ static void uart_start(struct tty_struct *tty) spin_unlock_irqrestore(&port->lock, flags); } -static inline void +static void uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) { unsigned long flags; @@ -171,14 +171,12 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, */ uart_change_speed(tty, state, NULL); - if (init_hw) { - /* - * Setup the RTS and DTR signals once the - * port is open and ready to respond. - */ - if (tty->termios.c_cflag & CBAUD) - uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); - } + /* + * Setup the RTS and DTR signals once the + * port is open and ready to respond. + */ + if (init_hw && C_BAUD(tty)) + uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); } /* @@ -240,7 +238,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) if (uart_console(uport) && tty) uport->cons->cflag = tty->termios.c_cflag; - if (!tty || (tty->termios.c_cflag & HUPCL)) + if (!tty || C_HUPCL(tty)) uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); uart_port_shutdown(port); @@ -485,12 +483,15 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, spin_unlock_irq(&uport->lock); } -static inline int __uart_put_char(struct uart_port *port, - struct circ_buf *circ, unsigned char c) +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 circ_buf *circ; unsigned long flags; int ret = 0; + circ = &state->xmit; if (!circ->buf) return 0; @@ -504,13 +505,6 @@ static inline int __uart_put_char(struct uart_port *port, return ret; } -static int uart_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct uart_state *state = tty->driver_data; - - return __uart_put_char(state->uart_port, &state->xmit, ch); -} - static void uart_flush_chars(struct tty_struct *tty) { uart_start(tty); @@ -639,7 +633,7 @@ static void uart_throttle(struct tty_struct *tty) if (I_IXOFF(tty)) mask |= UPSTAT_AUTOXOFF; - if (tty->termios.c_cflag & CRTSCTS) + if (C_CRTSCTS(tty)) mask |= UPSTAT_AUTORTS; if (port->status & mask) { @@ -647,11 +641,11 @@ static void uart_throttle(struct tty_struct *tty) mask &= ~port->status; } - if (mask & UPSTAT_AUTOXOFF) - uart_send_xchar(tty, STOP_CHAR(tty)); - if (mask & UPSTAT_AUTORTS) uart_clear_mctrl(port, TIOCM_RTS); + + if (mask & UPSTAT_AUTOXOFF) + uart_send_xchar(tty, STOP_CHAR(tty)); } static void uart_unthrottle(struct tty_struct *tty) @@ -662,7 +656,7 @@ static void uart_unthrottle(struct tty_struct *tty) if (I_IXOFF(tty)) mask |= UPSTAT_AUTOXOFF; - if (tty->termios.c_cflag & CRTSCTS) + if (C_CRTSCTS(tty)) mask |= UPSTAT_AUTORTS; if (port->status & mask) { @@ -670,21 +664,25 @@ static void uart_unthrottle(struct tty_struct *tty) mask &= ~port->status; } - if (mask & UPSTAT_AUTOXOFF) - uart_send_xchar(tty, START_CHAR(tty)); - if (mask & UPSTAT_AUTORTS) uart_set_mctrl(port, TIOCM_RTS); + + if (mask & UPSTAT_AUTOXOFF) + uart_send_xchar(tty, START_CHAR(tty)); } -static void do_uart_get_info(struct tty_port *port, - struct serial_struct *retinfo) +static void 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; memset(retinfo, 0, sizeof(*retinfo)); + /* + * Ensure the state we copy is consistent and no hardware changes + * occur as we go + */ + mutex_lock(&port->mutex); retinfo->type = uport->type; retinfo->line = uport->line; retinfo->port = uport->iobase; @@ -703,15 +701,6 @@ static void do_uart_get_info(struct tty_port *port, retinfo->io_type = uport->iotype; retinfo->iomem_reg_shift = uport->regshift; retinfo->iomem_base = (void *)(unsigned long)uport->mapbase; -} - -static void uart_get_info(struct tty_port *port, - struct serial_struct *retinfo) -{ - /* Ensure the state we copy is consistent and no hardware changes - occur as we go */ - mutex_lock(&port->mutex); - do_uart_get_info(port, retinfo); mutex_unlock(&port->mutex); } @@ -719,6 +708,7 @@ static int uart_get_info_user(struct tty_port *port, struct serial_struct __user *retinfo) { struct serial_struct tmp; + uart_get_info(port, &tmp); if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) @@ -1391,8 +1381,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) uport = state->uart_port; port = &state->port; - - pr_debug("uart_close(%d) called\n", uport ? uport->line : -1); + pr_debug("uart_close(%d) called\n", tty->index); if (!port->count || tty_port_close_start(port, tty, filp) == 0) return; @@ -1434,7 +1423,6 @@ static void uart_close(struct tty_struct *tty, struct file *filp) * Wake up anyone trying to open this port. */ clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); - clear_bit(ASYNCB_CLOSING, &port->flags); spin_unlock_irq(&port->lock); wake_up_interruptible(&port->open_wait); @@ -1510,7 +1498,7 @@ static void uart_hangup(struct tty_struct *tty) struct tty_port *port = &state->port; unsigned long flags; - pr_debug("uart_hangup(%d)\n", state->uart_port->line); + pr_debug("uart_hangup(%d)\n", tty->index); mutex_lock(&port->mutex); if (port->flags & ASYNC_NORMAL_ACTIVE) { @@ -1591,7 +1579,7 @@ static void uart_dtr_rts(struct tty_port *port, int onoff) */ static int uart_open(struct tty_struct *tty, struct file *filp) { - struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; + struct uart_driver *drv = tty->driver->driver_state; int retval, line = tty->index; struct uart_state *state = drv->state + line; struct tty_port *port = &state->port; @@ -1633,15 +1621,12 @@ static int uart_open(struct tty_struct *tty, struct file *filp) /* * If we succeeded, wait until the port is ready. */ +err_unlock: mutex_unlock(&port->mutex); if (retval == 0) retval = tty_port_block_til_ready(port, tty, filp); - end: return retval; -err_unlock: - mutex_unlock(&port->mutex); - goto end; } static const char *uart_type(struct uart_port *port) @@ -1700,17 +1685,13 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) seq_printf(m, " tx:%d rx:%d", uport->icount.tx, uport->icount.rx); if (uport->icount.frame) - seq_printf(m, " fe:%d", - uport->icount.frame); + seq_printf(m, " fe:%d", uport->icount.frame); if (uport->icount.parity) - seq_printf(m, " pe:%d", - uport->icount.parity); + seq_printf(m, " pe:%d", uport->icount.parity); if (uport->icount.brk) - seq_printf(m, " brk:%d", - uport->icount.brk); + seq_printf(m, " brk:%d", uport->icount.brk); if (uport->icount.overrun) - seq_printf(m, " oe:%d", - uport->icount.overrun); + seq_printf(m, " oe:%d", uport->icount.overrun); #define INFOBIT(bit, str) \ if (uport->mctrl & (bit)) \ @@ -1745,8 +1726,7 @@ static int uart_proc_show(struct seq_file *m, void *v) struct uart_driver *drv = ttydrv->driver_state; int i; - seq_printf(m, "serinfo:1.0 driver%s%s revision:%s\n", - "", "", ""); + seq_printf(m, "serinfo:1.0 driver%s%s revision:%s\n", "", "", ""); for (i = 0; i < drv->nr; i++) uart_line_info(m, drv, i); return 0; @@ -1818,8 +1798,8 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) * @options: ptr for <options> field; NULL if not present (out) * * Decodes earlycon kernel command line parameters of the form - * earlycon=<name>,io|mmio|mmio32|mmio32be|mmio32native,<addr>,<options> - * console=<name>,io|mmio|mmio32|mmio32be|mmio32native,<addr>,<options> + * earlycon=<name>,io|mmio|mmio16|mmio32|mmio32be|mmio32native,<addr>,<options> + * console=<name>,io|mmio|mmio16|mmio32|mmio32be|mmio32native,<addr>,<options> * * The optional form * earlycon=<name>,0x<addr>,<options> @@ -1834,6 +1814,9 @@ int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr, if (strncmp(p, "mmio,", 5) == 0) { *iotype = UPIO_MEM; p += 5; + } else if (strncmp(p, "mmio16,", 7) == 0) { + *iotype = UPIO_MEM16; + p += 7; } else if (strncmp(p, "mmio32,", 7) == 0) { *iotype = UPIO_MEM32; p += 7; @@ -1892,26 +1875,6 @@ uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow) } EXPORT_SYMBOL_GPL(uart_parse_options); -struct baud_rates { - unsigned int rate; - unsigned int cflag; -}; - -static const struct baud_rates baud_rates[] = { - { 921600, B921600 }, - { 460800, B460800 }, - { 230400, B230400 }, - { 115200, B115200 }, - { 57600, B57600 }, - { 38400, B38400 }, - { 19200, B19200 }, - { 9600, B9600 }, - { 4800, B4800 }, - { 2400, B2400 }, - { 1200, B1200 }, - { 0, B38400 } -}; - /** * uart_set_options - setup the serial console parameters * @port: pointer to the serial ports uart_port structure @@ -1927,7 +1890,6 @@ uart_set_options(struct uart_port *port, struct console *co, { struct ktermios termios; static struct ktermios dummy; - int i; /* * Ensure that the serial console lock is initialised @@ -1942,16 +1904,8 @@ uart_set_options(struct uart_port *port, struct console *co, memset(&termios, 0, sizeof(struct ktermios)); - termios.c_cflag = CREAD | HUPCL | CLOCAL; - - /* - * Construct a cflag setting. - */ - for (i = 0; baud_rates[i].rate; i++) - if (baud_rates[i].rate <= baud) - break; - - termios.c_cflag |= baud_rates[i].cflag; + termios.c_cflag |= CREAD | HUPCL | CLOCAL; + tty_termios_encode_baud_rate(&termios, baud, baud); if (bits == 7) termios.c_cflag |= CS7; @@ -2186,6 +2140,7 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) "I/O 0x%lx offset 0x%x", port->iobase, port->hub6); break; case UPIO_MEM: + case UPIO_MEM16: case UPIO_MEM32: case UPIO_MEM32BE: case UPIO_AU: @@ -2831,6 +2786,7 @@ int uart_match_port(struct uart_port *port1, struct uart_port *port2) return (port1->iobase == port2->iobase) && (port1->hub6 == port2->hub6); case UPIO_MEM: + case UPIO_MEM16: case UPIO_MEM32: case UPIO_MEM32BE: case UPIO_AU: diff --git a/drivers/tty/serial/serial_ks8695.c b/drivers/tty/serial/serial_ks8695.c index b4decf8787de..57f152394af5 100644 --- a/drivers/tty/serial/serial_ks8695.c +++ b/drivers/tty/serial/serial_ks8695.c @@ -554,7 +554,7 @@ static struct uart_port ks8695uart_ports[SERIAL_KS8695_NR] = { .uartclk = KS8695_CLOCK_RATE * 16, .fifosize = 16, .ops = &ks8695uart_pops, - .flags = ASYNC_BOOT_AUTOCONF, + .flags = UPF_BOOT_AUTOCONF, .line = 0, } }; diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index 3eb57eb532f1..02147361eaa9 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -20,6 +20,7 @@ #include <linux/gpio/consumer.h> #include <linux/termios.h> #include <linux/serial_core.h> +#include <linux/module.h> #include "serial_mctrl_gpio.h" @@ -193,6 +194,7 @@ struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx) return gpios; } +EXPORT_SYMBOL_GPL(mctrl_gpio_init); void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) { @@ -247,3 +249,6 @@ void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios) disable_irq(gpios->irq[i]); } } +EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms); + +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 51c7507b0444..0130feb069ae 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2,6 +2,7 @@ * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) * * Copyright (C) 2002 - 2011 Paul Mundt + * Copyright (C) 2015 Glider bvba * Modified to support SH7720 SCIF. Markus Brunner, Mark Jonas (Jul 2007). * * based off of the old drivers/char/sh-sci.c by: @@ -38,7 +39,6 @@ #include <linux/major.h> #include <linux/module.h> #include <linux/mm.h> -#include <linux/notifier.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> @@ -76,6 +76,30 @@ enum { ((port)->irqs[SCIx_ERI_IRQ] && \ ((port)->irqs[SCIx_RXI_IRQ] < 0)) +enum SCI_CLKS { + SCI_FCK, /* Functional Clock */ + SCI_SCK, /* Optional External Clock */ + SCI_BRG_INT, /* Optional BRG Internal Clock Source */ + SCI_SCIF_CLK, /* Optional BRG External Clock Source */ + SCI_NUM_CLKS +}; + +/* Bit x set means sampling rate x + 1 is supported */ +#define SCI_SR(x) BIT((x) - 1) +#define SCI_SR_RANGE(x, y) GENMASK((y) - 1, (x) - 1) + +#define SCI_SR_SCIFAB SCI_SR(5) | SCI_SR(7) | SCI_SR(11) | \ + SCI_SR(13) | SCI_SR(16) | SCI_SR(17) | \ + SCI_SR(19) | SCI_SR(27) + +#define min_sr(_port) ffs((_port)->sampling_rate_mask) +#define max_sr(_port) fls((_port)->sampling_rate_mask) + +/* Iterate over all supported sampling rates, from high to low */ +#define for_each_sr(_sr, _port) \ + for ((_sr) = max_sr(_port); (_sr) >= min_sr(_port); (_sr)--) \ + if ((_port)->sampling_rate_mask & SCI_SR((_sr))) + struct sci_port { struct uart_port port; @@ -85,17 +109,16 @@ struct sci_port { unsigned int overrun_mask; unsigned int error_mask; unsigned int error_clear; - unsigned int sampling_rate; + unsigned int sampling_rate_mask; resource_size_t reg_size; /* Break timer */ struct timer_list break_timer; int break_flag; - /* Interface clock */ - struct clk *iclk; - /* Function clock */ - struct clk *fclk; + /* Clocks */ + struct clk *clks[SCI_NUM_CLKS]; + unsigned long clk_rates[SCI_NUM_CLKS]; int irqs[SCIx_NR_IRQS]; char *irqstr[SCIx_NR_IRQS]; @@ -116,8 +139,6 @@ struct sci_port { struct timer_list rx_timer; unsigned int rx_timeout; #endif - - struct notifier_block freq_transition; }; #define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS @@ -163,6 +184,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [HSSRR] = sci_reg_invalid, [SCPCR] = sci_reg_invalid, [SCPDR] = sci_reg_invalid, + [SCDL] = sci_reg_invalid, + [SCCKS] = sci_reg_invalid, }, /* @@ -185,6 +208,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [HSSRR] = sci_reg_invalid, [SCPCR] = sci_reg_invalid, [SCPDR] = sci_reg_invalid, + [SCDL] = sci_reg_invalid, + [SCCKS] = sci_reg_invalid, }, /* @@ -206,6 +231,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [HSSRR] = sci_reg_invalid, [SCPCR] = { 0x30, 16 }, [SCPDR] = { 0x34, 16 }, + [SCDL] = sci_reg_invalid, + [SCCKS] = sci_reg_invalid, }, /* @@ -227,6 +254,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [HSSRR] = sci_reg_invalid, [SCPCR] = { 0x30, 16 }, [SCPDR] = { 0x34, 16 }, + [SCDL] = sci_reg_invalid, + [SCCKS] = sci_reg_invalid, }, /* @@ -249,6 +278,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [HSSRR] = sci_reg_invalid, [SCPCR] = sci_reg_invalid, [SCPDR] = sci_reg_invalid, + [SCDL] = sci_reg_invalid, + [SCCKS] = sci_reg_invalid, }, /* @@ -270,6 +301,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [HSSRR] = sci_reg_invalid, [SCPCR] = sci_reg_invalid, [SCPDR] = sci_reg_invalid, + [SCDL] = sci_reg_invalid, + [SCCKS] = sci_reg_invalid, }, /* @@ -291,6 +324,32 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [HSSRR] = sci_reg_invalid, [SCPCR] = sci_reg_invalid, [SCPDR] = sci_reg_invalid, + [SCDL] = sci_reg_invalid, + [SCCKS] = sci_reg_invalid, + }, + + /* + * Common SCIF definitions for ports with a Baud Rate Generator for + * External Clock (BRG). + */ + [SCIx_SH4_SCIF_BRG_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = { 0x20, 16 }, + [SCLSR] = { 0x24, 16 }, + [HSSRR] = sci_reg_invalid, + [SCPCR] = sci_reg_invalid, + [SCPDR] = sci_reg_invalid, + [SCDL] = { 0x30, 16 }, + [SCCKS] = { 0x34, 16 }, }, /* @@ -312,6 +371,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [HSSRR] = { 0x40, 16 }, [SCPCR] = sci_reg_invalid, [SCPDR] = sci_reg_invalid, + [SCDL] = { 0x30, 16 }, + [SCCKS] = { 0x34, 16 }, }, /* @@ -334,6 +395,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [HSSRR] = sci_reg_invalid, [SCPCR] = sci_reg_invalid, [SCPDR] = sci_reg_invalid, + [SCDL] = sci_reg_invalid, + [SCCKS] = sci_reg_invalid, }, /* @@ -356,6 +419,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [HSSRR] = sci_reg_invalid, [SCPCR] = sci_reg_invalid, [SCPDR] = sci_reg_invalid, + [SCDL] = sci_reg_invalid, + [SCCKS] = sci_reg_invalid, }, /* @@ -378,6 +443,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [HSSRR] = sci_reg_invalid, [SCPCR] = sci_reg_invalid, [SCPDR] = sci_reg_invalid, + [SCDL] = sci_reg_invalid, + [SCCKS] = sci_reg_invalid, }, }; @@ -452,18 +519,24 @@ static int sci_probe_regmap(struct plat_sci_port *cfg) static void sci_port_enable(struct sci_port *sci_port) { + unsigned int i; + if (!sci_port->port.dev) return; pm_runtime_get_sync(sci_port->port.dev); - clk_prepare_enable(sci_port->iclk); - sci_port->port.uartclk = clk_get_rate(sci_port->iclk); - clk_prepare_enable(sci_port->fclk); + for (i = 0; i < SCI_NUM_CLKS; i++) { + clk_prepare_enable(sci_port->clks[i]); + sci_port->clk_rates[i] = clk_get_rate(sci_port->clks[i]); + } + sci_port->port.uartclk = sci_port->clk_rates[SCI_FCK]; } static void sci_port_disable(struct sci_port *sci_port) { + unsigned int i; + if (!sci_port->port.dev) return; @@ -475,8 +548,8 @@ static void sci_port_disable(struct sci_port *sci_port) del_timer_sync(&sci_port->break_timer); sci_port->break_flag = 0; - clk_disable_unprepare(sci_port->fclk); - clk_disable_unprepare(sci_port->iclk); + for (i = SCI_NUM_CLKS; i-- > 0; ) + clk_disable_unprepare(sci_port->clks[i]); pm_runtime_put_sync(sci_port->port.dev); } @@ -580,7 +653,8 @@ static void sci_clear_SCxSR(struct uart_port *port, unsigned int mask) } } -#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE) +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE) || \ + defined(CONFIG_SERIAL_SH_SCI_EARLYCON) #ifdef CONFIG_CONSOLE_POLL static int sci_poll_get_char(struct uart_port *port) @@ -621,7 +695,8 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c) serial_port_out(port, SCxTDR, c); sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port)); } -#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ +#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE || + CONFIG_SERIAL_SH_SCI_EARLYCON */ static void sci_init_pins(struct uart_port *port, unsigned int cflag) { @@ -1606,29 +1681,6 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) return ret; } -/* - * Here we define a transition notifier so that we can update all of our - * ports' baud rate when the peripheral clock changes. - */ -static int sci_notifier(struct notifier_block *self, - unsigned long phase, void *p) -{ - struct sci_port *sci_port; - unsigned long flags; - - sci_port = container_of(self, struct sci_port, freq_transition); - - if (phase == CPUFREQ_POSTCHANGE) { - struct uart_port *port = &sci_port->port; - - spin_lock_irqsave(&port->lock, flags); - port->uartclk = clk_get_rate(sci_port->iclk); - spin_unlock_irqrestore(&port->lock, flags); - } - - return NOTIFY_OK; -} - static const struct sci_irq_desc { const char *desc; irq_handler_t handler; @@ -1864,90 +1916,130 @@ static void sci_shutdown(struct uart_port *port) sci_free_irq(s); } -static unsigned int sci_scbrr_calc(struct sci_port *s, unsigned int bps, - unsigned long freq) +static int sci_sck_calc(struct sci_port *s, unsigned int bps, + unsigned int *srr) { - if (s->sampling_rate) - return DIV_ROUND_CLOSEST(freq, s->sampling_rate * bps) - 1; + unsigned long freq = s->clk_rates[SCI_SCK]; + int err, min_err = INT_MAX; + unsigned int sr; + + if (s->port.type != PORT_HSCIF) + freq *= 2; - /* Warn, but use a safe default */ - WARN_ON(1); + for_each_sr(sr, s) { + err = DIV_ROUND_CLOSEST(freq, sr) - bps; + if (abs(err) >= abs(min_err)) + continue; + + min_err = err; + *srr = sr - 1; - return ((freq + 16 * bps) / (32 * bps) - 1); + if (!err) + break; + } + + dev_dbg(s->port.dev, "SCK: %u%+d bps using SR %u\n", bps, min_err, + *srr + 1); + return min_err; } -/* calculate frame length from SMR */ -static int sci_baud_calc_frame_len(unsigned int smr_val) +static int sci_brg_calc(struct sci_port *s, unsigned int bps, + unsigned long freq, unsigned int *dlr, + unsigned int *srr) { - int len = 10; + int err, min_err = INT_MAX; + unsigned int sr, dl; - if (smr_val & SCSMR_CHR) - len--; - if (smr_val & SCSMR_PE) - len++; - if (smr_val & SCSMR_STOP) - len++; + if (s->port.type != PORT_HSCIF) + freq *= 2; - return len; -} + for_each_sr(sr, s) { + dl = DIV_ROUND_CLOSEST(freq, sr * bps); + dl = clamp(dl, 1U, 65535U); + + err = DIV_ROUND_CLOSEST(freq, sr * dl) - bps; + if (abs(err) >= abs(min_err)) + continue; + min_err = err; + *dlr = dl; + *srr = sr - 1; -/* calculate sample rate, BRR, and clock select for HSCIF */ -static void sci_baud_calc_hscif(unsigned int bps, unsigned long freq, - int *brr, unsigned int *srr, - unsigned int *cks, int frame_len) + if (!err) + break; + } + + dev_dbg(s->port.dev, "BRG: %u%+d bps using DL %u SR %u\n", bps, + min_err, *dlr, *srr + 1); + return min_err; +} + +/* calculate sample rate, BRR, and clock select */ +static int sci_scbrr_calc(struct sci_port *s, unsigned int bps, + unsigned int *brr, unsigned int *srr, + unsigned int *cks) { - int sr, c, br, err, recv_margin; - int min_err = 1000; /* 100% */ - int recv_max_margin = 0; + unsigned long freq = s->clk_rates[SCI_FCK]; + unsigned int sr, br, prediv, scrate, c; + int err, min_err = INT_MAX; - /* Find the combination of sample rate and clock select with the - smallest deviation from the desired baud rate. */ - for (sr = 8; sr <= 32; sr++) { + if (s->port.type != PORT_HSCIF) + freq *= 2; + + /* + * Find the combination of sample rate and clock select with the + * smallest deviation from the desired baud rate. + * Prefer high sample rates to maximise the receive margin. + * + * M: Receive margin (%) + * N: Ratio of bit rate to clock (N = sampling rate) + * D: Clock duty (D = 0 to 1.0) + * L: Frame length (L = 9 to 12) + * F: Absolute value of clock frequency deviation + * + * M = |(0.5 - 1 / 2 * N) - ((L - 0.5) * F) - + * (|D - 0.5| / N * (1 + F))| + * NOTE: Usually, treat D for 0.5, F is 0 by this calculation. + */ + for_each_sr(sr, s) { for (c = 0; c <= 3; c++) { /* integerized formulas from HSCIF documentation */ - br = DIV_ROUND_CLOSEST(freq, (sr * - (1 << (2 * c + 1)) * bps)) - 1; - br = clamp(br, 0, 255); - err = DIV_ROUND_CLOSEST(freq, ((br + 1) * bps * sr * - (1 << (2 * c + 1)) / 1000)) - - 1000; - /* Calc recv margin - * M: Receive margin (%) - * N: Ratio of bit rate to clock (N = sampling rate) - * D: Clock duty (D = 0 to 1.0) - * L: Frame length (L = 9 to 12) - * F: Absolute value of clock frequency deviation + prediv = sr * (1 << (2 * c + 1)); + + /* + * We need to calculate: + * + * br = freq / (prediv * bps) clamped to [1..256] + * err = freq / (br * prediv) - bps * - * M = |(0.5 - 1 / 2 * N) - ((L - 0.5) * F) - - * (|D - 0.5| / N * (1 + F))| - * NOTE: Usually, treat D for 0.5, F is 0 by this - * calculation. + * Watch out for overflow when calculating the desired + * sampling clock rate! */ - recv_margin = abs((500 - - DIV_ROUND_CLOSEST(1000, sr << 1)) / 10); - if (abs(min_err) > abs(err)) { - min_err = err; - recv_max_margin = recv_margin; - } else if ((min_err == err) && - (recv_margin > recv_max_margin)) - recv_max_margin = recv_margin; - else + if (bps > UINT_MAX / prediv) + break; + + scrate = prediv * bps; + br = DIV_ROUND_CLOSEST(freq, scrate); + br = clamp(br, 1U, 256U); + + err = DIV_ROUND_CLOSEST(freq, br * prediv) - bps; + if (abs(err) >= abs(min_err)) continue; - *brr = br; + min_err = err; + *brr = br - 1; *srr = sr - 1; *cks = c; + + if (!err) + goto found; } } - if (min_err == 1000) { - WARN_ON(1); - /* use defaults */ - *brr = 255; - *srr = 15; - *cks = 0; - } +found: + dev_dbg(s->port.dev, "BRR: %u%+d bps using N %u SR %u cks %u\n", bps, + min_err, *brr, *srr + 1, *cks); + return min_err; } static void sci_reset(struct uart_port *port) @@ -1969,11 +2061,14 @@ static void sci_reset(struct uart_port *port) static void sci_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { + unsigned int baud, smr_val = SCSMR_ASYNC, scr_val = 0, i; + unsigned int brr = 255, cks = 0, srr = 15, dl = 0, sccks = 0; + unsigned int brr1 = 255, cks1 = 0, srr1 = 15, dl1 = 0; struct sci_port *s = to_sci_port(port); const struct plat_sci_reg *reg; - unsigned int baud, smr_val = 0, max_baud, cks = 0; - int t = -1; - unsigned int srr = 15; + int min_err = INT_MAX, err; + unsigned long max_freq = 0; + int best_clk = -1; if ((termios->c_cflag & CSIZE) == CS7) smr_val |= SCSMR_CHR; @@ -1992,41 +2087,134 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, * that the previous boot loader has enabled required clocks and * setup the baud rate generator hardware for us already. */ - max_baud = port->uartclk ? port->uartclk / 16 : 115200; - - baud = uart_get_baud_rate(port, termios, old, 0, max_baud); - if (likely(baud && port->uartclk)) { - if (s->cfg->type == PORT_HSCIF) { - int frame_len = sci_baud_calc_frame_len(smr_val); - sci_baud_calc_hscif(baud, port->uartclk, &t, &srr, - &cks, frame_len); - } else { - t = sci_scbrr_calc(s, baud, port->uartclk); - for (cks = 0; t >= 256 && cks <= 3; cks++) - t >>= 2; + if (!port->uartclk) { + baud = uart_get_baud_rate(port, termios, old, 0, 115200); + goto done; + } + + for (i = 0; i < SCI_NUM_CLKS; i++) + max_freq = max(max_freq, s->clk_rates[i]); + + baud = uart_get_baud_rate(port, termios, old, 0, max_freq / min_sr(s)); + if (!baud) + goto done; + + /* + * There can be multiple sources for the sampling clock. Find the one + * that gives us the smallest deviation from the desired baud rate. + */ + + /* Optional Undivided External Clock */ + if (s->clk_rates[SCI_SCK] && port->type != PORT_SCIFA && + port->type != PORT_SCIFB) { + err = sci_sck_calc(s, baud, &srr1); + if (abs(err) < abs(min_err)) { + best_clk = SCI_SCK; + scr_val = SCSCR_CKE1; + sccks = SCCKS_CKS; + min_err = err; + srr = srr1; + if (!err) + goto done; + } + } + + /* Optional BRG Frequency Divided External Clock */ + if (s->clk_rates[SCI_SCIF_CLK] && sci_getreg(port, SCDL)->size) { + err = sci_brg_calc(s, baud, s->clk_rates[SCI_SCIF_CLK], &dl1, + &srr1); + if (abs(err) < abs(min_err)) { + best_clk = SCI_SCIF_CLK; + scr_val = SCSCR_CKE1; + sccks = 0; + min_err = err; + dl = dl1; + srr = srr1; + if (!err) + goto done; + } + } + + /* Optional BRG Frequency Divided Internal Clock */ + if (s->clk_rates[SCI_BRG_INT] && sci_getreg(port, SCDL)->size) { + err = sci_brg_calc(s, baud, s->clk_rates[SCI_BRG_INT], &dl1, + &srr1); + if (abs(err) < abs(min_err)) { + best_clk = SCI_BRG_INT; + scr_val = SCSCR_CKE1; + sccks = SCCKS_XIN; + min_err = err; + dl = dl1; + srr = srr1; + if (!min_err) + goto done; } } + /* Divided Functional Clock using standard Bit Rate Register */ + err = sci_scbrr_calc(s, baud, &brr1, &srr1, &cks1); + if (abs(err) < abs(min_err)) { + best_clk = SCI_FCK; + scr_val = 0; + min_err = err; + brr = brr1; + srr = srr1; + cks = cks1; + } + +done: + if (best_clk >= 0) + dev_dbg(port->dev, "Using clk %pC for %u%+d bps\n", + s->clks[best_clk], baud, min_err); + sci_port_enable(s); - sci_reset(port); + /* + * Program the optional External Baud Rate Generator (BRG) first. + * It controls the mux to select (H)SCK or frequency divided clock. + */ + if (best_clk >= 0 && sci_getreg(port, SCCKS)->size) { + serial_port_out(port, SCDL, dl); + serial_port_out(port, SCCKS, sccks); + } - smr_val |= serial_port_in(port, SCSMR) & SCSMR_CKS; + sci_reset(port); uart_update_timeout(port, termios->c_cflag, baud); - dev_dbg(port->dev, "%s: SMR %x, cks %x, t %x, SCSCR %x\n", - __func__, smr_val, cks, t, s->cfg->scscr); - - if (t >= 0) { - serial_port_out(port, SCSMR, (smr_val & ~SCSMR_CKS) | cks); - serial_port_out(port, SCBRR, t); - reg = sci_getreg(port, HSSRR); - if (reg->size) + if (best_clk >= 0) { + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + switch (srr + 1) { + case 5: smr_val |= SCSMR_SRC_5; break; + case 7: smr_val |= SCSMR_SRC_7; break; + case 11: smr_val |= SCSMR_SRC_11; break; + case 13: smr_val |= SCSMR_SRC_13; break; + case 16: smr_val |= SCSMR_SRC_16; break; + case 17: smr_val |= SCSMR_SRC_17; break; + case 19: smr_val |= SCSMR_SRC_19; break; + case 27: smr_val |= SCSMR_SRC_27; break; + } + smr_val |= cks; + dev_dbg(port->dev, + "SCR 0x%x SMR 0x%x BRR %u CKS 0x%x DL %u SRR %u\n", + scr_val, smr_val, brr, sccks, dl, srr); + serial_port_out(port, SCSCR, scr_val); + serial_port_out(port, SCSMR, smr_val); + serial_port_out(port, SCBRR, brr); + if (sci_getreg(port, HSSRR)->size) serial_port_out(port, HSSRR, srr | HSCIF_SRE); - udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ - } else + + /* Wait one bit interval */ + udelay((1000000 + (baud - 1)) / baud); + } else { + /* Don't touch the bit rate configuration */ + scr_val = s->cfg->scscr & (SCSCR_CKE1 | SCSCR_CKE0); + smr_val |= serial_port_in(port, SCSMR) & + (SCSMR_CKEDG | SCSMR_SRC_MASK | SCSMR_CKS); + dev_dbg(port->dev, "SCR 0x%x SMR 0x%x\n", scr_val, smr_val); + serial_port_out(port, SCSCR, scr_val); serial_port_out(port, SCSMR, smr_val); + } sci_init_pins(port, termios->c_cflag); @@ -2051,7 +2239,19 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, serial_port_out(port, SCFCR, ctrl); } - serial_port_out(port, SCSCR, s->cfg->scscr); + scr_val |= s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0); + dev_dbg(port->dev, "SCSCR 0x%x\n", scr_val); + serial_port_out(port, SCSCR, scr_val); + if ((srr + 1 == 5) && + (port->type == PORT_SCIFA || port->type == PORT_SCIFB)) { + /* + * In asynchronous mode, when the sampling rate is 1/5, first + * received data may become invalid on some SCIFA and SCIFB. + * To avoid this problem wait more than 1 serial data time (1 + * bit time x serial data number) after setting SCSCR.RE = 1. + */ + udelay(DIV_ROUND_UP(10 * 1000000, baud)); + } #ifdef CONFIG_SERIAL_SH_SCI_DMA /* @@ -2241,6 +2441,63 @@ static struct uart_ops sci_uart_ops = { #endif }; +static int sci_init_clocks(struct sci_port *sci_port, struct device *dev) +{ + const char *clk_names[] = { + [SCI_FCK] = "fck", + [SCI_SCK] = "sck", + [SCI_BRG_INT] = "brg_int", + [SCI_SCIF_CLK] = "scif_clk", + }; + struct clk *clk; + unsigned int i; + + if (sci_port->cfg->type == PORT_HSCIF) + clk_names[SCI_SCK] = "hsck"; + + for (i = 0; i < SCI_NUM_CLKS; i++) { + clk = devm_clk_get(dev, clk_names[i]); + if (PTR_ERR(clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (IS_ERR(clk) && i == SCI_FCK) { + /* + * "fck" used to be called "sci_ick", and we need to + * maintain DT backward compatibility. + */ + clk = devm_clk_get(dev, "sci_ick"); + if (PTR_ERR(clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (!IS_ERR(clk)) + goto found; + + /* + * Not all SH platforms declare a clock lookup entry + * for SCI devices, in which case we need to get the + * global "peripheral_clk" clock. + */ + clk = devm_clk_get(dev, "peripheral_clk"); + if (!IS_ERR(clk)) + goto found; + + dev_err(dev, "failed to get %s (%ld)\n", clk_names[i], + PTR_ERR(clk)); + return PTR_ERR(clk); + } + +found: + if (IS_ERR(clk)) + dev_dbg(dev, "failed to get %s (%ld)\n", clk_names[i], + PTR_ERR(clk)); + else + dev_dbg(dev, "clk %s is %pC rate %pCr\n", clk_names[i], + clk, clk); + sci_port->clks[i] = IS_ERR(clk) ? NULL : clk; + } + return 0; +} + static int sci_init_single(struct platform_device *dev, struct sci_port *sci_port, unsigned int index, struct plat_sci_port *p, bool early) @@ -2291,37 +2548,37 @@ static int sci_init_single(struct platform_device *dev, port->fifosize = 256; sci_port->overrun_reg = SCxSR; sci_port->overrun_mask = SCIFA_ORER; - sci_port->sampling_rate = 16; + sci_port->sampling_rate_mask = SCI_SR_SCIFAB; break; case PORT_HSCIF: port->fifosize = 128; sci_port->overrun_reg = SCLSR; sci_port->overrun_mask = SCLSR_ORER; - sci_port->sampling_rate = 0; + sci_port->sampling_rate_mask = SCI_SR_RANGE(8, 32); break; case PORT_SCIFA: port->fifosize = 64; sci_port->overrun_reg = SCxSR; sci_port->overrun_mask = SCIFA_ORER; - sci_port->sampling_rate = 16; + sci_port->sampling_rate_mask = SCI_SR_SCIFAB; break; case PORT_SCIF: port->fifosize = 16; if (p->regtype == SCIx_SH7705_SCIF_REGTYPE) { sci_port->overrun_reg = SCxSR; sci_port->overrun_mask = SCIFA_ORER; - sci_port->sampling_rate = 16; + sci_port->sampling_rate_mask = SCI_SR(16); } else { sci_port->overrun_reg = SCLSR; sci_port->overrun_mask = SCLSR_ORER; - sci_port->sampling_rate = 32; + sci_port->sampling_rate_mask = SCI_SR(32); } break; default: port->fifosize = 1; sci_port->overrun_reg = SCxSR; sci_port->overrun_mask = SCI_ORER; - sci_port->sampling_rate = 32; + sci_port->sampling_rate_mask = SCI_SR(32); break; } @@ -2330,25 +2587,12 @@ static int sci_init_single(struct platform_device *dev, * data override the sampling rate for now. */ if (p->sampling_rate) - sci_port->sampling_rate = p->sampling_rate; + sci_port->sampling_rate_mask = SCI_SR(p->sampling_rate); if (!early) { - sci_port->iclk = clk_get(&dev->dev, "sci_ick"); - if (IS_ERR(sci_port->iclk)) { - sci_port->iclk = clk_get(&dev->dev, "peripheral_clk"); - if (IS_ERR(sci_port->iclk)) { - dev_err(&dev->dev, "can't get iclk\n"); - return PTR_ERR(sci_port->iclk); - } - } - - /* - * The function clock is optional, ignore it if we can't - * find it. - */ - sci_port->fclk = clk_get(&dev->dev, "sci_fck"); - if (IS_ERR(sci_port->fclk)) - sci_port->fclk = NULL; + ret = sci_init_clocks(sci_port, &dev->dev); + if (ret < 0) + return ret; port->dev = &dev->dev; @@ -2405,13 +2649,11 @@ static int sci_init_single(struct platform_device *dev, static void sci_cleanup_single(struct sci_port *port) { - clk_put(port->iclk); - clk_put(port->fclk); - pm_runtime_disable(port->port.dev); } -#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE +#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) || \ + defined(CONFIG_SERIAL_SH_SCI_EARLYCON) static void serial_console_putchar(struct uart_port *port, int ch) { sci_poll_put_char(port, ch); @@ -2426,21 +2668,26 @@ static void serial_console_write(struct console *co, const char *s, { struct sci_port *sci_port = &sci_ports[co->index]; struct uart_port *port = &sci_port->port; - unsigned short bits, ctrl; + unsigned short bits, ctrl, ctrl_temp; unsigned long flags; int locked = 1; local_irq_save(flags); +#if defined(SUPPORT_SYSRQ) if (port->sysrq) locked = 0; - else if (oops_in_progress) + else +#endif + if (oops_in_progress) locked = spin_trylock(&port->lock); else spin_lock(&port->lock); - /* first save the SCSCR then disable the interrupts */ + /* first save SCSCR then disable interrupts, keep clock source */ ctrl = serial_port_in(port, SCSCR); - serial_port_out(port, SCSCR, sci_port->cfg->scscr); + ctrl_temp = (sci_port->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0)) | + (ctrl & (SCSCR_CKE1 | SCSCR_CKE0)); + serial_port_out(port, SCSCR, ctrl_temp); uart_console_write(port, s, count, serial_console_putchar); @@ -2541,7 +2788,7 @@ static inline int sci_probe_earlyprintk(struct platform_device *pdev) #define SCI_CONSOLE NULL -#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */ +#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE || CONFIG_SERIAL_SH_SCI_EARLYCON */ static const char banner[] __initconst = "SuperH (H)SCI(F) driver initialized"; @@ -2559,9 +2806,6 @@ static int sci_remove(struct platform_device *dev) { struct sci_port *port = platform_get_drvdata(dev); - cpufreq_unregister_notifier(&port->freq_transition, - CPUFREQ_TRANSITION_NOTIFIER); - uart_remove_one_port(&sci_uart_driver, &port->port); sci_cleanup_single(port); @@ -2569,42 +2813,44 @@ static int sci_remove(struct platform_device *dev) return 0; } -struct sci_port_info { - unsigned int type; - unsigned int regtype; -}; + +#define SCI_OF_DATA(type, regtype) (void *)((type) << 16 | (regtype)) +#define SCI_OF_TYPE(data) ((unsigned long)(data) >> 16) +#define SCI_OF_REGTYPE(data) ((unsigned long)(data) & 0xffff) static const struct of_device_id of_sci_match[] = { + /* SoC-specific types */ + { + .compatible = "renesas,scif-r7s72100", + .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH2_SCIF_FIFODATA_REGTYPE), + }, + /* Family-specific types */ + { + .compatible = "renesas,rcar-gen1-scif", + .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE), + }, { + .compatible = "renesas,rcar-gen2-scif", + .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE), + }, { + .compatible = "renesas,rcar-gen3-scif", + .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE), + }, + /* Generic types */ { .compatible = "renesas,scif", - .data = &(const struct sci_port_info) { - .type = PORT_SCIF, - .regtype = SCIx_SH4_SCIF_REGTYPE, - }, + .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_REGTYPE), }, { .compatible = "renesas,scifa", - .data = &(const struct sci_port_info) { - .type = PORT_SCIFA, - .regtype = SCIx_SCIFA_REGTYPE, - }, + .data = SCI_OF_DATA(PORT_SCIFA, SCIx_SCIFA_REGTYPE), }, { .compatible = "renesas,scifb", - .data = &(const struct sci_port_info) { - .type = PORT_SCIFB, - .regtype = SCIx_SCIFB_REGTYPE, - }, + .data = SCI_OF_DATA(PORT_SCIFB, SCIx_SCIFB_REGTYPE), }, { .compatible = "renesas,hscif", - .data = &(const struct sci_port_info) { - .type = PORT_HSCIF, - .regtype = SCIx_HSCIF_REGTYPE, - }, + .data = SCI_OF_DATA(PORT_HSCIF, SCIx_HSCIF_REGTYPE), }, { .compatible = "renesas,sci", - .data = &(const struct sci_port_info) { - .type = PORT_SCI, - .regtype = SCIx_SCI_REGTYPE, - }, + .data = SCI_OF_DATA(PORT_SCI, SCIx_SCI_REGTYPE), }, { /* Terminator */ }, @@ -2616,24 +2862,21 @@ sci_parse_dt(struct platform_device *pdev, unsigned int *dev_id) { struct device_node *np = pdev->dev.of_node; const struct of_device_id *match; - const struct sci_port_info *info; struct plat_sci_port *p; int id; if (!IS_ENABLED(CONFIG_OF) || !np) return NULL; - match = of_match_node(of_sci_match, pdev->dev.of_node); + match = of_match_node(of_sci_match, np); if (!match) return NULL; - info = match->data; - p = devm_kzalloc(&pdev->dev, sizeof(struct plat_sci_port), GFP_KERNEL); if (!p) return NULL; - /* Get the line number for the aliases node. */ + /* Get the line number from the aliases node. */ id = of_alias_get_id(np, "serial"); if (id < 0) { dev_err(&pdev->dev, "failed to get alias id (%d)\n", id); @@ -2643,8 +2886,8 @@ sci_parse_dt(struct platform_device *pdev, unsigned int *dev_id) *dev_id = id; p->flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; - p->type = info->type; - p->regtype = info->regtype; + p->type = SCI_OF_TYPE(match->data); + p->regtype = SCI_OF_REGTYPE(match->data); p->scscr = SCSCR_RE | SCSCR_TE; return p; @@ -2714,16 +2957,6 @@ static int sci_probe(struct platform_device *dev) if (ret) return ret; - sp->freq_transition.notifier_call = sci_notifier; - - ret = cpufreq_register_notifier(&sp->freq_transition, - CPUFREQ_TRANSITION_NOTIFIER); - if (unlikely(ret < 0)) { - uart_remove_one_port(&sci_uart_driver, &sp->port); - sci_cleanup_single(sp); - return ret; - } - #ifdef CONFIG_SH_STANDARD_BIOS sh_bios_gdb_detach(); #endif @@ -2789,6 +3022,62 @@ static void __exit sci_exit(void) early_platform_init_buffer("earlyprintk", &sci_driver, early_serial_buf, ARRAY_SIZE(early_serial_buf)); #endif +#ifdef CONFIG_SERIAL_SH_SCI_EARLYCON +static struct __init plat_sci_port port_cfg; + +static int __init early_console_setup(struct earlycon_device *device, + int type) +{ + if (!device->port.membase) + return -ENODEV; + + device->port.serial_in = sci_serial_in; + device->port.serial_out = sci_serial_out; + device->port.type = type; + memcpy(&sci_ports[0].port, &device->port, sizeof(struct uart_port)); + sci_ports[0].cfg = &port_cfg; + sci_ports[0].cfg->type = type; + sci_probe_regmap(sci_ports[0].cfg); + port_cfg.scscr = sci_serial_in(&sci_ports[0].port, SCSCR) | + SCSCR_RE | SCSCR_TE; + sci_serial_out(&sci_ports[0].port, SCSCR, port_cfg.scscr); + + device->con->write = serial_console_write; + return 0; +} +static int __init sci_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + return early_console_setup(device, PORT_SCI); +} +static int __init scif_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + return early_console_setup(device, PORT_SCIF); +} +static int __init scifa_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + return early_console_setup(device, PORT_SCIFA); +} +static int __init scifb_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + return early_console_setup(device, PORT_SCIFB); +} +static int __init hscif_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + return early_console_setup(device, PORT_HSCIF); +} + +OF_EARLYCON_DECLARE(sci, "renesas,sci", sci_early_console_setup); +OF_EARLYCON_DECLARE(scif, "renesas,scif", scif_early_console_setup); +OF_EARLYCON_DECLARE(scifa, "renesas,scifa", scifa_early_console_setup); +OF_EARLYCON_DECLARE(scifb, "renesas,scifb", scifb_early_console_setup); +OF_EARLYCON_DECLARE(hscif, "renesas,hscif", hscif_early_console_setup); +#endif /* CONFIG_SERIAL_SH_SCI_EARLYCON */ + module_init(sci_init); module_exit(sci_exit); diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index bf69bbdcc1f9..7a4fa185b93e 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -27,18 +27,35 @@ enum { HSSRR, /* Sampling Rate Register */ SCPCR, /* Serial Port Control Register */ SCPDR, /* Serial Port Data Register */ + SCDL, /* BRG Frequency Division Register */ + SCCKS, /* BRG Clock Select Register */ SCIx_NR_REGS, }; /* SCSMR (Serial Mode Register) */ +#define SCSMR_C_A BIT(7) /* Communication Mode */ +#define SCSMR_CSYNC BIT(7) /* - Clocked synchronous mode */ +#define SCSMR_ASYNC 0 /* - Asynchronous mode */ #define SCSMR_CHR BIT(6) /* 7-bit Character Length */ #define SCSMR_PE BIT(5) /* Parity Enable */ #define SCSMR_ODD BIT(4) /* Odd Parity */ #define SCSMR_STOP BIT(3) /* Stop Bit Length */ #define SCSMR_CKS 0x0003 /* Clock Select */ +/* Serial Mode Register, SCIFA/SCIFB only bits */ +#define SCSMR_CKEDG BIT(12) /* Transmit/Receive Clock Edge Select */ +#define SCSMR_SRC_MASK 0x0700 /* Sampling Control */ +#define SCSMR_SRC_16 0x0000 /* Sampling rate 1/16 */ +#define SCSMR_SRC_5 0x0100 /* Sampling rate 1/5 */ +#define SCSMR_SRC_7 0x0200 /* Sampling rate 1/7 */ +#define SCSMR_SRC_11 0x0300 /* Sampling rate 1/11 */ +#define SCSMR_SRC_13 0x0400 /* Sampling rate 1/13 */ +#define SCSMR_SRC_17 0x0500 /* Sampling rate 1/17 */ +#define SCSMR_SRC_19 0x0600 /* Sampling rate 1/19 */ +#define SCSMR_SRC_27 0x0700 /* Sampling rate 1/27 */ + /* Serial Control Register, SCIFA/SCIFB only bits */ #define SCSCR_TDRQE BIT(15) /* Tx Data Transfer Request Enable */ #define SCSCR_RDRQE BIT(14) /* Rx Data Transfer Request Enable */ @@ -109,6 +126,14 @@ enum { #define SCPDR_RTSD BIT(4) /* Serial Port RTS Output Pin Data */ #define SCPDR_CTSD BIT(3) /* Serial Port CTS Input Pin Data */ +/* + * BRG Clock Select Register (Some SCIF and HSCIF) + * The Baud Rate Generator for external clock can provide a clock source for + * the sampling clock. It outputs either its frequency divided clock, or the + * (undivided) (H)SCK external clock. + */ +#define SCCKS_CKS BIT(15) /* Select (H)SCK (1) or divided SC_CLK (0) */ +#define SCCKS_XIN BIT(14) /* SC_CLK uses bus clock (1) or SCIF_CLK (0) */ #define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND) #define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF) diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 9dbae01d41ce..18971063f95f 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -517,7 +517,7 @@ static struct uart_ops serial_sprd_ops = { }; #ifdef CONFIG_SERIAL_SPRD_CONSOLE -static inline void wait_for_xmitr(struct uart_port *port) +static void wait_for_xmitr(struct uart_port *port) { unsigned int status, tmout = 10000; @@ -624,8 +624,6 @@ static int __init sprd_early_console_setup( device->con->write = sprd_early_write; return 0; } - -EARLYCON_DECLARE(sprd_serial, sprd_early_console_setup); OF_EARLYCON_DECLARE(sprd_serial, "sprd,sc9836-uart", sprd_early_console_setup); diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index e124d2e88996..9ad98eaa35bf 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1262,7 +1262,7 @@ static int sunsu_kbd_ms_init(struct uart_sunsu_port *up) /* * Wait for transmitter & holding register to empty */ -static __inline__ void wait_for_xmitr(struct uart_sunsu_port *up) +static void wait_for_xmitr(struct uart_sunsu_port *up) { unsigned int status, tmout = 10000; diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index b1c6bd3d483f..c9fdfc8bf47f 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -28,7 +28,7 @@ #define ULITE_NAME "ttyUL" #define ULITE_MAJOR 204 #define ULITE_MINOR 187 -#define ULITE_NR_UARTS 4 +#define ULITE_NR_UARTS 16 /* --------------------------------------------------------------------- * Register definitions @@ -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); } @@ -193,12 +193,15 @@ static int ulite_transmit(struct uart_port *port, int stat) static irqreturn_t ulite_isr(int irq, void *dev_id) { struct uart_port *port = dev_id; - int busy, n = 0; + int stat, busy, n = 0; + unsigned long flags; do { - int stat = uart_in32(ULITE_STATUS, port); + spin_lock_irqsave(&port->lock, flags); + stat = uart_in32(ULITE_STATUS, port); busy = ulite_receive(port, stat); busy |= ulite_transmit(port, stat); + spin_unlock_irqrestore(&port->lock, flags); n++; } while (busy); @@ -259,7 +262,8 @@ static int ulite_startup(struct uart_port *port) { int ret; - ret = request_irq(port->irq, ulite_isr, IRQF_SHARED, "uartlite", port); + ret = request_irq(port->irq, ulite_isr, IRQF_SHARED | IRQF_TRIGGER_RISING, + "uartlite", port); if (ret) return ret; @@ -519,6 +523,47 @@ static int __init ulite_console_init(void) console_initcall(ulite_console_init); +static void early_uartlite_putc(struct uart_port *port, int c) +{ + /* + * Limit how many times we'll spin waiting for TX FIFO status. + * This will prevent lockups if the base address is incorrectly + * set, or any other issue on the UARTLITE. + * This limit is pretty arbitrary, unless we are at about 10 baud + * we'll never timeout on a working UART. + */ + + unsigned retries = 1000000; + /* read status bit - 0x8 offset */ + while (--retries && (readl(port->membase + 8) & (1 << 3))) + ; + + /* Only attempt the iowrite if we didn't timeout */ + /* write to TX_FIFO - 0x4 offset */ + if (retries) + writel(c & 0xff, port->membase + 4); +} + +static void early_uartlite_write(struct console *console, + const char *s, unsigned n) +{ + struct earlycon_device *device = console->data; + uart_console_write(&device->port, s, n, early_uartlite_putc); +} + +static int __init early_uartlite_setup(struct earlycon_device *device, + const char *options) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = early_uartlite_write; + return 0; +} +EARLYCON_DECLARE(uartlite, early_uartlite_setup); +OF_EARLYCON_DECLARE(uartlite_b, "xlnx,opb-uartlite-1.00.b", early_uartlite_setup); +OF_EARLYCON_DECLARE(uartlite_a, "xlnx,xps-uartlite-1.00.a", early_uartlite_setup); + #endif /* CONFIG_SERIAL_UARTLITE_CONSOLE */ static struct uart_driver ulite_uart_driver = { diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index 73190f5d2832..1a7dc3c590b1 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -31,7 +31,7 @@ #include <linux/dma-mapping.h> #include <linux/fs_uart_pd.h> -#include <asm/ucc_slow.h> +#include <soc/fsl/qe/ucc_slow.h> #include <linux/firmware.h> #include <asm/reg.h> diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 4079ec56f5f9..b384060e3b1f 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -485,7 +485,7 @@ static struct uart_driver vt8500_uart_driver; #ifdef CONFIG_SERIAL_VT8500_CONSOLE -static inline void wait_for_xmitr(struct uart_port *port) +static void wait_for_xmitr(struct uart_port *port) { unsigned int status, tmout = 10000; diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 009e0dbc12d2..cd46e64c4255 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -50,24 +50,24 @@ module_param(rx_timeout, uint, S_IRUGO); MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); /* Register offsets for the UART. */ -#define CDNS_UART_CR_OFFSET 0x00 /* Control Register */ -#define CDNS_UART_MR_OFFSET 0x04 /* Mode Register */ -#define CDNS_UART_IER_OFFSET 0x08 /* Interrupt Enable */ -#define CDNS_UART_IDR_OFFSET 0x0C /* Interrupt Disable */ -#define CDNS_UART_IMR_OFFSET 0x10 /* Interrupt Mask */ -#define CDNS_UART_ISR_OFFSET 0x14 /* Interrupt Status */ -#define CDNS_UART_BAUDGEN_OFFSET 0x18 /* Baud Rate Generator */ -#define CDNS_UART_RXTOUT_OFFSET 0x1C /* RX Timeout */ -#define CDNS_UART_RXWM_OFFSET 0x20 /* RX FIFO Trigger Level */ -#define CDNS_UART_MODEMCR_OFFSET 0x24 /* Modem Control */ -#define CDNS_UART_MODEMSR_OFFSET 0x28 /* Modem Status */ -#define CDNS_UART_SR_OFFSET 0x2C /* Channel Status */ -#define CDNS_UART_FIFO_OFFSET 0x30 /* FIFO */ -#define CDNS_UART_BAUDDIV_OFFSET 0x34 /* Baud Rate Divider */ -#define CDNS_UART_FLOWDEL_OFFSET 0x38 /* Flow Delay */ -#define CDNS_UART_IRRX_PWIDTH_OFFSET 0x3C /* IR Min Received Pulse Width */ -#define CDNS_UART_IRTX_PWIDTH_OFFSET 0x40 /* IR Transmitted pulse Width */ -#define CDNS_UART_TXWM_OFFSET 0x44 /* TX FIFO Trigger Level */ +#define CDNS_UART_CR 0x00 /* Control Register */ +#define CDNS_UART_MR 0x04 /* Mode Register */ +#define CDNS_UART_IER 0x08 /* Interrupt Enable */ +#define CDNS_UART_IDR 0x0C /* Interrupt Disable */ +#define CDNS_UART_IMR 0x10 /* Interrupt Mask */ +#define CDNS_UART_ISR 0x14 /* Interrupt Status */ +#define CDNS_UART_BAUDGEN 0x18 /* Baud Rate Generator */ +#define CDNS_UART_RXTOUT 0x1C /* RX Timeout */ +#define CDNS_UART_RXWM 0x20 /* RX FIFO Trigger Level */ +#define CDNS_UART_MODEMCR 0x24 /* Modem Control */ +#define CDNS_UART_MODEMSR 0x28 /* Modem Status */ +#define CDNS_UART_SR 0x2C /* Channel Status */ +#define CDNS_UART_FIFO 0x30 /* FIFO */ +#define CDNS_UART_BAUDDIV 0x34 /* Baud Rate Divider */ +#define CDNS_UART_FLOWDEL 0x38 /* Flow Delay */ +#define CDNS_UART_IRRX_PWIDTH 0x3C /* IR Min Received Pulse Width */ +#define CDNS_UART_IRTX_PWIDTH 0x40 /* IR Transmitted pulse Width */ +#define CDNS_UART_TXWM 0x44 /* TX FIFO Trigger Level */ /* Control Register Bit Definitions */ #define CDNS_UART_CR_STOPBRK 0x00000100 /* Stop TX break */ @@ -126,6 +126,10 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); #define CDNS_UART_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */ #define CDNS_UART_IXR_MASK 0x00001FFF /* Valid bit mask */ +#define CDNS_UART_RX_IRQS (CDNS_UART_IXR_PARITY | CDNS_UART_IXR_FRAMING | \ + CDNS_UART_IXR_OVERRUN | CDNS_UART_IXR_RXTRIG | \ + CDNS_UART_IXR_TOUT) + /* Goes in read_status_mask for break detection as the HW doesn't do it*/ #define CDNS_UART_IXR_BRK 0x80000000 @@ -172,43 +176,22 @@ struct cdns_uart { #define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \ clk_rate_change_nb); -/** - * cdns_uart_isr - Interrupt handler - * @irq: Irq number - * @dev_id: Id of the port - * - * Return: IRQHANDLED - */ -static irqreturn_t cdns_uart_isr(int irq, void *dev_id) +static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus) { - struct uart_port *port = (struct uart_port *)dev_id; - unsigned long flags; - unsigned int isrstatus, numbytes; - unsigned int data; - char status = TTY_NORMAL; - - spin_lock_irqsave(&port->lock, flags); - - /* Read the interrupt status register to determine which - * interrupt(s) is/are active. - */ - isrstatus = readl(port->membase + CDNS_UART_ISR_OFFSET); - /* * There is no hardware break detection, so we interpret framing * error with all-zeros data as a break sequence. Most of the time, * there's another non-zero byte at the end of the sequence. */ if (isrstatus & CDNS_UART_IXR_FRAMING) { - while (!(readl(port->membase + CDNS_UART_SR_OFFSET) & + while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY)) { - if (!readl(port->membase + CDNS_UART_FIFO_OFFSET)) { + if (!readl(port->membase + CDNS_UART_FIFO)) { port->read_status_mask |= CDNS_UART_IXR_BRK; isrstatus &= ~CDNS_UART_IXR_FRAMING; } } - writel(CDNS_UART_IXR_FRAMING, - port->membase + CDNS_UART_ISR_OFFSET); + writel(CDNS_UART_IXR_FRAMING, port->membase + CDNS_UART_ISR); } /* drop byte with parity error if IGNPAR specified */ @@ -218,94 +201,106 @@ static irqreturn_t cdns_uart_isr(int irq, void *dev_id) isrstatus &= port->read_status_mask; isrstatus &= ~port->ignore_status_mask; - if ((isrstatus & CDNS_UART_IXR_TOUT) || - (isrstatus & CDNS_UART_IXR_RXTRIG)) { - /* Receive Timeout Interrupt */ - while (!(readl(port->membase + CDNS_UART_SR_OFFSET) & - CDNS_UART_SR_RXEMPTY)) { - data = readl(port->membase + CDNS_UART_FIFO_OFFSET); - - /* Non-NULL byte after BREAK is garbage (99%) */ - if (data && (port->read_status_mask & - CDNS_UART_IXR_BRK)) { - port->read_status_mask &= ~CDNS_UART_IXR_BRK; - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } + if (!(isrstatus & (CDNS_UART_IXR_TOUT | CDNS_UART_IXR_RXTRIG))) + return; -#ifdef SUPPORT_SYSRQ - /* - * uart_handle_sysrq_char() doesn't work if - * spinlocked, for some reason - */ - if (port->sysrq) { - spin_unlock(&port->lock); - if (uart_handle_sysrq_char(port, - (unsigned char)data)) { - spin_lock(&port->lock); - continue; - } - spin_lock(&port->lock); - } -#endif + while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY)) { + u32 data; + char status = TTY_NORMAL; - port->icount.rx++; + data = readl(port->membase + CDNS_UART_FIFO); - if (isrstatus & CDNS_UART_IXR_PARITY) { - port->icount.parity++; - status = TTY_PARITY; - } else if (isrstatus & CDNS_UART_IXR_FRAMING) { - port->icount.frame++; - status = TTY_FRAME; - } else if (isrstatus & CDNS_UART_IXR_OVERRUN) { - port->icount.overrun++; - } + /* Non-NULL byte after BREAK is garbage (99%) */ + if (data && (port->read_status_mask & CDNS_UART_IXR_BRK)) { + port->read_status_mask &= ~CDNS_UART_IXR_BRK; + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } + + if (uart_handle_sysrq_char(port, data)) + continue; + + port->icount.rx++; - uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN, - data, status); + if (isrstatus & CDNS_UART_IXR_PARITY) { + port->icount.parity++; + status = TTY_PARITY; + } else if (isrstatus & CDNS_UART_IXR_FRAMING) { + port->icount.frame++; + status = TTY_FRAME; + } else if (isrstatus & CDNS_UART_IXR_OVERRUN) { + port->icount.overrun++; } - spin_unlock(&port->lock); - tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); + + uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN, + data, status); } + tty_flip_buffer_push(&port->state->port); +} - /* Dispatch an appropriate handler */ - if ((isrstatus & CDNS_UART_IXR_TXEMPTY) == CDNS_UART_IXR_TXEMPTY) { - if (uart_circ_empty(&port->state->xmit)) { - writel(CDNS_UART_IXR_TXEMPTY, - port->membase + CDNS_UART_IDR_OFFSET); - } else { - numbytes = port->fifosize; - /* Break if no more data available in the UART buffer */ - while (numbytes--) { - if (uart_circ_empty(&port->state->xmit)) - break; - /* Get the data from the UART circular buffer - * and write it to the cdns_uart's TX_FIFO - * register. - */ - writel(port->state->xmit.buf[ - port->state->xmit.tail], - port->membase + CDNS_UART_FIFO_OFFSET); - - port->icount.tx++; - - /* Adjust the tail of the UART buffer and wrap - * the buffer if it reaches limit. - */ - port->state->xmit.tail = - (port->state->xmit.tail + 1) & - (UART_XMIT_SIZE - 1); - } +static void cdns_uart_handle_tx(struct uart_port *port) +{ + unsigned int numbytes; - if (uart_circ_chars_pending( - &port->state->xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - } + if (uart_circ_empty(&port->state->xmit)) { + writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IDR); + return; + } + + numbytes = port->fifosize; + while (numbytes && !uart_circ_empty(&port->state->xmit) && + !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) { + /* + * Get the data from the UART circular buffer + * and write it to the cdns_uart's TX_FIFO + * register. + */ + writel(port->state->xmit.buf[port->state->xmit.tail], + port->membase + CDNS_UART_FIFO); + port->icount.tx++; + + /* + * Adjust the tail of the UART buffer and wrap + * the buffer if it reaches limit. + */ + port->state->xmit.tail = + (port->state->xmit.tail + 1) & (UART_XMIT_SIZE - 1); + + numbytes--; } - writel(isrstatus, port->membase + CDNS_UART_ISR_OFFSET); + if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +/** + * cdns_uart_isr - Interrupt handler + * @irq: Irq number + * @dev_id: Id of the port + * + * Return: IRQHANDLED + */ +static irqreturn_t cdns_uart_isr(int irq, void *dev_id) +{ + struct uart_port *port = (struct uart_port *)dev_id; + unsigned long flags; + unsigned int isrstatus; + + spin_lock_irqsave(&port->lock, flags); + + /* Read the interrupt status register to determine which + * interrupt(s) is/are active. + */ + isrstatus = readl(port->membase + CDNS_UART_ISR); + + if (isrstatus & CDNS_UART_RX_IRQS) + cdns_uart_handle_rx(port, isrstatus); + + if ((isrstatus & CDNS_UART_IXR_TXEMPTY) == CDNS_UART_IXR_TXEMPTY) + cdns_uart_handle_tx(port); + + writel(isrstatus, port->membase + CDNS_UART_ISR); /* be sure to release the lock and tty before leaving */ spin_unlock_irqrestore(&port->lock, flags); @@ -395,14 +390,14 @@ static unsigned int cdns_uart_set_baud_rate(struct uart_port *port, &div8); /* Write new divisors to hardware */ - mreg = readl(port->membase + CDNS_UART_MR_OFFSET); + mreg = readl(port->membase + CDNS_UART_MR); if (div8) mreg |= CDNS_UART_MR_CLKSEL; else mreg &= ~CDNS_UART_MR_CLKSEL; - writel(mreg, port->membase + CDNS_UART_MR_OFFSET); - writel(cd, port->membase + CDNS_UART_BAUDGEN_OFFSET); - writel(bdiv, port->membase + CDNS_UART_BAUDDIV_OFFSET); + writel(mreg, port->membase + CDNS_UART_MR); + writel(cd, port->membase + CDNS_UART_BAUDGEN); + writel(bdiv, port->membase + CDNS_UART_BAUDDIV); cdns_uart->baud = baud; return calc_baud; @@ -449,9 +444,9 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, spin_lock_irqsave(&cdns_uart->port->lock, flags); /* Disable the TX and RX to set baud rate */ - ctrl_reg = readl(port->membase + CDNS_UART_CR_OFFSET); + ctrl_reg = readl(port->membase + CDNS_UART_CR); ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; - writel(ctrl_reg, port->membase + CDNS_UART_CR_OFFSET); + writel(ctrl_reg, port->membase + CDNS_UART_CR); spin_unlock_irqrestore(&cdns_uart->port->lock, flags); @@ -476,11 +471,11 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, spin_lock_irqsave(&cdns_uart->port->lock, flags); /* Set TX/RX Reset */ - ctrl_reg = readl(port->membase + CDNS_UART_CR_OFFSET); + ctrl_reg = readl(port->membase + CDNS_UART_CR); ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; - writel(ctrl_reg, port->membase + CDNS_UART_CR_OFFSET); + writel(ctrl_reg, port->membase + CDNS_UART_CR); - while (readl(port->membase + CDNS_UART_CR_OFFSET) & + while (readl(port->membase + CDNS_UART_CR) & (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) cpu_relax(); @@ -489,11 +484,11 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, * enable bit and RX enable bit to enable the transmitter and * receiver. */ - writel(rx_timeout, port->membase + CDNS_UART_RXTOUT_OFFSET); - ctrl_reg = readl(port->membase + CDNS_UART_CR_OFFSET); + writel(rx_timeout, port->membase + CDNS_UART_RXTOUT); + ctrl_reg = readl(port->membase + CDNS_UART_CR); ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; - writel(ctrl_reg, port->membase + CDNS_UART_CR_OFFSET); + writel(ctrl_reg, port->membase + CDNS_UART_CR); spin_unlock_irqrestore(&cdns_uart->port->lock, flags); @@ -510,43 +505,28 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, */ static void cdns_uart_start_tx(struct uart_port *port) { - unsigned int status, numbytes = port->fifosize; + unsigned int status; - if (uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port)) + if (uart_tx_stopped(port)) return; - status = readl(port->membase + CDNS_UART_CR_OFFSET); - /* Set the TX enable bit and clear the TX disable bit to enable the + /* + * Set the TX enable bit and clear the TX disable bit to enable the * transmitter. */ - writel((status & ~CDNS_UART_CR_TX_DIS) | CDNS_UART_CR_TX_EN, - port->membase + CDNS_UART_CR_OFFSET); + status = readl(port->membase + CDNS_UART_CR); + status &= ~CDNS_UART_CR_TX_DIS; + status |= CDNS_UART_CR_TX_EN; + writel(status, port->membase + CDNS_UART_CR); - while (numbytes-- && ((readl(port->membase + CDNS_UART_SR_OFFSET) & - CDNS_UART_SR_TXFULL)) != CDNS_UART_SR_TXFULL) { - /* Break if no more data available in the UART buffer */ - if (uart_circ_empty(&port->state->xmit)) - break; + if (uart_circ_empty(&port->state->xmit)) + return; - /* Get the data from the UART circular buffer and - * write it to the cdns_uart's TX_FIFO register. - */ - writel(port->state->xmit.buf[port->state->xmit.tail], - port->membase + CDNS_UART_FIFO_OFFSET); - port->icount.tx++; + cdns_uart_handle_tx(port); - /* Adjust the tail of the UART buffer and wrap - * the buffer if it reaches limit. - */ - port->state->xmit.tail = (port->state->xmit.tail + 1) & - (UART_XMIT_SIZE - 1); - } - writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_ISR_OFFSET); + writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_ISR); /* Enable the TX Empty interrupt */ - writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IER_OFFSET); - - if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); + writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IER); } /** @@ -557,10 +537,10 @@ static void cdns_uart_stop_tx(struct uart_port *port) { unsigned int regval; - regval = readl(port->membase + CDNS_UART_CR_OFFSET); + regval = readl(port->membase + CDNS_UART_CR); regval |= CDNS_UART_CR_TX_DIS; /* Disable the transmitter */ - writel(regval, port->membase + CDNS_UART_CR_OFFSET); + writel(regval, port->membase + CDNS_UART_CR); } /** @@ -571,10 +551,13 @@ static void cdns_uart_stop_rx(struct uart_port *port) { unsigned int regval; - regval = readl(port->membase + CDNS_UART_CR_OFFSET); - regval |= CDNS_UART_CR_RX_DIS; + /* Disable RX IRQs */ + writel(CDNS_UART_RX_IRQS, port->membase + CDNS_UART_IDR); + /* Disable the receiver */ - writel(regval, port->membase + CDNS_UART_CR_OFFSET); + regval = readl(port->membase + CDNS_UART_CR); + regval |= CDNS_UART_CR_RX_DIS; + writel(regval, port->membase + CDNS_UART_CR); } /** @@ -587,7 +570,7 @@ static unsigned int cdns_uart_tx_empty(struct uart_port *port) { unsigned int status; - status = readl(port->membase + CDNS_UART_SR_OFFSET) & + status = readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY; return status ? TIOCSER_TEMT : 0; } @@ -605,15 +588,15 @@ static void cdns_uart_break_ctl(struct uart_port *port, int ctl) spin_lock_irqsave(&port->lock, flags); - status = readl(port->membase + CDNS_UART_CR_OFFSET); + status = readl(port->membase + CDNS_UART_CR); if (ctl == -1) writel(CDNS_UART_CR_STARTBRK | status, - port->membase + CDNS_UART_CR_OFFSET); + port->membase + CDNS_UART_CR); else { if ((status & CDNS_UART_CR_STOPBRK) == 0) writel(CDNS_UART_CR_STOPBRK | status, - port->membase + CDNS_UART_CR_OFFSET); + port->membase + CDNS_UART_CR); } spin_unlock_irqrestore(&port->lock, flags); } @@ -636,18 +619,18 @@ static void cdns_uart_set_termios(struct uart_port *port, spin_lock_irqsave(&port->lock, flags); /* Wait for the transmit FIFO to empty before making changes */ - if (!(readl(port->membase + CDNS_UART_CR_OFFSET) & + if (!(readl(port->membase + CDNS_UART_CR) & CDNS_UART_CR_TX_DIS)) { - while (!(readl(port->membase + CDNS_UART_SR_OFFSET) & + while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY)) { cpu_relax(); } } /* Disable the TX and RX to set baud rate */ - ctrl_reg = readl(port->membase + CDNS_UART_CR_OFFSET); + ctrl_reg = readl(port->membase + CDNS_UART_CR); ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; - writel(ctrl_reg, port->membase + CDNS_UART_CR_OFFSET); + writel(ctrl_reg, port->membase + CDNS_UART_CR); /* * Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk @@ -666,20 +649,20 @@ static void cdns_uart_set_termios(struct uart_port *port, uart_update_timeout(port, termios->c_cflag, baud); /* Set TX/RX Reset */ - ctrl_reg = readl(port->membase + CDNS_UART_CR_OFFSET); + ctrl_reg = readl(port->membase + CDNS_UART_CR); ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; - writel(ctrl_reg, port->membase + CDNS_UART_CR_OFFSET); + writel(ctrl_reg, port->membase + CDNS_UART_CR); /* * Clear the RX disable and TX disable bits and then set the TX enable * bit and RX enable bit to enable the transmitter and receiver. */ - ctrl_reg = readl(port->membase + CDNS_UART_CR_OFFSET); + ctrl_reg = readl(port->membase + CDNS_UART_CR); ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; - writel(ctrl_reg, port->membase + CDNS_UART_CR_OFFSET); + writel(ctrl_reg, port->membase + CDNS_UART_CR); - writel(rx_timeout, port->membase + CDNS_UART_RXTOUT_OFFSET); + writel(rx_timeout, port->membase + CDNS_UART_RXTOUT); port->read_status_mask = CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_OVERRUN | CDNS_UART_IXR_TOUT; @@ -699,7 +682,7 @@ static void cdns_uart_set_termios(struct uart_port *port, CDNS_UART_IXR_TOUT | CDNS_UART_IXR_PARITY | CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN; - mode_reg = readl(port->membase + CDNS_UART_MR_OFFSET); + mode_reg = readl(port->membase + CDNS_UART_MR); /* Handling Data Size */ switch (termios->c_cflag & CSIZE) { @@ -740,7 +723,7 @@ static void cdns_uart_set_termios(struct uart_port *port, cval |= CDNS_UART_MR_PARITY_NONE; } cval |= mode_reg & 1; - writel(cval, port->membase + CDNS_UART_MR_OFFSET); + writel(cval, port->membase + CDNS_UART_MR); spin_unlock_irqrestore(&port->lock, flags); } @@ -753,63 +736,67 @@ static void cdns_uart_set_termios(struct uart_port *port, */ static int cdns_uart_startup(struct uart_port *port) { - unsigned int retval = 0, status = 0; + int ret; + unsigned long flags; + unsigned int status = 0; - retval = request_irq(port->irq, cdns_uart_isr, 0, CDNS_UART_NAME, - (void *)port); - if (retval) - return retval; + spin_lock_irqsave(&port->lock, flags); /* Disable the TX and RX */ writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, - port->membase + CDNS_UART_CR_OFFSET); + port->membase + CDNS_UART_CR); /* Set the Control Register with TX/RX Enable, TX/RX Reset, * no break chars. */ writel(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST, - port->membase + CDNS_UART_CR_OFFSET); - - status = readl(port->membase + CDNS_UART_CR_OFFSET); + port->membase + CDNS_UART_CR); - /* Clear the RX disable and TX disable bits and then set the TX enable - * bit and RX enable bit to enable the transmitter and receiver. + /* + * Clear the RX disable bit and then set the RX enable bit to enable + * the receiver. */ - writel((status & ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS)) - | (CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN | - CDNS_UART_CR_STOPBRK), - port->membase + CDNS_UART_CR_OFFSET); + status = readl(port->membase + CDNS_UART_CR); + status &= CDNS_UART_CR_RX_DIS; + status |= CDNS_UART_CR_RX_EN; + writel(status, port->membase + CDNS_UART_CR); /* Set the Mode Register with normal mode,8 data bits,1 stop bit, * no parity. */ writel(CDNS_UART_MR_CHMODE_NORM | CDNS_UART_MR_STOPMODE_1_BIT | CDNS_UART_MR_PARITY_NONE | CDNS_UART_MR_CHARLEN_8_BIT, - port->membase + CDNS_UART_MR_OFFSET); + port->membase + CDNS_UART_MR); /* * Set the RX FIFO Trigger level to use most of the FIFO, but it * can be tuned with a module parameter */ - writel(rx_trigger_level, port->membase + CDNS_UART_RXWM_OFFSET); + writel(rx_trigger_level, port->membase + CDNS_UART_RXWM); /* * Receive Timeout register is enabled but it * can be tuned with a module parameter */ - writel(rx_timeout, port->membase + CDNS_UART_RXTOUT_OFFSET); + writel(rx_timeout, port->membase + CDNS_UART_RXTOUT); /* Clear out any pending interrupts before enabling them */ - writel(readl(port->membase + CDNS_UART_ISR_OFFSET), - port->membase + CDNS_UART_ISR_OFFSET); + writel(readl(port->membase + CDNS_UART_ISR), + port->membase + CDNS_UART_ISR); + + spin_unlock_irqrestore(&port->lock, flags); + + ret = request_irq(port->irq, cdns_uart_isr, 0, CDNS_UART_NAME, port); + if (ret) { + dev_err(port->dev, "request_irq '%d' failed with %d\n", + port->irq, ret); + return ret; + } /* Set the Interrupt Registers with desired interrupts */ - writel(CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_PARITY | - CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN | - CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT, - port->membase + CDNS_UART_IER_OFFSET); + writel(CDNS_UART_RX_IRQS, port->membase + CDNS_UART_IER); - return retval; + return 0; } /** @@ -819,14 +806,21 @@ static int cdns_uart_startup(struct uart_port *port) static void cdns_uart_shutdown(struct uart_port *port) { int status; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); /* Disable interrupts */ - status = readl(port->membase + CDNS_UART_IMR_OFFSET); - writel(status, port->membase + CDNS_UART_IDR_OFFSET); + status = readl(port->membase + CDNS_UART_IMR); + writel(status, port->membase + CDNS_UART_IDR); + writel(0xffffffff, port->membase + CDNS_UART_ISR); /* Disable the TX and RX */ writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, - port->membase + CDNS_UART_CR_OFFSET); + port->membase + CDNS_UART_CR); + + spin_unlock_irqrestore(&port->lock, flags); + free_irq(port->irq, port); } @@ -928,7 +922,7 @@ static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { u32 val; - val = readl(port->membase + CDNS_UART_MODEMCR_OFFSET); + val = readl(port->membase + CDNS_UART_MODEMCR); val &= ~(CDNS_UART_MODEMCR_RTS | CDNS_UART_MODEMCR_DTR); @@ -937,55 +931,46 @@ static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) if (mctrl & TIOCM_DTR) val |= CDNS_UART_MODEMCR_DTR; - writel(val, port->membase + CDNS_UART_MODEMCR_OFFSET); + writel(val, port->membase + CDNS_UART_MODEMCR); } #ifdef CONFIG_CONSOLE_POLL static int cdns_uart_poll_get_char(struct uart_port *port) { - u32 imr; int c; + unsigned long flags; - /* Disable all interrupts */ - imr = readl(port->membase + CDNS_UART_IMR_OFFSET); - writel(imr, port->membase + CDNS_UART_IDR_OFFSET); + spin_lock_irqsave(&port->lock, flags); /* Check if FIFO is empty */ - if (readl(port->membase + CDNS_UART_SR_OFFSET) & CDNS_UART_SR_RXEMPTY) + if (readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY) c = NO_POLL_CHAR; else /* Read a character */ - c = (unsigned char) readl( - port->membase + CDNS_UART_FIFO_OFFSET); + c = (unsigned char) readl(port->membase + CDNS_UART_FIFO); - /* Enable interrupts */ - writel(imr, port->membase + CDNS_UART_IER_OFFSET); + spin_unlock_irqrestore(&port->lock, flags); return c; } static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) { - u32 imr; + unsigned long flags; - /* Disable all interrupts */ - imr = readl(port->membase + CDNS_UART_IMR_OFFSET); - writel(imr, port->membase + CDNS_UART_IDR_OFFSET); + spin_lock_irqsave(&port->lock, flags); /* Wait until FIFO is empty */ - while (!(readl(port->membase + CDNS_UART_SR_OFFSET) & - CDNS_UART_SR_TXEMPTY)) + while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY)) cpu_relax(); /* Write a character */ - writel(c, port->membase + CDNS_UART_FIFO_OFFSET); + writel(c, port->membase + CDNS_UART_FIFO); /* Wait until FIFO is empty */ - while (!(readl(port->membase + CDNS_UART_SR_OFFSET) & - CDNS_UART_SR_TXEMPTY)) + while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY)) cpu_relax(); - /* Enable interrupts */ - writel(imr, port->membase + CDNS_UART_IER_OFFSET); + spin_unlock_irqrestore(&port->lock, flags); return; } @@ -1059,8 +1044,7 @@ static struct uart_port *cdns_uart_get_port(int id) */ static void cdns_uart_console_wait_tx(struct uart_port *port) { - while (!(readl(port->membase + CDNS_UART_SR_OFFSET) & - CDNS_UART_SR_TXEMPTY)) + while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY)) barrier(); } @@ -1072,7 +1056,7 @@ static void cdns_uart_console_wait_tx(struct uart_port *port) static void cdns_uart_console_putchar(struct uart_port *port, int ch) { cdns_uart_console_wait_tx(port); - writel(ch, port->membase + CDNS_UART_FIFO_OFFSET); + writel(ch, port->membase + CDNS_UART_FIFO); } static void __init cdns_early_write(struct console *con, const char *s, @@ -1093,7 +1077,9 @@ static int __init cdns_early_console_setup(struct earlycon_device *device, return 0; } -EARLYCON_DECLARE(cdns, cdns_early_console_setup); +OF_EARLYCON_DECLARE(cdns, "xlnx,xuartps", cdns_early_console_setup); +OF_EARLYCON_DECLARE(cdns, "cdns,uart-r1p8", cdns_early_console_setup); +OF_EARLYCON_DECLARE(cdns, "cdns,uart-r1p12", cdns_early_console_setup); /** * cdns_uart_console_write - perform write operation @@ -1109,30 +1095,33 @@ static void cdns_uart_console_write(struct console *co, const char *s, unsigned int imr, ctrl; int locked = 1; - if (oops_in_progress) + if (port->sysrq) + locked = 0; + else if (oops_in_progress) locked = spin_trylock_irqsave(&port->lock, flags); else spin_lock_irqsave(&port->lock, flags); /* save and disable interrupt */ - imr = readl(port->membase + CDNS_UART_IMR_OFFSET); - writel(imr, port->membase + CDNS_UART_IDR_OFFSET); + imr = readl(port->membase + CDNS_UART_IMR); + writel(imr, port->membase + CDNS_UART_IDR); /* * Make sure that the tx part is enabled. Set the TX enable bit and * clear the TX disable bit to enable the transmitter. */ - ctrl = readl(port->membase + CDNS_UART_CR_OFFSET); - writel((ctrl & ~CDNS_UART_CR_TX_DIS) | CDNS_UART_CR_TX_EN, - port->membase + CDNS_UART_CR_OFFSET); + ctrl = readl(port->membase + CDNS_UART_CR); + ctrl &= ~CDNS_UART_CR_TX_DIS; + ctrl |= CDNS_UART_CR_TX_EN; + writel(ctrl, port->membase + CDNS_UART_CR); uart_console_write(port, s, count, cdns_uart_console_putchar); cdns_uart_console_wait_tx(port); - writel(ctrl, port->membase + CDNS_UART_CR_OFFSET); + writel(ctrl, port->membase + CDNS_UART_CR); /* restore interrupt state */ - writel(imr, port->membase + CDNS_UART_IER_OFFSET); + writel(imr, port->membase + CDNS_UART_IER); if (locked) spin_unlock_irqrestore(&port->lock, flags); @@ -1244,14 +1233,13 @@ static int cdns_uart_suspend(struct device *device) spin_lock_irqsave(&port->lock, flags); /* Empty the receive FIFO 1st before making changes */ - while (!(readl(port->membase + CDNS_UART_SR_OFFSET) & + while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY)) - readl(port->membase + CDNS_UART_FIFO_OFFSET); + readl(port->membase + CDNS_UART_FIFO); /* set RX trigger level to 1 */ - writel(1, port->membase + CDNS_UART_RXWM_OFFSET); + writel(1, port->membase + CDNS_UART_RXWM); /* disable RX timeout interrups */ - writel(CDNS_UART_IXR_TOUT, - port->membase + CDNS_UART_IDR_OFFSET); + writel(CDNS_UART_IXR_TOUT, port->membase + CDNS_UART_IDR); spin_unlock_irqrestore(&port->lock, flags); } @@ -1290,30 +1278,28 @@ static int cdns_uart_resume(struct device *device) spin_lock_irqsave(&port->lock, flags); /* Set TX/RX Reset */ - ctrl_reg = readl(port->membase + CDNS_UART_CR_OFFSET); + ctrl_reg = readl(port->membase + CDNS_UART_CR); ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; - writel(ctrl_reg, port->membase + CDNS_UART_CR_OFFSET); - while (readl(port->membase + CDNS_UART_CR_OFFSET) & + writel(ctrl_reg, port->membase + CDNS_UART_CR); + while (readl(port->membase + CDNS_UART_CR) & (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) cpu_relax(); /* restore rx timeout value */ - writel(rx_timeout, port->membase + CDNS_UART_RXTOUT_OFFSET); + writel(rx_timeout, port->membase + CDNS_UART_RXTOUT); /* Enable Tx/Rx */ - ctrl_reg = readl(port->membase + CDNS_UART_CR_OFFSET); + ctrl_reg = readl(port->membase + CDNS_UART_CR); ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; - writel(ctrl_reg, port->membase + CDNS_UART_CR_OFFSET); + writel(ctrl_reg, port->membase + CDNS_UART_CR); spin_unlock_irqrestore(&port->lock, flags); } else { spin_lock_irqsave(&port->lock, flags); /* restore original rx trigger level */ - writel(rx_trigger_level, - port->membase + CDNS_UART_RXWM_OFFSET); + writel(rx_trigger_level, port->membase + CDNS_UART_RXWM); /* enable RX timeout interrupt */ - writel(CDNS_UART_IXR_TOUT, - port->membase + CDNS_UART_IER_OFFSET); + writel(CDNS_UART_IXR_TOUT, port->membase + CDNS_UART_IER); spin_unlock_irqrestore(&port->lock, flags); } @@ -1406,27 +1392,30 @@ static int cdns_uart_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Cannot get uart_port structure\n"); rc = -ENODEV; goto err_out_notif_unreg; - } else { - /* Register the port. - * This function also registers this device with the tty layer - * and triggers invocation of the config_port() entry point. - */ - port->mapbase = res->start; - port->irq = irq; - port->dev = &pdev->dev; - port->uartclk = clk_get_rate(cdns_uart_data->uartclk); - port->private_data = cdns_uart_data; - cdns_uart_data->port = port; - platform_set_drvdata(pdev, port); - rc = uart_add_one_port(&cdns_uart_uart_driver, port); - if (rc) { - dev_err(&pdev->dev, - "uart_add_one_port() failed; err=%i\n", rc); - goto err_out_notif_unreg; - } - return 0; } + /* + * Register the port. + * This function also registers this device with the tty layer + * and triggers invocation of the config_port() entry point. + */ + port->mapbase = res->start; + port->irq = irq; + port->dev = &pdev->dev; + port->uartclk = clk_get_rate(cdns_uart_data->uartclk); + port->private_data = cdns_uart_data; + cdns_uart_data->port = port; + platform_set_drvdata(pdev, port); + + rc = uart_add_one_port(&cdns_uart_uart_driver, port); + if (rc) { + dev_err(&pdev->dev, + "uart_add_one_port() failed; err=%i\n", rc); + goto err_out_notif_unreg; + } + + return 0; + err_out_notif_unreg: #ifdef CONFIG_COMMON_CLK clk_notifier_unregister(cdns_uart_data->uartclk, diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 2b65bb7ffb8a..eeefd76a30da 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -1181,6 +1181,10 @@ static void zs_console_write(struct console *co, const char *s, if (txint & TxINT_ENAB) { zport->regs[1] |= TxINT_ENAB; write_zsreg(zport, R1, zport->regs[1]); + + /* Resume any transmission as the TxIP bit won't be set. */ + if (!zport->tx_stopped) + zs_raw_transmit_chars(zport); } spin_unlock_irqrestore(&scc->zlock, flags); } diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 6188059fd523..f5476e270734 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -2363,7 +2363,7 @@ static void mgsl_throttle(struct tty_struct * tty) if (I_IXOFF(tty)) mgsl_send_xchar(tty, STOP_CHAR(tty)); - if (tty->termios.c_cflag & CRTSCTS) { + if (C_CRTSCTS(tty)) { spin_lock_irqsave(&info->irq_spinlock,flags); info->serial_signals &= ~SerialSignal_RTS; usc_set_serial_signals(info); @@ -2397,7 +2397,7 @@ static void mgsl_unthrottle(struct tty_struct * tty) mgsl_send_xchar(tty, START_CHAR(tty)); } - if (tty->termios.c_cflag & CRTSCTS) { + if (C_CRTSCTS(tty)) { spin_lock_irqsave(&info->irq_spinlock,flags); info->serial_signals |= SerialSignal_RTS; usc_set_serial_signals(info); @@ -3039,30 +3039,25 @@ static void mgsl_set_termios(struct tty_struct *tty, struct ktermios *old_termio mgsl_change_params(info); /* Handle transition to B0 status */ - if (old_termios->c_cflag & CBAUD && - !(tty->termios.c_cflag & CBAUD)) { + if ((old_termios->c_cflag & CBAUD) && !C_BAUD(tty)) { info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); spin_lock_irqsave(&info->irq_spinlock,flags); usc_set_serial_signals(info); spin_unlock_irqrestore(&info->irq_spinlock,flags); } - + /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && - tty->termios.c_cflag & CBAUD) { + if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) { info->serial_signals |= SerialSignal_DTR; - if (!(tty->termios.c_cflag & CRTSCTS) || - !test_bit(TTY_THROTTLED, &tty->flags)) { + if (!C_CRTSCTS(tty) || !test_bit(TTY_THROTTLED, &tty->flags)) info->serial_signals |= SerialSignal_RTS; - } spin_lock_irqsave(&info->irq_spinlock,flags); usc_set_serial_signals(info); spin_unlock_irqrestore(&info->irq_spinlock,flags); } - + /* Handle turning off CRTSCTS */ - if (old_termios->c_cflag & CRTSCTS && - !(tty->termios.c_cflag & CRTSCTS)) { + if (old_termios->c_cflag & CRTSCTS && !C_CRTSCTS(tty)) { tty->hw_stopped = 0; mgsl_start(tty); } @@ -3281,7 +3276,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, return 0; } - if (tty->termios.c_cflag & CLOCAL) + if (C_CLOCAL(tty)) do_clocal = true; /* Wait for carrier detect and the line to become diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 6fc39fbfc275..c0a2f5a1b1c2 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -89,7 +89,7 @@ * module identification */ static char *driver_name = "SyncLink GT"; -static char *tty_driver_name = "synclink_gt"; +static char *slgt_driver_name = "synclink_gt"; static char *tty_dev_prefix = "ttySLG"; MODULE_LICENSE("GPL"); #define MGSL_MAGIC 0x5401 @@ -774,8 +774,7 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) change_params(info); /* Handle transition to B0 status */ - if (old_termios->c_cflag & CBAUD && - !(tty->termios.c_cflag & CBAUD)) { + if ((old_termios->c_cflag & CBAUD) && !C_BAUD(tty)) { info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); spin_lock_irqsave(&info->lock,flags); set_signals(info); @@ -783,21 +782,17 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) } /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && - tty->termios.c_cflag & CBAUD) { + if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) { info->signals |= SerialSignal_DTR; - if (!(tty->termios.c_cflag & CRTSCTS) || - !test_bit(TTY_THROTTLED, &tty->flags)) { + if (!C_CRTSCTS(tty) || !test_bit(TTY_THROTTLED, &tty->flags)) info->signals |= SerialSignal_RTS; - } spin_lock_irqsave(&info->lock,flags); set_signals(info); spin_unlock_irqrestore(&info->lock,flags); } /* Handle turning off CRTSCTS */ - if (old_termios->c_cflag & CRTSCTS && - !(tty->termios.c_cflag & CRTSCTS)) { + if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { tty->hw_stopped = 0; tx_release(tty); } @@ -1362,7 +1357,7 @@ static void throttle(struct tty_struct * tty) DBGINFO(("%s throttle\n", info->device_name)); if (I_IXOFF(tty)) send_xchar(tty, STOP_CHAR(tty)); - if (tty->termios.c_cflag & CRTSCTS) { + if (C_CRTSCTS(tty)) { spin_lock_irqsave(&info->lock,flags); info->signals &= ~SerialSignal_RTS; set_signals(info); @@ -1387,7 +1382,7 @@ static void unthrottle(struct tty_struct * tty) else send_xchar(tty, START_CHAR(tty)); } - if (tty->termios.c_cflag & CRTSCTS) { + if (C_CRTSCTS(tty)) { spin_lock_irqsave(&info->lock,flags); info->signals |= SerialSignal_RTS; set_signals(info); @@ -3280,7 +3275,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, return 0; } - if (tty->termios.c_cflag & CLOCAL) + if (C_CLOCAL(tty)) do_clocal = true; /* Wait for carrier detect and the line to become @@ -3799,7 +3794,7 @@ static int __init slgt_init(void) /* Initialize the tty_driver structure */ - serial_driver->driver_name = tty_driver_name; + serial_driver->driver_name = slgt_driver_name; serial_driver->name = tty_dev_prefix; serial_driver->major = ttymajor; serial_driver->minor_start = 64; diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index fb00a06dfa4b..90da0c712262 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -549,8 +549,8 @@ static int tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static int set_break(struct tty_struct *tty, int break_state); -static void add_device(SLMP_INFO *info); -static void device_init(int adapter_num, struct pci_dev *pdev); +static int add_device(SLMP_INFO *info); +static int device_init(int adapter_num, struct pci_dev *pdev); static int claim_resources(SLMP_INFO *info); static void release_resources(SLMP_INFO *info); @@ -871,8 +871,7 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) change_params(info); /* Handle transition to B0 status */ - if (old_termios->c_cflag & CBAUD && - !(tty->termios.c_cflag & CBAUD)) { + if ((old_termios->c_cflag & CBAUD) && !C_BAUD(tty)) { info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); spin_lock_irqsave(&info->lock,flags); set_signals(info); @@ -880,21 +879,17 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) } /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && - tty->termios.c_cflag & CBAUD) { + if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) { info->serial_signals |= SerialSignal_DTR; - if (!(tty->termios.c_cflag & CRTSCTS) || - !test_bit(TTY_THROTTLED, &tty->flags)) { + if (!C_CRTSCTS(tty) || !test_bit(TTY_THROTTLED, &tty->flags)) info->serial_signals |= SerialSignal_RTS; - } spin_lock_irqsave(&info->lock,flags); set_signals(info); spin_unlock_irqrestore(&info->lock,flags); } /* Handle turning off CRTSCTS */ - if (old_termios->c_cflag & CRTSCTS && - !(tty->termios.c_cflag & CRTSCTS)) { + if (old_termios->c_cflag & CRTSCTS && !C_CRTSCTS(tty)) { tty->hw_stopped = 0; tx_release(tty); } @@ -1472,7 +1467,7 @@ static void throttle(struct tty_struct * tty) if (I_IXOFF(tty)) send_xchar(tty, STOP_CHAR(tty)); - if (tty->termios.c_cflag & CRTSCTS) { + if (C_CRTSCTS(tty)) { spin_lock_irqsave(&info->lock,flags); info->serial_signals &= ~SerialSignal_RTS; set_signals(info); @@ -1501,7 +1496,7 @@ static void unthrottle(struct tty_struct * tty) send_xchar(tty, START_CHAR(tty)); } - if (tty->termios.c_cflag & CRTSCTS) { + if (C_CRTSCTS(tty)) { spin_lock_irqsave(&info->lock,flags); info->serial_signals |= SerialSignal_RTS; set_signals(info); @@ -3297,7 +3292,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, return 0; } - if (tty->termios.c_cflag & CLOCAL) + if (C_CLOCAL(tty)) do_clocal = true; /* Wait for carrier detect and the line to become @@ -3693,7 +3688,7 @@ static void release_resources(SLMP_INFO *info) /* Add the specified device instance data structure to the * global linked list of devices and increment the device count. */ -static void add_device(SLMP_INFO *info) +static int add_device(SLMP_INFO *info) { info->next_device = NULL; info->line = synclinkmp_device_count; @@ -3731,7 +3726,9 @@ static void add_device(SLMP_INFO *info) info->max_frame_size ); #if SYNCLINK_GENERIC_HDLC - hdlcdev_init(info); + return hdlcdev_init(info); +#else + return 0; #endif } @@ -3820,10 +3817,10 @@ static SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev) return info; } -static void device_init(int adapter_num, struct pci_dev *pdev) +static int device_init(int adapter_num, struct pci_dev *pdev) { SLMP_INFO *port_array[SCA_MAX_PORTS]; - int port; + int port, rc; /* allocate device instances for up to SCA_MAX_PORTS devices */ for ( port = 0; port < SCA_MAX_PORTS; ++port ) { @@ -3833,14 +3830,16 @@ static void device_init(int adapter_num, struct pci_dev *pdev) tty_port_destroy(&port_array[port]->port); kfree(port_array[port]); } - return; + return -ENOMEM; } } /* give copy of port_array to all ports and add to device list */ for ( port = 0; port < SCA_MAX_PORTS; ++port ) { memcpy(port_array[port]->port_array,port_array,sizeof(port_array)); - add_device( port_array[port] ); + rc = add_device( port_array[port] ); + if (rc) + goto err_add; spin_lock_init(&port_array[port]->lock); } @@ -3860,21 +3859,30 @@ static void device_init(int adapter_num, struct pci_dev *pdev) alloc_dma_bufs(port_array[port]); } - if ( request_irq(port_array[0]->irq_level, + rc = request_irq(port_array[0]->irq_level, synclinkmp_interrupt, port_array[0]->irq_flags, port_array[0]->device_name, - port_array[0]) < 0 ) { + port_array[0]); + if ( rc ) { printk( "%s(%d):%s Can't request interrupt, IRQ=%d\n", __FILE__,__LINE__, port_array[0]->device_name, port_array[0]->irq_level ); + goto err_irq; } - else { - port_array[0]->irq_requested = true; - adapter_test(port_array[0]); - } + port_array[0]->irq_requested = true; + adapter_test(port_array[0]); } + return 0; +err_irq: + release_resources( port_array[0] ); +err_add: + for ( port = 0; port < SCA_MAX_PORTS; ++port ) { + tty_port_destroy(&port_array[port]->port); + kfree(port_array[port]); + } + return rc; } static const struct tty_operations ops = { @@ -5589,8 +5597,7 @@ static int synclinkmp_init_one (struct pci_dev *dev, printk("error enabling pci device %p\n", dev); return -EIO; } - device_init( ++synclinkmp_adapter_count, dev ); - return 0; + return device_init( ++synclinkmp_adapter_count, dev ); } static void synclinkmp_remove_one (struct pci_dev *dev) diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index 3d245cd3d8e6..df2d735338e2 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -14,16 +14,23 @@ #include <linux/tty.h> struct tty_audit_buf { - atomic_t count; struct mutex mutex; /* Protects all data below */ - int major, minor; /* The TTY which the data is from */ + dev_t dev; /* The TTY which the data is from */ unsigned icanon:1; size_t valid; unsigned char *data; /* Allocated size N_TTY_BUF_SIZE */ }; -static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor, - unsigned icanon) +static struct tty_audit_buf *tty_audit_buf_ref(void) +{ + struct tty_audit_buf *buf; + + buf = current->signal->tty_audit_buf; + WARN_ON(buf == ERR_PTR(-ESRCH)); + return buf; +} + +static struct tty_audit_buf *tty_audit_buf_alloc(void) { struct tty_audit_buf *buf; @@ -33,11 +40,9 @@ static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor, buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL); if (!buf->data) goto err_buf; - atomic_set(&buf->count, 1); mutex_init(&buf->mutex); - buf->major = major; - buf->minor = minor; - buf->icanon = icanon; + buf->dev = MKDEV(0, 0); + buf->icanon = 0; buf->valid = 0; return buf; @@ -54,13 +59,7 @@ static void tty_audit_buf_free(struct tty_audit_buf *buf) kfree(buf); } -static void tty_audit_buf_put(struct tty_audit_buf *buf) -{ - if (atomic_dec_and_test(&buf->count)) - tty_audit_buf_free(buf); -} - -static void tty_audit_log(const char *description, int major, int minor, +static void tty_audit_log(const char *description, dev_t dev, unsigned char *data, size_t size) { struct audit_buffer *ab; @@ -76,7 +75,7 @@ static void tty_audit_log(const char *description, int major, int minor, audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d" " minor=%d comm=", description, pid, uid, - loginuid, sessionid, major, minor); + loginuid, sessionid, MAJOR(dev), MINOR(dev)); get_task_comm(name, tsk); audit_log_untrustedstring(ab, name); audit_log_format(ab, " data="); @@ -99,7 +98,7 @@ static void tty_audit_buf_push(struct tty_audit_buf *buf) buf->valid = 0; return; } - tty_audit_log("tty", buf->major, buf->minor, buf->data, buf->valid); + tty_audit_log("tty", buf->dev, buf->data, buf->valid); buf->valid = 0; } @@ -108,21 +107,20 @@ static void tty_audit_buf_push(struct tty_audit_buf *buf) * * Make sure all buffered data is written out and deallocate the buffer. * Only needs to be called if current->signal->tty_audit_buf != %NULL. + * + * The process is single-threaded at this point; no other threads share + * current->signal. */ void tty_audit_exit(void) { struct tty_audit_buf *buf; - buf = current->signal->tty_audit_buf; - current->signal->tty_audit_buf = NULL; + buf = xchg(¤t->signal->tty_audit_buf, ERR_PTR(-ESRCH)); if (!buf) return; - mutex_lock(&buf->mutex); tty_audit_buf_push(buf); - mutex_unlock(&buf->mutex); - - tty_audit_buf_put(buf); + tty_audit_buf_free(buf); } /** @@ -133,7 +131,6 @@ void tty_audit_exit(void) void tty_audit_fork(struct signal_struct *sig) { sig->audit_tty = current->signal->audit_tty; - sig->audit_tty_log_passwd = current->signal->audit_tty_log_passwd; } /** @@ -141,123 +138,62 @@ void tty_audit_fork(struct signal_struct *sig) */ void tty_audit_tiocsti(struct tty_struct *tty, char ch) { - struct tty_audit_buf *buf; - int major, minor, should_audit; - unsigned long flags; + dev_t dev; - spin_lock_irqsave(¤t->sighand->siglock, flags); - should_audit = current->signal->audit_tty; - buf = current->signal->tty_audit_buf; - if (buf) - atomic_inc(&buf->count); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - - major = tty->driver->major; - minor = tty->driver->minor_start + tty->index; - if (buf) { - mutex_lock(&buf->mutex); - if (buf->major == major && buf->minor == minor) - tty_audit_buf_push(buf); - mutex_unlock(&buf->mutex); - tty_audit_buf_put(buf); - } - - if (should_audit && audit_enabled) { - kuid_t auid; - unsigned int sessionid; + dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index; + if (tty_audit_push()) + return; - auid = audit_get_loginuid(current); - sessionid = audit_get_sessionid(current); - tty_audit_log("ioctl=TIOCSTI", major, minor, &ch, 1); - } + if (audit_enabled) + tty_audit_log("ioctl=TIOCSTI", dev, &ch, 1); } /** - * tty_audit_push_current - Flush current's pending audit data + * tty_audit_push - Flush current's pending audit data * - * Try to lock sighand and get a reference to the tty audit buffer if available. - * Flush the buffer or return an appropriate error code. + * Returns 0 if success, -EPERM if tty audit is disabled */ -int tty_audit_push_current(void) +int tty_audit_push(void) { - struct tty_audit_buf *buf = ERR_PTR(-EPERM); - struct task_struct *tsk = current; - unsigned long flags; + struct tty_audit_buf *buf; - if (!lock_task_sighand(tsk, &flags)) - return -ESRCH; + if (~current->signal->audit_tty & AUDIT_TTY_ENABLE) + return -EPERM; - if (tsk->signal->audit_tty) { - buf = tsk->signal->tty_audit_buf; - if (buf) - atomic_inc(&buf->count); + buf = tty_audit_buf_ref(); + if (!IS_ERR_OR_NULL(buf)) { + mutex_lock(&buf->mutex); + tty_audit_buf_push(buf); + mutex_unlock(&buf->mutex); } - unlock_task_sighand(tsk, &flags); - - /* - * Return 0 when signal->audit_tty set - * but tsk->signal->tty_audit_buf == NULL. - */ - if (!buf || IS_ERR(buf)) - return PTR_ERR(buf); - - mutex_lock(&buf->mutex); - tty_audit_buf_push(buf); - mutex_unlock(&buf->mutex); - - tty_audit_buf_put(buf); return 0; } /** * tty_audit_buf_get - Get an audit buffer. * - * Get an audit buffer for @tty, allocate it if necessary. Return %NULL - * if TTY auditing is disabled or out of memory. Otherwise, return a new - * reference to the buffer. + * Get an audit buffer, allocate it if necessary. Return %NULL + * if out of memory or ERR_PTR(-ESRCH) if tty_audit_exit() has already + * occurred. Otherwise, return a new reference to the buffer. */ -static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, - unsigned icanon) +static struct tty_audit_buf *tty_audit_buf_get(void) { - struct tty_audit_buf *buf, *buf2; - unsigned long flags; - - buf = NULL; - buf2 = NULL; - spin_lock_irqsave(¤t->sighand->siglock, flags); - if (likely(!current->signal->audit_tty)) - goto out; - buf = current->signal->tty_audit_buf; - if (buf) { - atomic_inc(&buf->count); - goto out; - } - spin_unlock_irqrestore(¤t->sighand->siglock, flags); + struct tty_audit_buf *buf; + + buf = tty_audit_buf_ref(); + if (buf) + return buf; - buf2 = tty_audit_buf_alloc(tty->driver->major, - tty->driver->minor_start + tty->index, - icanon); - if (buf2 == NULL) { + buf = tty_audit_buf_alloc(); + if (buf == NULL) { audit_log_lost("out of memory in TTY auditing"); return NULL; } - spin_lock_irqsave(¤t->sighand->siglock, flags); - if (!current->signal->audit_tty) - goto out; - buf = current->signal->tty_audit_buf; - if (!buf) { - current->signal->tty_audit_buf = buf2; - buf = buf2; - buf2 = NULL; - } - atomic_inc(&buf->count); - /* Fall through */ - out: - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - if (buf2) - tty_audit_buf_free(buf2); - return buf; + /* Race to use this buffer, free it if another wins */ + if (cmpxchg(¤t->signal->tty_audit_buf, NULL, buf) != NULL) + tty_audit_buf_free(buf); + return tty_audit_buf_ref(); } /** @@ -265,39 +201,36 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, * * Audit @data of @size from @tty, if necessary. */ -void tty_audit_add_data(struct tty_struct *tty, const void *data, - size_t size, unsigned icanon) +void tty_audit_add_data(struct tty_struct *tty, const void *data, size_t size) { struct tty_audit_buf *buf; - int major, minor; - int audit_log_tty_passwd; - unsigned long flags; + unsigned int icanon = !!L_ICANON(tty); + unsigned int audit_tty; + dev_t dev; - if (unlikely(size == 0)) + audit_tty = READ_ONCE(current->signal->audit_tty); + if (~audit_tty & AUDIT_TTY_ENABLE) return; - spin_lock_irqsave(¤t->sighand->siglock, flags); - audit_log_tty_passwd = current->signal->audit_tty_log_passwd; - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - if (!audit_log_tty_passwd && icanon && !L_ECHO(tty)) + if (unlikely(size == 0)) return; if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) return; - buf = tty_audit_buf_get(tty, icanon); - if (!buf) + if ((~audit_tty & AUDIT_TTY_LOG_PASSWD) && icanon && !L_ECHO(tty)) + return; + + buf = tty_audit_buf_get(); + if (IS_ERR_OR_NULL(buf)) return; mutex_lock(&buf->mutex); - major = tty->driver->major; - minor = tty->driver->minor_start + tty->index; - if (buf->major != major || buf->minor != minor - || buf->icanon != icanon) { + dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index; + if (buf->dev != dev || buf->icanon != icanon) { tty_audit_buf_push(buf); - buf->major = major; - buf->minor = minor; + buf->dev = dev; buf->icanon = icanon; } do { @@ -314,38 +247,4 @@ void tty_audit_add_data(struct tty_struct *tty, const void *data, tty_audit_buf_push(buf); } while (size != 0); mutex_unlock(&buf->mutex); - tty_audit_buf_put(buf); -} - -/** - * tty_audit_push - Push buffered data out - * - * Make sure no audit data is pending for @tty on the current process. - */ -void tty_audit_push(struct tty_struct *tty) -{ - struct tty_audit_buf *buf; - unsigned long flags; - - spin_lock_irqsave(¤t->sighand->siglock, flags); - if (likely(!current->signal->audit_tty)) { - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - return; - } - buf = current->signal->tty_audit_buf; - if (buf) - atomic_inc(&buf->count); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - - if (buf) { - int major, minor; - - major = tty->driver->major; - minor = tty->driver->minor_start + tty->index; - mutex_lock(&buf->mutex); - if (buf->major == major && buf->minor == minor) - tty_audit_buf_push(buf); - mutex_unlock(&buf->mutex); - tty_audit_buf_put(buf); - } } diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 3cd31e0d4bd9..a946e49a2626 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -435,25 +435,42 @@ int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars, } EXPORT_SYMBOL_GPL(tty_prepare_flip_string); +/** + * tty_ldisc_receive_buf - forward data to line discipline + * @ld: line discipline to process input + * @p: char buffer + * @f: TTY_* flags buffer + * @count: number of bytes to process + * + * Callers other than flush_to_ldisc() need to exclude the kworker + * from concurrent use of the line discipline, see paste_selection(). + * + * Returns the number of bytes not processed + */ +int tty_ldisc_receive_buf(struct tty_ldisc *ld, unsigned char *p, + char *f, int count) +{ + if (ld->ops->receive_buf2) + count = ld->ops->receive_buf2(ld->tty, p, f, count); + else { + count = min_t(int, count, ld->tty->receive_room); + if (count && ld->ops->receive_buf) + ld->ops->receive_buf(ld->tty, p, f, count); + } + return count; +} +EXPORT_SYMBOL_GPL(tty_ldisc_receive_buf); static int -receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) +receive_buf(struct tty_ldisc *ld, struct tty_buffer *head, int count) { - struct tty_ldisc *disc = tty->ldisc; unsigned char *p = char_buf_ptr(head, head->read); char *f = NULL; if (~head->flags & TTYB_NORMAL) f = flag_buf_ptr(head, head->read); - if (disc->ops->receive_buf2) - count = disc->ops->receive_buf2(tty, p, f, count); - else { - count = min_t(int, count, tty->receive_room); - if (count && disc->ops->receive_buf) - disc->ops->receive_buf(tty, p, f, count); - } - return count; + return tty_ldisc_receive_buf(ld, p, f, count); } /** @@ -514,7 +531,7 @@ static void flush_to_ldisc(struct work_struct *work) continue; } - count = receive_buf(tty, head, count); + count = receive_buf(disc, head, count); if (!count) break; head->read += count; diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index bcc8e1e8bb72..8d26ed79bb4c 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -123,7 +123,8 @@ struct ktermios tty_std_termios = { /* for the benefit of tty drivers */ ECHOCTL | ECHOKE | IEXTEN, .c_cc = INIT_C_CC, .c_ispeed = 38400, - .c_ospeed = 38400 + .c_ospeed = 38400, + /* .c_line = N_TTY, */ }; EXPORT_SYMBOL(tty_std_termios); @@ -134,13 +135,8 @@ EXPORT_SYMBOL(tty_std_termios); LIST_HEAD(tty_drivers); /* linked list of tty drivers */ -/* Mutex to protect creating and releasing a tty. This is shared with - vt.c for deeply disgusting hack reasons */ +/* Mutex to protect creating and releasing a tty */ DEFINE_MUTEX(tty_mutex); -EXPORT_SYMBOL(tty_mutex); - -/* Spinlock to protect the tty->tty_files list */ -DEFINE_SPINLOCK(tty_files_lock); static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *); static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *); @@ -168,10 +164,9 @@ static void release_tty(struct tty_struct *tty, int idx); * Locking: none. Must be called after tty is definitely unused */ -void free_tty_struct(struct tty_struct *tty) +static void free_tty_struct(struct tty_struct *tty) { - if (!tty) - return; + tty_ldisc_deinit(tty); put_device(tty->dev); kfree(tty->write_buf); tty->magic = 0xDEADDEAD; @@ -204,9 +199,9 @@ void tty_add_file(struct tty_struct *tty, struct file *file) priv->tty = tty; priv->file = file; - spin_lock(&tty_files_lock); + spin_lock(&tty->files_lock); list_add(&priv->list, &tty->tty_files); - spin_unlock(&tty_files_lock); + spin_unlock(&tty->files_lock); } /** @@ -227,10 +222,11 @@ void tty_free_file(struct file *file) static void tty_del_file(struct file *file) { struct tty_file_private *priv = file->private_data; + struct tty_struct *tty = priv->tty; - spin_lock(&tty_files_lock); + spin_lock(&tty->files_lock); list_del(&priv->list); - spin_unlock(&tty_files_lock); + spin_unlock(&tty->files_lock); tty_free_file(file); } @@ -256,19 +252,24 @@ const char *tty_name(const struct tty_struct *tty) EXPORT_SYMBOL(tty_name); -int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, +const char *tty_driver_name(const struct tty_struct *tty) +{ + if (!tty || !tty->driver) + return ""; + return tty->driver->name; +} + +static int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, const char *routine) { #ifdef TTY_PARANOIA_CHECK if (!tty) { - printk(KERN_WARNING - "null TTY for (%d:%d) in %s\n", + pr_warn("(%d:%d): %s: NULL tty\n", imajor(inode), iminor(inode), routine); return 1; } if (tty->magic != TTY_MAGIC) { - printk(KERN_WARNING - "bad magic number for tty struct (%d:%d) in %s\n", + pr_warn("(%d:%d): %s: bad magic number\n", imajor(inode), iminor(inode), routine); return 1; } @@ -283,19 +284,18 @@ static int check_tty_count(struct tty_struct *tty, const char *routine) struct list_head *p; int count = 0; - spin_lock(&tty_files_lock); + spin_lock(&tty->files_lock); list_for_each(p, &tty->tty_files) { count++; } - spin_unlock(&tty_files_lock); + spin_unlock(&tty->files_lock); if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_SLAVE && tty->link && tty->link->count) count++; if (tty->count != count) { - printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) " - "!= #fd's(%d) in %s\n", - tty->name, tty->count, count, routine); + tty_warn(tty, "%s: tty->count(%d) != #fd's(%d)\n", + routine, tty->count, count); return count; } #endif @@ -379,6 +379,12 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line) EXPORT_SYMBOL_GPL(tty_find_polling_driver); #endif +static int is_ignored(int sig) +{ + return (sigismember(¤t->blocked, sig) || + current->sighand->action[sig-1].sa.sa_handler == SIG_IGN); +} + /** * tty_check_change - check for POSIX terminal changes * @tty: tty to check @@ -420,10 +426,8 @@ int __tty_check_change(struct tty_struct *tty, int sig) } rcu_read_unlock(); - if (!tty_pgrp) { - pr_warn("%s: tty_check_change: sig=%d, tty->pgrp == NULL!\n", - tty_name(tty), sig); - } + if (!tty_pgrp) + tty_warn(tty, "sig=%d, tty->pgrp == NULL!\n", sig); return ret; } @@ -464,6 +468,11 @@ static long hung_up_tty_compat_ioctl(struct file *file, return cmd == TIOCSPGRP ? -ENOTTY : -EIO; } +static int hung_up_tty_fasync(int fd, struct file *file, int on) +{ + return -ENOTTY; +} + static const struct file_operations tty_fops = { .llseek = no_llseek, .read = tty_read, @@ -496,6 +505,7 @@ static const struct file_operations hung_up_tty_fops = { .unlocked_ioctl = hung_up_tty_ioctl, .compat_ioctl = hung_up_tty_compat_ioctl, .release = tty_release, + .fasync = hung_up_tty_fasync, }; static DEFINE_SPINLOCK(redirect_lock); @@ -707,7 +717,7 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) workqueue with the lock held */ check_tty_count(tty, "tty_hangup"); - spin_lock(&tty_files_lock); + spin_lock(&tty->files_lock); /* This breaks for file handles being sent over AF_UNIX sockets ? */ list_for_each_entry(priv, &tty->tty_files, list) { filp = priv->file; @@ -719,14 +729,14 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) __tty_fasync(-1, filp, 0); /* can't block */ filp->f_op = &hung_up_tty_fops; } - spin_unlock(&tty_files_lock); + spin_unlock(&tty->files_lock); refs = tty_signal_session_leader(tty, exit_session); /* Account for the p->signal references we killed */ while (refs--) tty_kref_put(tty); - tty_ldisc_hangup(tty); + tty_ldisc_hangup(tty, cons_filp != NULL); spin_lock_irq(&tty->ctrl_lock); clear_bit(TTY_THROTTLED, &tty->flags); @@ -751,10 +761,9 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) } else if (tty->ops->hangup) tty->ops->hangup(tty); /* - * We don't want to have driver/ldisc interactions beyond - * the ones we did here. The driver layer expects no - * calls after ->hangup() from the ldisc side. However we - * can't yet guarantee all that. + * We don't want to have driver/ldisc interactions beyond the ones + * we did here. The driver layer expects no calls after ->hangup() + * from the ldisc side, which is now guaranteed. */ set_bit(TTY_HUPPED, &tty->flags); tty_unlock(tty); @@ -781,7 +790,7 @@ static void do_tty_hangup(struct work_struct *work) void tty_hangup(struct tty_struct *tty) { - tty_debug_hangup(tty, "\n"); + tty_debug_hangup(tty, "hangup\n"); schedule_work(&tty->hangup_work); } @@ -798,7 +807,7 @@ EXPORT_SYMBOL(tty_hangup); void tty_vhangup(struct tty_struct *tty) { - tty_debug_hangup(tty, "\n"); + tty_debug_hangup(tty, "vhangup\n"); __tty_hangup(tty, 0); } @@ -835,7 +844,7 @@ void tty_vhangup_self(void) static void tty_vhangup_session(struct tty_struct *tty) { - tty_debug_hangup(tty, "\n"); + tty_debug_hangup(tty, "session hangup\n"); __tty_hangup(tty, 1); } @@ -1067,6 +1076,8 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, /* We want to wait for the line discipline to sort out in this situation */ ld = tty_ldisc_ref_wait(tty); + if (!ld) + return hung_up_tty_read(file, buf, count, ppos); if (ld->ops->read) i = ld->ops->read(tty, file, buf, count); else @@ -1239,9 +1250,10 @@ static ssize_t tty_write(struct file *file, const char __user *buf, return -EIO; /* Short term debug to catch buggy drivers */ if (tty->ops->write_room == NULL) - printk(KERN_ERR "tty driver %s lacks a write_room method.\n", - tty->driver->name); + tty_err(tty, "missing write_room method\n"); ld = tty_ldisc_ref_wait(tty); + if (!ld) + return hung_up_tty_write(file, buf, count, ppos); if (!ld->ops->write) ret = -EIO; else @@ -1377,7 +1389,7 @@ static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, * the tty_mutex currently so we can be relaxed about ordering. */ -int tty_init_termios(struct tty_struct *tty) +void tty_init_termios(struct tty_struct *tty) { struct ktermios *tp; int idx = tty->index; @@ -1387,24 +1399,21 @@ int tty_init_termios(struct tty_struct *tty) else { /* Check for lazy saved data */ tp = tty->driver->termios[idx]; - if (tp != NULL) + if (tp != NULL) { tty->termios = *tp; - else + tty->termios.c_line = tty->driver->init_termios.c_line; + } else tty->termios = tty->driver->init_termios; } /* Compatibility until drivers always set this */ tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios); tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios); - return 0; } EXPORT_SYMBOL_GPL(tty_init_termios); int tty_standard_install(struct tty_driver *driver, struct tty_struct *tty) { - int ret = tty_init_termios(tty); - if (ret) - return ret; - + tty_init_termios(tty); tty_driver_kref_get(driver); tty->count++; driver->ttys[tty->index] = tty; @@ -1441,7 +1450,7 @@ static int tty_driver_install_tty(struct tty_driver *driver, * * Locking: tty_mutex for now */ -void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty) +static void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty) { if (driver->ops->remove) driver->ops->remove(driver, tty); @@ -1462,19 +1471,20 @@ static int tty_reopen(struct tty_struct *tty) { struct tty_driver *driver = tty->driver; - if (!tty->count) - return -EIO; - if (driver->type == TTY_DRIVER_TYPE_PTY && driver->subtype == PTY_TYPE_MASTER) return -EIO; + if (!tty->count) + return -EAGAIN; + if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN)) return -EBUSY; tty->count++; - WARN_ON(!tty->ldisc); + if (!tty->ldisc) + return tty_ldisc_reinit(tty, tty->termios.c_line); return 0; } @@ -1528,7 +1538,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) tty_lock(tty); retval = tty_driver_install_tty(driver, tty); if (retval < 0) - goto err_deinit_tty; + goto err_free_tty; if (!tty->port) tty->port = driver->ports[idx]; @@ -1550,9 +1560,8 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) /* Return the tty locked so that it cannot vanish under the caller */ return tty; -err_deinit_tty: +err_free_tty: tty_unlock(tty); - deinitialize_tty_struct(tty); free_tty_struct(tty); err_module_put: module_put(driver->owner); @@ -1561,13 +1570,13 @@ err_module_put: /* call the tty release_tty routine to clean out this slot */ err_release_tty: tty_unlock(tty); - printk_ratelimited(KERN_INFO "tty_init_dev: ldisc open failed, " - "clearing slot %d\n", idx); + tty_info_ratelimited(tty, "ldisc open failed (%d), clearing slot %d\n", + retval, idx); release_tty(tty, idx); return ERR_PTR(retval); } -void tty_free_termios(struct tty_struct *tty) +static void tty_free_termios(struct tty_struct *tty) { struct ktermios *tp; int idx = tty->index; @@ -1580,15 +1589,12 @@ void tty_free_termios(struct tty_struct *tty) tp = tty->driver->termios[idx]; if (tp == NULL) { tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL); - if (tp == NULL) { - pr_warn("tty: no memory to save termios state.\n"); + if (tp == NULL) return; - } tty->driver->termios[idx] = tp; } *tp = tty->termios; } -EXPORT_SYMBOL(tty_free_termios); /** * tty_flush_works - flush all works of a tty/pty pair @@ -1635,9 +1641,9 @@ static void release_one_tty(struct work_struct *work) tty_driver_kref_put(driver); module_put(owner); - spin_lock(&tty_files_lock); + spin_lock(&tty->files_lock); list_del_init(&tty->tty_files); - spin_unlock(&tty_files_lock); + spin_unlock(&tty->files_lock); put_pid(tty->pgrp); put_pid(tty->session); @@ -1788,7 +1794,7 @@ int tty_release(struct inode *inode, struct file *filp) return 0; } - tty_debug_hangup(tty, "(tty count=%d)...\n", tty->count); + tty_debug_hangup(tty, "releasing (count=%d)\n", tty->count); if (tty->ops->close) tty->ops->close(tty, filp); @@ -1837,8 +1843,7 @@ int tty_release(struct inode *inode, struct file *filp) if (once) { once = 0; - printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", - __func__, tty_name(tty)); + tty_warn(tty, "read/write wait queue active!\n"); } schedule_timeout_killable(timeout); if (timeout < 120 * HZ) @@ -1849,14 +1854,12 @@ int tty_release(struct inode *inode, struct file *filp) if (o_tty) { if (--o_tty->count < 0) { - printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n", - __func__, o_tty->count, tty_name(o_tty)); + tty_warn(tty, "bad slave count (%d)\n", o_tty->count); o_tty->count = 0; } } if (--tty->count < 0) { - printk(KERN_WARNING "%s: bad tty->count (%d) for %s\n", - __func__, tty->count, tty_name(tty)); + tty_warn(tty, "bad tty->count (%d)\n", tty->count); tty->count = 0; } @@ -1907,7 +1910,7 @@ int tty_release(struct inode *inode, struct file *filp) /* Wait for pending work before tty destruction commmences */ tty_flush_works(tty); - tty_debug_hangup(tty, "freeing structure...\n"); + tty_debug_hangup(tty, "freeing structure\n"); /* * The release_tty function takes care of the details of clearing * the slots and preserving the termios structure. The tty_unlock_pair @@ -1971,7 +1974,7 @@ static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp) * Locking: tty_mutex protects get_tty_driver */ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, - int *noctty, int *index) + int *index) { struct tty_driver *driver; @@ -1981,7 +1984,6 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, extern struct tty_driver *console_driver; driver = tty_driver_kref_get(console_driver); *index = fg_console; - *noctty = 1; break; } #endif @@ -1992,7 +1994,6 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, if (driver) { /* Don't let /dev/console block */ filp->f_flags |= O_NONBLOCK; - *noctty = 1; break; } } @@ -2008,6 +2009,69 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, } /** + * tty_open_by_driver - open a tty device + * @device: dev_t of device to open + * @inode: inode of device file + * @filp: file pointer to tty + * + * Performs the driver lookup, checks for a reopen, or otherwise + * performs the first-time tty initialization. + * + * Returns the locked initialized or re-opened &tty_struct + * + * Claims the global tty_mutex to serialize: + * - concurrent first-time tty initialization + * - concurrent tty driver removal w/ lookup + * - concurrent tty removal from driver table + */ +static struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode, + struct file *filp) +{ + struct tty_struct *tty; + struct tty_driver *driver = NULL; + int index = -1; + int retval; + + mutex_lock(&tty_mutex); + driver = tty_lookup_driver(device, filp, &index); + if (IS_ERR(driver)) { + mutex_unlock(&tty_mutex); + return ERR_CAST(driver); + } + + /* check whether we're reopening an existing tty */ + tty = tty_driver_lookup_tty(driver, inode, index); + if (IS_ERR(tty)) { + mutex_unlock(&tty_mutex); + goto out; + } + + if (tty) { + mutex_unlock(&tty_mutex); + retval = tty_lock_interruptible(tty); + if (retval) { + if (retval == -EINTR) + retval = -ERESTARTSYS; + tty = ERR_PTR(retval); + goto out; + } + /* safe to drop the kref from tty_driver_lookup_tty() */ + tty_kref_put(tty); + retval = tty_reopen(tty); + if (retval < 0) { + tty_unlock(tty); + tty = ERR_PTR(retval); + } + } else { /* Returns with the tty_lock held for now */ + tty = tty_init_dev(driver, index); + mutex_unlock(&tty_mutex); + } +out: + tty_driver_kref_put(driver); + return tty; +} + +/** * tty_open - open a tty device * @inode: inode of device file * @filp: file pointer to tty @@ -2035,8 +2099,6 @@ static int tty_open(struct inode *inode, struct file *filp) { struct tty_struct *tty; int noctty, retval; - struct tty_driver *driver = NULL; - int index; dev_t device = inode->i_rdev; unsigned saved_flags = filp->f_flags; @@ -2047,57 +2109,23 @@ retry_open: if (retval) return -ENOMEM; - noctty = filp->f_flags & O_NOCTTY; - index = -1; - retval = 0; - tty = tty_open_current_tty(device, filp); - if (!tty) { - mutex_lock(&tty_mutex); - driver = tty_lookup_driver(device, filp, &noctty, &index); - if (IS_ERR(driver)) { - retval = PTR_ERR(driver); - goto err_unlock; - } - - /* check whether we're reopening an existing tty */ - tty = tty_driver_lookup_tty(driver, inode, index); - if (IS_ERR(tty)) { - retval = PTR_ERR(tty); - goto err_unlock; - } - - if (tty) { - mutex_unlock(&tty_mutex); - tty_lock(tty); - /* safe to drop the kref from tty_driver_lookup_tty() */ - tty_kref_put(tty); - retval = tty_reopen(tty); - if (retval < 0) { - tty_unlock(tty); - tty = ERR_PTR(retval); - } - } else { /* Returns with the tty_lock held for now */ - tty = tty_init_dev(driver, index); - mutex_unlock(&tty_mutex); - } - - tty_driver_kref_put(driver); - } + if (!tty) + tty = tty_open_by_driver(device, inode, filp); if (IS_ERR(tty)) { + tty_free_file(filp); retval = PTR_ERR(tty); - goto err_file; + if (retval != -EAGAIN || signal_pending(current)) + return retval; + schedule(); + goto retry_open; } tty_add_file(tty, filp); check_tty_count(tty, __func__); - if (tty->driver->type == TTY_DRIVER_TYPE_PTY && - tty->driver->subtype == PTY_TYPE_MASTER) - noctty = 1; - - tty_debug_hangup(tty, "(tty count=%d)\n", tty->count); + tty_debug_hangup(tty, "opening (count=%d)\n", tty->count); if (tty->ops->open) retval = tty->ops->open(tty, filp); @@ -2106,7 +2134,7 @@ retry_open: filp->f_flags = saved_flags; if (retval) { - tty_debug_hangup(tty, "error %d, releasing...\n", retval); + tty_debug_hangup(tty, "open error %d, releasing\n", retval); tty_unlock(tty); /* need to call tty_release without BTM */ tty_release(inode, filp); @@ -2129,6 +2157,12 @@ retry_open: read_lock(&tasklist_lock); spin_lock_irq(¤t->sighand->siglock); + noctty = (filp->f_flags & O_NOCTTY) || + device == MKDEV(TTY_MAJOR, 0) || + device == MKDEV(TTYAUX_MAJOR, 1) || + (tty->driver->type == TTY_DRIVER_TYPE_PTY && + tty->driver->subtype == PTY_TYPE_MASTER); + if (!noctty && current->signal->leader && !current->signal->tty && @@ -2154,14 +2188,6 @@ retry_open: read_unlock(&tasklist_lock); tty_unlock(tty); return 0; -err_unlock: - mutex_unlock(&tty_mutex); - /* after locks to avoid deadlock */ - if (!IS_ERR_OR_NULL(driver)) - tty_driver_kref_put(driver); -err_file: - tty_free_file(filp); - return retval; } @@ -2188,6 +2214,8 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait) return 0; ld = tty_ldisc_ref_wait(tty); + if (!ld) + return hung_up_tty_poll(filp, wait); if (ld->ops->poll) ret = ld->ops->poll(tty, filp, wait); tty_ldisc_deref(ld); @@ -2197,7 +2225,6 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait) static int __tty_fasync(int fd, struct file *filp, int on) { struct tty_struct *tty = file_tty(filp); - struct tty_ldisc *ldisc; unsigned long flags; int retval = 0; @@ -2208,13 +2235,6 @@ static int __tty_fasync(int fd, struct file *filp, int on) if (retval <= 0) goto out; - ldisc = tty_ldisc_ref(tty); - if (ldisc) { - if (ldisc->ops->fasync) - ldisc->ops->fasync(tty, on); - tty_ldisc_deref(ldisc); - } - if (on) { enum pid_type type; struct pid *pid; @@ -2240,10 +2260,11 @@ out: static int tty_fasync(int fd, struct file *filp, int on) { struct tty_struct *tty = file_tty(filp); - int retval; + int retval = -ENOTTY; tty_lock(tty); - retval = __tty_fasync(fd, filp, on); + if (!tty_hung_up_p(filp)) + retval = __tty_fasync(fd, filp, on); tty_unlock(tty); return retval; @@ -2277,6 +2298,8 @@ static int tiocsti(struct tty_struct *tty, char __user *p) return -EFAULT; tty_audit_tiocsti(tty, ch); ld = tty_ldisc_ref_wait(tty); + if (!ld) + return -EIO; ld->ops->receive_buf(tty, &ch, &mbz, 1); tty_ldisc_deref(ld); return 0; @@ -2641,18 +2664,42 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _ static int tiocsetd(struct tty_struct *tty, int __user *p) { - int ldisc; + int disc; int ret; - if (get_user(ldisc, p)) + if (get_user(disc, p)) return -EFAULT; - ret = tty_set_ldisc(tty, ldisc); + ret = tty_set_ldisc(tty, disc); return ret; } /** + * tiocgetd - get line discipline + * @tty: tty device + * @p: pointer to user data + * + * Retrieves the line discipline id directly from the ldisc. + * + * Locking: waits for ldisc reference (in case the line discipline + * is changing or the tty is being hungup) + */ + +static int tiocgetd(struct tty_struct *tty, int __user *p) +{ + struct tty_ldisc *ld; + int ret; + + ld = tty_ldisc_ref_wait(tty); + if (!ld) + return -EIO; + ret = put_user(ld->ops->num, p); + tty_ldisc_deref(ld); + return ret; +} + +/** * send_break - performed time break * @tty: device to break on * @duration: timeout in mS @@ -2870,7 +2917,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) no_tty(); return 0; case TIOCSCTTY: - return tiocsctty(tty, file, arg); + return tiocsctty(real_tty, file, arg); case TIOCGPGRP: return tiocgpgrp(tty, real_tty, p); case TIOCSPGRP: @@ -2878,7 +2925,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case TIOCGSID: return tiocgsid(tty, real_tty, p); case TIOCGETD: - return put_user(tty->ldisc->ops->num, (int __user *)p); + return tiocgetd(tty, p); case TIOCSETD: return tiocsetd(tty, p); case TIOCVHANGUP: @@ -2944,6 +2991,8 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return retval; } ld = tty_ldisc_ref_wait(tty); + if (!ld) + return hung_up_tty_ioctl(file, cmd, arg); retval = -EINVAL; if (ld->ops->ioctl) { retval = ld->ops->ioctl(tty, file, cmd, arg); @@ -2972,6 +3021,8 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd, } ld = tty_ldisc_ref_wait(tty); + if (!ld) + return hung_up_tty_compat_ioctl(file, cmd, arg); if (ld->ops->compat_ioctl) retval = ld->ops->compat_ioctl(tty, file, cmd, arg); else @@ -3028,28 +3079,24 @@ void __do_SAK(struct tty_struct *tty) read_lock(&tasklist_lock); /* Kill the entire session */ do_each_pid_task(session, PIDTYPE_SID, p) { - printk(KERN_NOTICE "SAK: killed process %d" - " (%s): task_session(p)==tty->session\n", - task_pid_nr(p), p->comm); + tty_notice(tty, "SAK: killed process %d (%s): by session\n", + task_pid_nr(p), p->comm); send_sig(SIGKILL, p, 1); } while_each_pid_task(session, PIDTYPE_SID, p); - /* Now kill any processes that happen to have the - * tty open. - */ + + /* Now kill any processes that happen to have the tty open */ do_each_thread(g, p) { if (p->signal->tty == tty) { - printk(KERN_NOTICE "SAK: killed process %d" - " (%s): task_session(p)==tty->session\n", - task_pid_nr(p), p->comm); + tty_notice(tty, "SAK: killed process %d (%s): by controlling tty\n", + task_pid_nr(p), p->comm); send_sig(SIGKILL, p, 1); continue; } task_lock(p); i = iterate_fd(p->files, 0, this_tty, tty); if (i != 0) { - printk(KERN_NOTICE "SAK: killed process %d" - " (%s): fd#%d opened to the tty\n", - task_pid_nr(p), p->comm, i - 1); + tty_notice(tty, "SAK: killed process %d (%s): by fd#%d\n", + task_pid_nr(p), p->comm, i - 1); force_sig(SIGKILL, p); } task_unlock(p); @@ -3126,6 +3173,7 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx) mutex_init(&tty->atomic_write_lock); spin_lock_init(&tty->ctrl_lock); spin_lock_init(&tty->flow_lock); + spin_lock_init(&tty->files_lock); INIT_LIST_HEAD(&tty->tty_files); INIT_WORK(&tty->SAK_work, do_SAK_work); @@ -3139,20 +3187,6 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx) } /** - * deinitialize_tty_struct - * @tty: tty to deinitialize - * - * This subroutine deinitializes a tty structure that has been newly - * allocated but tty_release cannot be called on that yet. - * - * Locking: none - tty in question must not be exposed at this point - */ -void deinitialize_tty_struct(struct tty_struct *tty) -{ - tty_ldisc_deinit(tty); -} - -/** * tty_put_char - write one character to a tty * @tty: tty * @ch: character @@ -3219,7 +3253,7 @@ EXPORT_SYMBOL(tty_register_device); static void tty_device_create_release(struct device *dev) { - pr_debug("device: '%s': %s\n", dev_name(dev), __func__); + dev_dbg(dev, "releasing...\n"); kfree(dev); } @@ -3255,8 +3289,8 @@ struct device *tty_register_device_attr(struct tty_driver *driver, bool cdev = false; if (index >= driver->num) { - printk(KERN_ERR "Attempt to register invalid tty line number " - " (%d).\n", index); + pr_err("%s: Attempt to register invalid tty line number (%d)\n", + driver->name, index); return ERR_PTR(-EINVAL); } @@ -3546,7 +3580,7 @@ void __init console_init(void) initcall_t *call; /* Setup the default TTY line discipline. */ - tty_ldisc_begin(); + n_tty_init(); /* * set up the console device so that later boot sequences can diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 1445dd39aa62..23bf5bb1d8bf 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -216,7 +216,7 @@ int tty_unthrottle_safe(struct tty_struct *tty) void tty_wait_until_sent(struct tty_struct *tty, long timeout) { - tty_debug_wait_until_sent(tty, "\n"); + tty_debug_wait_until_sent(tty, "wait until sent, timeout=%ld\n", timeout); if (!timeout) timeout = MAX_SCHEDULE_TIMEOUT; @@ -239,19 +239,14 @@ EXPORT_SYMBOL(tty_wait_until_sent); * Termios Helper Methods */ -static void unset_locked_termios(struct ktermios *termios, - struct ktermios *old, - struct ktermios *locked) +static void unset_locked_termios(struct tty_struct *tty, struct ktermios *old) { + struct ktermios *termios = &tty->termios; + struct ktermios *locked = &tty->termios_locked; int i; #define NOSET_MASK(x, y, z) (x = ((x) & ~(z)) | ((y) & (z))) - if (!locked) { - printk(KERN_WARNING "Warning?!? termios_locked is NULL.\n"); - return; - } - NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag); NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag); NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag); @@ -463,10 +458,8 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, if (ifound == -1 && (ibaud != obaud || ibinput)) termios->c_cflag |= (BOTHER << IBSHIFT); #else - if (ifound == -1 || ofound == -1) { - printk_once(KERN_WARNING "tty: Unable to return correct " - "speed data as your architecture needs updating.\n"); - } + if (ifound == -1 || ofound == -1) + pr_warn_once("tty: Unable to return correct speed data as your architecture needs updating.\n"); #endif } EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate); @@ -556,7 +549,7 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) down_write(&tty->termios_rwsem); old_termios = tty->termios; tty->termios = *new_termios; - unset_locked_termios(&tty->termios, &old_termios, &tty->termios_locked); + unset_locked_termios(tty, &old_termios); if (tty->ops->set_termios) tty->ops->set_termios(tty, &old_termios); @@ -726,16 +719,16 @@ static int get_sgflags(struct tty_struct *tty) { int flags = 0; - if (!(tty->termios.c_lflag & ICANON)) { - if (tty->termios.c_lflag & ISIG) + if (!L_ICANON(tty)) { + if (L_ISIG(tty)) flags |= 0x02; /* cbreak */ else flags |= 0x20; /* raw */ } - if (tty->termios.c_lflag & ECHO) + if (L_ECHO(tty)) flags |= 0x08; /* echo */ - if (tty->termios.c_oflag & OPOST) - if (tty->termios.c_oflag & ONLCR) + if (O_OPOST(tty)) + if (O_ONLCR(tty)) flags |= 0x10; /* crmod */ return flags; } @@ -915,7 +908,7 @@ static int tty_change_softcar(struct tty_struct *tty, int arg) tty->termios.c_cflag |= bit; if (tty->ops->set_termios) tty->ops->set_termios(tty, &old); - if ((tty->termios.c_cflag & CLOCAL) != bit) + if (C_CLOCAL(tty) != bit) ret = -EINVAL; up_write(&tty->termios_rwsem); return ret; diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 629e3c865072..68947f6de5ad 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -140,9 +140,16 @@ static void put_ldops(struct tty_ldisc_ops *ldops) * @disc: ldisc number * * Takes a reference to a line discipline. Deals with refcounts and - * module locking counts. Returns NULL if the discipline is not available. - * Returns a pointer to the discipline and bumps the ref count if it is - * available + * module locking counts. + * + * Returns: -EINVAL if the discipline index is not [N_TTY..NR_LDISCS] or + * if the discipline is not registered + * -EAGAIN if request_module() failed to load or register the + * the discipline + * -ENOMEM if allocation failure + * + * Otherwise, returns a pointer to the discipline and bumps the + * ref count * * Locking: * takes tty_ldiscs_lock to guard against ldisc races @@ -185,7 +192,7 @@ static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc) * * Complement of tty_ldisc_get(). */ -static inline void tty_ldisc_put(struct tty_ldisc *ld) +static void tty_ldisc_put(struct tty_ldisc *ld) { if (WARN_ON_ONCE(!ld)) return; @@ -250,19 +257,23 @@ const struct file_operations tty_ldiscs_proc_fops = { * reference to it. If the line discipline is in flux then * wait patiently until it changes. * + * Returns: NULL if the tty has been hungup and not re-opened with + * a new file descriptor, otherwise valid ldisc reference + * * Note: Must not be called from an IRQ/timer context. The caller * must also be careful not to hold other locks that will deadlock * against a discipline change, such as an existing ldisc reference * (which we check for) * - * Note: only callable from a file_operations routine (which - * guarantees tty->ldisc != NULL when the lock is acquired). + * Note: a file_operations routine (read/poll/write) should use this + * function to wait for any ldisc lifetime events to finish. */ struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) { ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT); - WARN_ON(!tty->ldisc); + if (!tty->ldisc) + ldsem_up_read(&tty->ldisc_sem); return tty->ldisc; } EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); @@ -304,13 +315,13 @@ void tty_ldisc_deref(struct tty_ldisc *ld) EXPORT_SYMBOL_GPL(tty_ldisc_deref); -static inline int __lockfunc +static inline int __tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) { return ldsem_down_write(&tty->ldisc_sem, timeout); } -static inline int __lockfunc +static inline int __tty_ldisc_lock_nested(struct tty_struct *tty, unsigned long timeout) { return ldsem_down_write_nested(&tty->ldisc_sem, @@ -322,8 +333,7 @@ static inline void __tty_ldisc_unlock(struct tty_struct *tty) ldsem_up_write(&tty->ldisc_sem); } -static int __lockfunc -tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) +static int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) { int ret; @@ -340,7 +350,7 @@ static void tty_ldisc_unlock(struct tty_struct *tty) __tty_ldisc_unlock(tty); } -static int __lockfunc +static int tty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2, unsigned long timeout) { @@ -376,14 +386,13 @@ tty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2, return 0; } -static void __lockfunc -tty_ldisc_lock_pair(struct tty_struct *tty, struct tty_struct *tty2) +static void tty_ldisc_lock_pair(struct tty_struct *tty, struct tty_struct *tty2) { tty_ldisc_lock_pair_timeout(tty, tty2, MAX_SCHEDULE_TIMEOUT); } -static void __lockfunc tty_ldisc_unlock_pair(struct tty_struct *tty, - struct tty_struct *tty2) +static void tty_ldisc_unlock_pair(struct tty_struct *tty, + struct tty_struct *tty2) { __tty_ldisc_unlock(tty); if (tty2) @@ -411,20 +420,27 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush); /** * tty_set_termios_ldisc - set ldisc field * @tty: tty structure - * @num: line discipline number + * @disc: line discipline number * * This is probably overkill for real world processors but * they are not on hot paths so a little discipline won't do * any harm. * + * The line discipline-related tty_struct fields are reset to + * prevent the ldisc driver from re-using stale information for + * the new ldisc instance. + * * Locking: takes termios_rwsem */ -static void tty_set_termios_ldisc(struct tty_struct *tty, int num) +static void tty_set_termios_ldisc(struct tty_struct *tty, int disc) { down_write(&tty->termios_rwsem); - tty->termios.c_line = num; + tty->termios.c_line = disc; up_write(&tty->termios_rwsem); + + tty->disc_data = NULL; + tty->receive_room = 0; } /** @@ -448,7 +464,7 @@ static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld) if (ret) clear_bit(TTY_LDISC_OPEN, &tty->flags); - tty_ldisc_debug(tty, "%p: opened\n", tty->ldisc); + tty_ldisc_debug(tty, "%p: opened\n", ld); return ret; } return 0; @@ -469,7 +485,7 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld) clear_bit(TTY_LDISC_OPEN, &tty->flags); if (ld->ops->close) ld->ops->close(tty); - tty_ldisc_debug(tty, "%p: closed\n", tty->ldisc); + tty_ldisc_debug(tty, "%p: closed\n", ld); } /** @@ -518,51 +534,43 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) * the close of one side of a tty/pty pair, and eventually hangup. */ -int tty_set_ldisc(struct tty_struct *tty, int ldisc) +int tty_set_ldisc(struct tty_struct *tty, int disc) { int retval; struct tty_ldisc *old_ldisc, *new_ldisc; - new_ldisc = tty_ldisc_get(tty, ldisc); + new_ldisc = tty_ldisc_get(tty, disc); if (IS_ERR(new_ldisc)) return PTR_ERR(new_ldisc); tty_lock(tty); retval = tty_ldisc_lock(tty, 5 * HZ); - if (retval) { - tty_ldisc_put(new_ldisc); - tty_unlock(tty); - return retval; - } - - /* - * Check the no-op case - */ + if (retval) + goto err; - if (tty->ldisc->ops->num == ldisc) { - tty_ldisc_unlock(tty); - tty_ldisc_put(new_ldisc); - tty_unlock(tty); - return 0; + if (!tty->ldisc) { + retval = -EIO; + goto out; } - old_ldisc = tty->ldisc; + /* Check the no-op case */ + if (tty->ldisc->ops->num == disc) + goto out; if (test_bit(TTY_HUPPED, &tty->flags)) { - /* We were raced by the hangup method. It will have stomped - the ldisc data and closed the ldisc down */ - tty_ldisc_unlock(tty); - tty_ldisc_put(new_ldisc); - tty_unlock(tty); - return -EIO; + /* We were raced by hangup */ + retval = -EIO; + goto out; } + old_ldisc = tty->ldisc; + /* Shutdown the old discipline. */ tty_ldisc_close(tty, old_ldisc); /* Now set up the new line discipline. */ tty->ldisc = new_ldisc; - tty_set_termios_ldisc(tty, ldisc); + tty_set_termios_ldisc(tty, disc); retval = tty_ldisc_open(tty, new_ldisc); if (retval < 0) { @@ -582,23 +590,39 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) the old ldisc (if it was restored as part of error cleanup above). In either case, releasing a single reference from the old ldisc is correct. */ - - tty_ldisc_put(old_ldisc); - - /* - * Allow ldisc referencing to occur again - */ + new_ldisc = old_ldisc; +out: tty_ldisc_unlock(tty); /* Restart the work queue in case no characters kick it off. Safe if already running */ tty_buffer_restart_work(tty->port); - +err: + tty_ldisc_put(new_ldisc); /* drop the extra reference */ tty_unlock(tty); return retval; } /** + * tty_ldisc_kill - teardown ldisc + * @tty: tty being released + * + * Perform final close of the ldisc and reset tty->ldisc + */ +static void tty_ldisc_kill(struct tty_struct *tty) +{ + if (!tty->ldisc) + return; + /* + * Now kill off the ldisc + */ + tty_ldisc_close(tty, tty->ldisc); + tty_ldisc_put(tty->ldisc); + /* Force an oops if we mess this up */ + tty->ldisc = NULL; +} + +/** * tty_reset_termios - reset terminal state * @tty: tty to reset * @@ -618,28 +642,44 @@ static void tty_reset_termios(struct tty_struct *tty) /** * tty_ldisc_reinit - reinitialise the tty ldisc * @tty: tty to reinit - * @ldisc: line discipline to reinitialize + * @disc: line discipline to reinitialize + * + * Completely reinitialize the line discipline state, by closing the + * current instance, if there is one, and opening a new instance. If + * an error occurs opening the new non-N_TTY instance, the instance + * is dropped and tty->ldisc reset to NULL. The caller can then retry + * with N_TTY instead. * - * Switch the tty to a line discipline and leave the ldisc - * state closed + * Returns 0 if successful, otherwise error code < 0 */ -static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc) +int tty_ldisc_reinit(struct tty_struct *tty, int disc) { - struct tty_ldisc *ld = tty_ldisc_get(tty, ldisc); + struct tty_ldisc *ld; + int retval; - if (IS_ERR(ld)) - return -1; + ld = tty_ldisc_get(tty, disc); + if (IS_ERR(ld)) { + BUG_ON(disc == N_TTY); + return PTR_ERR(ld); + } - tty_ldisc_close(tty, tty->ldisc); - tty_ldisc_put(tty->ldisc); - /* - * Switch the line discipline back - */ - tty->ldisc = ld; - tty_set_termios_ldisc(tty, ldisc); + if (tty->ldisc) { + tty_ldisc_close(tty, tty->ldisc); + tty_ldisc_put(tty->ldisc); + } - return 0; + /* switch the line discipline */ + tty->ldisc = ld; + tty_set_termios_ldisc(tty, disc); + retval = tty_ldisc_open(tty, tty->ldisc); + if (retval) { + if (!WARN_ON(disc == N_TTY)) { + tty_ldisc_put(tty->ldisc); + tty->ldisc = NULL; + } + } + return retval; } /** @@ -657,13 +697,11 @@ static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc) * tty itself so we must be careful about locking rules. */ -void tty_ldisc_hangup(struct tty_struct *tty) +void tty_ldisc_hangup(struct tty_struct *tty, bool reinit) { struct tty_ldisc *ld; - int reset = tty->driver->flags & TTY_DRIVER_RESET_TERMIOS; - int err = 0; - tty_ldisc_debug(tty, "%p: closing\n", tty->ldisc); + tty_ldisc_debug(tty, "%p: hangup\n", tty->ldisc); ld = tty_ldisc_ref(tty); if (ld != NULL) { @@ -689,31 +727,17 @@ void tty_ldisc_hangup(struct tty_struct *tty) */ tty_ldisc_lock(tty, MAX_SCHEDULE_TIMEOUT); - if (tty->ldisc) { - - /* At this point we have a halted ldisc; we want to close it and - reopen a new ldisc. We could defer the reopen to the next - open but it means auditing a lot of other paths so this is - a FIXME */ - if (reset == 0) { + if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) + tty_reset_termios(tty); - if (!tty_ldisc_reinit(tty, tty->termios.c_line)) - err = tty_ldisc_open(tty, tty->ldisc); - else - err = 1; - } - /* If the re-open fails or we reset then go to N_TTY. The - N_TTY open cannot fail */ - if (reset || err) { - BUG_ON(tty_ldisc_reinit(tty, N_TTY)); - WARN_ON(tty_ldisc_open(tty, tty->ldisc)); - } + if (tty->ldisc) { + if (reinit) { + if (tty_ldisc_reinit(tty, tty->termios.c_line) < 0) + tty_ldisc_reinit(tty, N_TTY); + } else + tty_ldisc_kill(tty); } tty_ldisc_unlock(tty); - if (reset) - tty_reset_termios(tty); - - tty_ldisc_debug(tty, "%p: re-opened\n", tty->ldisc); } /** @@ -728,44 +752,26 @@ void tty_ldisc_hangup(struct tty_struct *tty) int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) { - struct tty_ldisc *ld = tty->ldisc; - int retval; - - retval = tty_ldisc_open(tty, ld); + int retval = tty_ldisc_open(tty, tty->ldisc); if (retval) return retval; if (o_tty) { retval = tty_ldisc_open(o_tty, o_tty->ldisc); if (retval) { - tty_ldisc_close(tty, ld); + tty_ldisc_close(tty, tty->ldisc); return retval; } } return 0; } -static void tty_ldisc_kill(struct tty_struct *tty) -{ - /* - * Now kill off the ldisc - */ - tty_ldisc_close(tty, tty->ldisc); - tty_ldisc_put(tty->ldisc); - /* Force an oops if we mess this up */ - tty->ldisc = NULL; - - /* Ensure the next open requests the N_TTY ldisc */ - tty_set_termios_ldisc(tty, N_TTY); -} - /** * tty_ldisc_release - release line discipline * @tty: tty being shut down (or one end of pty pair) * * Called during the final close of a tty or a pty pair in order to shut - * down the line discpline layer. On exit, each ldisc assigned is N_TTY and - * each ldisc has not been opened. + * down the line discpline layer. On exit, each tty's ldisc is NULL. */ void tty_ldisc_release(struct tty_struct *tty) @@ -806,7 +812,7 @@ void tty_ldisc_init(struct tty_struct *tty) } /** - * tty_ldisc_init - ldisc cleanup for new tty + * tty_ldisc_deinit - ldisc cleanup for new tty * @tty: tty that was allocated recently * * The tty structure must not becompletely set up (tty_ldisc_setup) when @@ -814,12 +820,7 @@ void tty_ldisc_init(struct tty_struct *tty) */ void tty_ldisc_deinit(struct tty_struct *tty) { - tty_ldisc_put(tty->ldisc); + if (tty->ldisc) + tty_ldisc_put(tty->ldisc); tty->ldisc = NULL; } - -void tty_ldisc_begin(void) -{ - /* Setup the default TTY line discipline. */ - (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); -} diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c index ad7eba5ca380..1bf8ed13f827 100644 --- a/drivers/tty/tty_ldsem.c +++ b/drivers/tty/tty_ldsem.c @@ -319,7 +319,7 @@ down_write_failed(struct ld_semaphore *sem, long count, long timeout) -static inline int __ldsem_down_read_nested(struct ld_semaphore *sem, +static int __ldsem_down_read_nested(struct ld_semaphore *sem, int subclass, long timeout) { long count; @@ -338,7 +338,7 @@ static inline int __ldsem_down_read_nested(struct ld_semaphore *sem, return 1; } -static inline int __ldsem_down_write_nested(struct ld_semaphore *sem, +static int __ldsem_down_write_nested(struct ld_semaphore *sem, int subclass, long timeout) { long count; diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 0efcf713b756..d8bae67a6174 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -10,37 +10,44 @@ * Getting the big tty mutex. */ -void __lockfunc tty_lock(struct tty_struct *tty) +void tty_lock(struct tty_struct *tty) { - if (tty->magic != TTY_MAGIC) { - pr_err("L Bad %p\n", tty); - WARN_ON(1); + if (WARN(tty->magic != TTY_MAGIC, "L Bad %p\n", tty)) return; - } tty_kref_get(tty); mutex_lock(&tty->legacy_mutex); } EXPORT_SYMBOL(tty_lock); -void __lockfunc tty_unlock(struct tty_struct *tty) +int tty_lock_interruptible(struct tty_struct *tty) { - if (tty->magic != TTY_MAGIC) { - pr_err("U Bad %p\n", tty); - WARN_ON(1); + int ret; + + if (WARN(tty->magic != TTY_MAGIC, "L Bad %p\n", tty)) + return -EIO; + tty_kref_get(tty); + ret = mutex_lock_interruptible(&tty->legacy_mutex); + if (ret) + tty_kref_put(tty); + return ret; +} + +void tty_unlock(struct tty_struct *tty) +{ + if (WARN(tty->magic != TTY_MAGIC, "U Bad %p\n", tty)) return; - } mutex_unlock(&tty->legacy_mutex); tty_kref_put(tty); } EXPORT_SYMBOL(tty_unlock); -void __lockfunc tty_lock_slave(struct tty_struct *tty) +void tty_lock_slave(struct tty_struct *tty) { if (tty && tty != tty->link) tty_lock(tty); } -void __lockfunc tty_unlock_slave(struct tty_struct *tty) +void tty_unlock_slave(struct tty_struct *tty) { if (tty && tty != tty->link) tty_unlock(tty); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 482f33f20043..dbcca30a54b1 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -370,7 +370,7 @@ int tty_port_block_til_ready(struct tty_port *port, } if (filp->f_flags & O_NONBLOCK) { /* Indicate we are open */ - if (tty->termios.c_cflag & CBAUD) + if (C_BAUD(tty)) tty_port_raise_dtr_rts(port); port->flags |= ASYNC_NORMAL_ACTIVE; return 0; @@ -462,14 +462,13 @@ int tty_port_close_start(struct tty_port *port, spin_lock_irqsave(&port->lock, flags); if (tty->count == 1 && port->count != 1) { - printk(KERN_WARNING - "tty_port_close_start: tty->count = 1 port count = %d.\n", - port->count); + tty_warn(tty, "%s: tty->count = 1 port count = %d\n", __func__, + port->count); port->count = 1; } if (--port->count < 0) { - printk(KERN_WARNING "tty_port_close_start: count = %d\n", - port->count); + tty_warn(tty, "%s: bad port count (%d)\n", __func__, + port->count); port->count = 0; } @@ -477,7 +476,6 @@ int tty_port_close_start(struct tty_port *port, spin_unlock_irqrestore(&port->lock, flags); return 0; } - set_bit(ASYNCB_CLOSING, &port->flags); spin_unlock_irqrestore(&port->lock, flags); tty->closing = 1; @@ -511,14 +509,12 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) if (port->blocked_open) { spin_unlock_irqrestore(&port->lock, flags); - if (port->close_delay) { - msleep_interruptible( - jiffies_to_msecs(port->close_delay)); - } + if (port->close_delay) + msleep_interruptible(jiffies_to_msecs(port->close_delay)); spin_lock_irqsave(&port->lock, flags); wake_up_interruptible(&port->open_wait); } - port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); + port->flags &= ~ASYNC_NORMAL_ACTIVE; spin_unlock_irqrestore(&port->lock, flags); } EXPORT_SYMBOL(tty_port_close_end); diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 6f0336fff501..f973bfce5d08 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -1706,16 +1706,12 @@ int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm) return -EINVAL; if (ct) { - dia = kmalloc(sizeof(struct kbdiacr) * ct, - GFP_KERNEL); - if (!dia) - return -ENOMEM; - if (copy_from_user(dia, a->kbdiacr, - sizeof(struct kbdiacr) * ct)) { - kfree(dia); - return -EFAULT; - } + dia = memdup_user(a->kbdiacr, + sizeof(struct kbdiacr) * ct); + if (IS_ERR(dia)) + return PTR_ERR(dia); + } spin_lock_irqsave(&kbd_event_lock, flags); diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 381a2b13682c..4dd9dd2270a0 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -347,6 +347,8 @@ int paste_selection(struct tty_struct *tty) console_unlock(); ld = tty_ldisc_ref_wait(tty); + if (!ld) + return -EIO; /* ldisc was hung up */ tty_buffer_lock_exclusive(&vc->port); add_wait_queue(&vc->paste_wait, &wait); diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 4462d167900c..3e3c7575e92d 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -568,7 +568,7 @@ static void delete_char(struct vc_data *vc, unsigned int nr) vc->vc_cols - vc->vc_x); } -static int softcursor_original; +static int softcursor_original = -1; static void add_softcursor(struct vc_data *vc) { @@ -634,7 +634,7 @@ static void set_origin(struct vc_data *vc) vc->vc_pos = vc->vc_origin + vc->vc_size_row * vc->vc_y + 2 * vc->vc_x; } -static inline void save_screen(struct vc_data *vc) +static void save_screen(struct vc_data *vc) { WARN_CONSOLE_UNLOCKED(); @@ -4250,6 +4250,7 @@ unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed) { return screenpos(vc, 2 * w_offset, viewed); } +EXPORT_SYMBOL_GPL(screen_pos); void getconsxy(struct vc_data *vc, unsigned char *p) { |