diff options
Diffstat (limited to 'drivers/tty')
48 files changed, 3934 insertions, 1189 deletions
diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index 75dc9d25f326..09495f515fa9 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -21,6 +21,7 @@ #include <linux/slab.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/goldfish.h> enum { GOLDFISH_TTY_PUT_CHAR = 0x00, @@ -29,6 +30,7 @@ enum { GOLDFISH_TTY_DATA_PTR = 0x10, GOLDFISH_TTY_DATA_LEN = 0x14, + GOLDFISH_TTY_DATA_PTR_HIGH = 0x18, GOLDFISH_TTY_CMD_INT_DISABLE = 0, GOLDFISH_TTY_CMD_INT_ENABLE = 1, @@ -57,7 +59,8 @@ static void goldfish_tty_do_write(int line, const char *buf, unsigned count) struct goldfish_tty *qtty = &goldfish_ttys[line]; void __iomem *base = qtty->base; spin_lock_irqsave(&qtty->lock, irq_flags); - writel((u32)buf, base + GOLDFISH_TTY_DATA_PTR); + gf_write64((u64)buf, base + GOLDFISH_TTY_DATA_PTR, + base + GOLDFISH_TTY_DATA_PTR_HIGH); writel(count, base + GOLDFISH_TTY_DATA_LEN); writel(GOLDFISH_TTY_CMD_WRITE_BUFFER, base + GOLDFISH_TTY_CMD); spin_unlock_irqrestore(&qtty->lock, irq_flags); @@ -73,12 +76,13 @@ static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id) u32 count; count = readl(base + GOLDFISH_TTY_BYTES_READY); - if(count == 0) + if (count == 0) return IRQ_NONE; count = tty_prepare_flip_string(&qtty->port, &buf, count); spin_lock_irqsave(&qtty->lock, irq_flags); - writel((u32)buf, base + GOLDFISH_TTY_DATA_PTR); + gf_write64((u64)buf, base + GOLDFISH_TTY_DATA_PTR, + base + GOLDFISH_TTY_DATA_PTR_HIGH); writel(count, base + GOLDFISH_TTY_DATA_LEN); writel(GOLDFISH_TTY_CMD_READ_BUFFER, base + GOLDFISH_TTY_CMD); spin_unlock_irqrestore(&qtty->lock, irq_flags); @@ -88,24 +92,26 @@ static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id) static int goldfish_tty_activate(struct tty_port *port, struct tty_struct *tty) { - struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, port); + struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, + port); writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_CMD); return 0; } static void goldfish_tty_shutdown(struct tty_port *port) { - struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, port); + struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, + port); writel(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_CMD); } -static int goldfish_tty_open(struct tty_struct * tty, struct file * filp) +static int goldfish_tty_open(struct tty_struct *tty, struct file *filp) { struct goldfish_tty *qtty = &goldfish_ttys[tty->index]; return tty_port_open(&qtty->port, tty, filp); } -static void goldfish_tty_close(struct tty_struct * tty, struct file * filp) +static void goldfish_tty_close(struct tty_struct *tty, struct file *filp) { tty_port_close(tty->port, tty, filp); } @@ -115,7 +121,8 @@ static void goldfish_tty_hangup(struct tty_struct *tty) tty_port_hangup(tty->port); } -static int goldfish_tty_write(struct tty_struct * tty, const unsigned char *buf, int count) +static int goldfish_tty_write(struct tty_struct *tty, const unsigned char *buf, + int count) { goldfish_tty_do_write(tty->index, buf, count); return count; @@ -133,12 +140,14 @@ static int goldfish_tty_chars_in_buffer(struct tty_struct *tty) return readl(base + GOLDFISH_TTY_BYTES_READY); } -static void goldfish_tty_console_write(struct console *co, const char *b, unsigned count) +static void goldfish_tty_console_write(struct console *co, const char *b, + unsigned count) { goldfish_tty_do_write(co->index, b, count); } -static struct tty_driver *goldfish_tty_console_device(struct console *c, int *index) +static struct tty_driver *goldfish_tty_console_device(struct console *c, + int *index) { *index = c->index; return goldfish_tty_driver; @@ -146,9 +155,9 @@ static struct tty_driver *goldfish_tty_console_device(struct console *c, int *in static int goldfish_tty_console_setup(struct console *co, char *options) { - if((unsigned)co->index > goldfish_tty_line_count) + if ((unsigned)co->index > goldfish_tty_line_count) return -ENODEV; - if(goldfish_ttys[co->index].base == 0) + if (goldfish_ttys[co->index].base == 0) return -ENODEV; return 0; } @@ -158,7 +167,7 @@ static struct tty_port_operations goldfish_port_ops = { .shutdown = goldfish_tty_shutdown }; -static struct tty_operations goldfish_tty_ops = { +static const struct tty_operations goldfish_tty_ops = { .open = goldfish_tty_open, .close = goldfish_tty_close, .hangup = goldfish_tty_hangup, @@ -172,13 +181,14 @@ static int goldfish_tty_create_driver(void) int ret; struct tty_driver *tty; - goldfish_ttys = kzalloc(sizeof(*goldfish_ttys) * goldfish_tty_line_count, GFP_KERNEL); - if(goldfish_ttys == NULL) { + goldfish_ttys = kzalloc(sizeof(*goldfish_ttys) * + goldfish_tty_line_count, GFP_KERNEL); + if (goldfish_ttys == NULL) { ret = -ENOMEM; goto err_alloc_goldfish_ttys_failed; } tty = alloc_tty_driver(goldfish_tty_line_count); - if(tty == NULL) { + if (tty == NULL) { ret = -ENOMEM; goto err_alloc_tty_driver_failed; } @@ -187,10 +197,11 @@ static int goldfish_tty_create_driver(void) tty->type = TTY_DRIVER_TYPE_SERIAL; tty->subtype = SERIAL_TYPE_NORMAL; tty->init_termios = tty_std_termios; - tty->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV; tty_set_operations(tty, &goldfish_tty_ops); ret = tty_register_driver(tty); - if(ret) + if (ret) goto err_tty_register_driver_failed; goldfish_tty_driver = tty; @@ -225,7 +236,7 @@ static int goldfish_tty_probe(struct platform_device *pdev) u32 irq; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if(r == NULL) + if (r == NULL) return -EINVAL; base = ioremap(r->start, 0x1000); @@ -233,18 +244,18 @@ static int goldfish_tty_probe(struct platform_device *pdev) pr_err("goldfish_tty: unable to remap base\n"); r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if(r == NULL) + if (r == NULL) goto err_unmap; irq = r->start; - if(pdev->id >= goldfish_tty_line_count) + if (pdev->id >= goldfish_tty_line_count) goto err_unmap; mutex_lock(&goldfish_tty_lock); - if(goldfish_tty_current_line_count == 0) { + if (goldfish_tty_current_line_count == 0) { ret = goldfish_tty_create_driver(); - if(ret) + if (ret) goto err_create_driver_failed; } goldfish_tty_current_line_count++; @@ -258,14 +269,15 @@ 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); - if(ret) + ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED, + "goldfish_tty", pdev); + if (ret) goto err_request_irq_failed; ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver, pdev->id, &pdev->dev); - if(IS_ERR(ttydev)) { + if (IS_ERR(ttydev)) { ret = PTR_ERR(ttydev); goto err_tty_register_device_failed; } @@ -286,7 +298,7 @@ err_tty_register_device_failed: free_irq(irq, pdev); err_request_irq_failed: goldfish_tty_current_line_count--; - if(goldfish_tty_current_line_count == 0) + if (goldfish_tty_current_line_count == 0) goldfish_tty_delete_driver(); err_create_driver_failed: mutex_unlock(&goldfish_tty_lock); @@ -308,7 +320,7 @@ static int goldfish_tty_remove(struct platform_device *pdev) qtty->base = 0; free_irq(qtty->irq, pdev); goldfish_tty_current_line_count--; - if(goldfish_tty_current_line_count == 0) + if (goldfish_tty_current_line_count == 0) goldfish_tty_delete_driver(); mutex_unlock(&goldfish_tty_lock); return 0; diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index 0ff7fda0742f..4fcec1d793a7 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -760,10 +760,17 @@ static int khvcd(void *unused) if (poll_mask == 0) schedule(); else { + unsigned long j_timeout; + if (timeout < MAX_TIMEOUT) timeout += (timeout >> 6) + 1; - msleep_interruptible(timeout); + /* + * We don't use msleep_interruptible otherwise + * "kick" will fail to wake us up + */ + j_timeout = msecs_to_jiffies(timeout) + 1; + schedule_timeout_interruptible(j_timeout); } } __set_current_state(TASK_RUNNING); diff --git a/drivers/tty/hvc/hvc_dcc.c b/drivers/tty/hvc/hvc_dcc.c index 3502a7bbb69e..809920d80a66 100644 --- a/drivers/tty/hvc/hvc_dcc.c +++ b/drivers/tty/hvc/hvc_dcc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010, 2014 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -8,20 +8,11 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ -#include <linux/console.h> -#include <linux/delay.h> -#include <linux/err.h> #include <linux/init.h> -#include <linux/moduleparam.h> -#include <linux/types.h> +#include <asm/dcc.h> #include <asm/processor.h> #include "hvc_console.h" @@ -30,35 +21,6 @@ #define DCC_STATUS_RX (1 << 30) #define DCC_STATUS_TX (1 << 29) -static inline u32 __dcc_getstatus(void) -{ - u32 __ret; - asm volatile("mrc p14, 0, %0, c0, c1, 0 @ read comms ctrl reg" - : "=r" (__ret) : : "cc"); - - return __ret; -} - - -static inline char __dcc_getchar(void) -{ - char __c; - - asm volatile("mrc p14, 0, %0, c0, c5, 0 @ read comms data reg" - : "=r" (__c)); - isb(); - - return __c; -} - -static inline void __dcc_putchar(char c) -{ - asm volatile("mcr p14, 0, %0, c0, c5, 0 @ write a char" - : /* no output register */ - : "r" (c)); - isb(); -} - static int hvc_dcc_put_chars(uint32_t vt, const char *buf, int count) { int i; diff --git a/drivers/tty/hvc/hvc_tile.c b/drivers/tty/hvc/hvc_tile.c index af8cdaa1dcb9..147d49e95db2 100644 --- a/drivers/tty/hvc/hvc_tile.c +++ b/drivers/tty/hvc/hvc_tile.c @@ -133,14 +133,14 @@ static int hvc_tile_probe(struct platform_device *pdev) int tile_hvc_irq; /* Create our IRQ and register it. */ - tile_hvc_irq = create_irq(); - if (tile_hvc_irq < 0) + tile_hvc_irq = irq_alloc_hwirq(-1); + if (!tile_hvc_irq) return -ENXIO; tile_irq_activate(tile_hvc_irq, TILE_IRQ_PERCPU); hp = hvc_alloc(0, tile_hvc_irq, &hvc_tile_get_put_ops, 128); if (IS_ERR(hp)) { - destroy_irq(tile_hvc_irq); + irq_free_hwirq(tile_hvc_irq); return PTR_ERR(hp); } dev_set_drvdata(&pdev->dev, hp); @@ -155,7 +155,7 @@ static int hvc_tile_remove(struct platform_device *pdev) rc = hvc_remove(hp); if (rc == 0) - destroy_irq(hp->data); + irq_free_hwirq(hp->data); return rc; } diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 1b2db9a3038c..644ddb841d9f 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -848,13 +848,11 @@ static struct n_hdlc *n_hdlc_alloc(void) { struct n_hdlc_buf *buf; int i; - struct n_hdlc *n_hdlc = kmalloc(sizeof(*n_hdlc), GFP_KERNEL); + struct n_hdlc *n_hdlc = kzalloc(sizeof(*n_hdlc), GFP_KERNEL); if (!n_hdlc) return NULL; - memset(n_hdlc, 0, sizeof(*n_hdlc)); - 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); @@ -952,8 +950,6 @@ static char hdlc_register_ok[] __initdata = KERN_INFO "N_HDLC line discipline registered.\n"; static char hdlc_register_fail[] __initdata = KERN_ERR "error registering line discipline: %d\n"; -static char hdlc_init_fail[] __initdata = - KERN_INFO "N_HDLC: init failure %d\n"; static int __init n_hdlc_init(void) { @@ -973,8 +969,6 @@ static int __init n_hdlc_init(void) else printk(hdlc_register_fail, status); - if (status) - printk(hdlc_init_fail, status); return status; } /* end of init_module() */ diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index fe9d129c8735..f95569dedc88 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2041,7 +2041,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, if (found) clear_bit(eol, ldata->read_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); ldata->read_tail += c; if (found) { diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 2d4bd3929e50..27f7ad6b74c1 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1926,13 +1926,8 @@ static void serial8250_put_poll_char(struct uart_port *port, wait_for_xmitr(up, BOTH_EMPTY); /* * Send the character out. - * If a LF, also do CR... */ serial_port_out(port, UART_TX, c); - if (c == 10) { - wait_for_xmitr(up, BOTH_EMPTY); - serial_port_out(port, UART_TX, 13); - } /* * Finally, wait for transmitter to become empty @@ -2338,9 +2333,11 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, * the trigger, or the MCR RTS bit is cleared. In the case where * the remote UART is not using CTS auto flow control, we must * have sufficient FIFO entries for the latency of the remote - * UART to respond. IOW, at least 32 bytes of FIFO. + * UART to respond. IOW, at least 32 bytes of FIFO. Also enable + * AFE if hw flow control is supported */ - if (up->capabilities & UART_CAP_AFE && port->fifosize >= 32) { + if ((up->capabilities & UART_CAP_AFE && (port->fifosize >= 32)) || + (port->flags & UPF_HARD_FLOW)) { up->mcr &= ~UART_MCR_AFE; if (termios->c_cflag & CRTSCTS) up->mcr |= UART_MCR_AFE; diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index ab9096dc3849..148ffe4c232f 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -192,21 +192,28 @@ int serial8250_request_dma(struct uart_8250_port *p) dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size, &dma->rx_addr, GFP_KERNEL); - if (!dma->rx_buf) { - dma_release_channel(dma->rxchan); - dma_release_channel(dma->txchan); - return -ENOMEM; - } + if (!dma->rx_buf) + goto err; /* TX buffer */ dma->tx_addr = dma_map_single(dma->txchan->device->dev, p->port.state->xmit.buf, UART_XMIT_SIZE, DMA_TO_DEVICE); + if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) { + dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, + dma->rx_buf, dma->rx_addr); + goto err; + } dev_dbg_ratelimited(p->port.dev, "got both dma channels\n"); return 0; +err: + dma_release_channel(dma->rxchan); + dma_release_channel(dma->txchan); + + return -ENOMEM; } EXPORT_SYMBOL_GPL(serial8250_request_dma); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index ed3113576740..51b307aab75e 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -62,6 +62,70 @@ struct dw8250_data { struct uart_8250_dma dma; }; +struct dw8250_acpi_desc { + void (*set_termios)(struct uart_port *p, struct ktermios *termios, + struct ktermios *old); +}; + +#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 void byt_set_termios(struct uart_port *p, struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud = tty_termios_baud_rate(termios); + unsigned int m, n; + u32 reg; + + /* + * For baud rates 0.5M, 1M, 1.5M, 2M, 2.5M, 3M, 3.5M and 4M the + * dividers must be adjusted. + * + * uartclk = (m / n) * 100 MHz, where m <= n + */ + switch (baud) { + case 500000: + case 1000000: + case 2000000: + case 4000000: + m = 64; + n = 100; + p->uartclk = 64000000; + break; + case 3500000: + m = 56; + n = 100; + p->uartclk = 56000000; + break; + case 1500000: + case 3000000: + m = 48; + n = 100; + p->uartclk = 48000000; + break; + case 2500000: + m = 40; + n = 100; + p->uartclk = 40000000; + break; + default: + m = 2304; + n = 3125; + p->uartclk = 73728000; + } + + /* Reset the clock */ + reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT); + writel(reg, p->membase + BYT_PRV_CLK); + reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE; + writel(reg, p->membase + BYT_PRV_CLK); + + serial8250_do_set_termios(p, termios, old); +} + static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value) { struct dw8250_data *d = p->private_data; @@ -278,6 +342,7 @@ static int dw8250_probe_acpi(struct uart_8250_port *up, { const struct acpi_device_id *id; struct uart_port *p = &up->port; + struct dw8250_acpi_desc *acpi_desc; dw8250_setup_port(up); @@ -290,14 +355,18 @@ static int dw8250_probe_acpi(struct uart_8250_port *up, p->serial_out = dw8250_serial_out32; p->regshift = 2; - if (!p->uartclk) - p->uartclk = (unsigned int)id->driver_data; - up->dma = &data->dma; up->dma->rxconf.src_maxburst = p->fifosize / 4; up->dma->txconf.dst_maxburst = p->fifosize / 4; + acpi_desc = (struct dw8250_acpi_desc *)id->driver_data; + if (!acpi_desc) + return 0; + + if (acpi_desc->set_termios) + p->set_termios = acpi_desc->set_termios; + return 0; } @@ -445,12 +514,16 @@ static const struct of_device_id dw8250_of_match[] = { }; MODULE_DEVICE_TABLE(of, dw8250_of_match); +static struct dw8250_acpi_desc byt_8250_desc = { + .set_termios = byt_set_termios, +}; + static const struct acpi_device_id dw8250_acpi_match[] = { { "INT33C4", 0 }, { "INT33C5", 0 }, { "INT3434", 0 }, { "INT3435", 0 }, - { "80860F0A", 0 }, + { "80860F0A", (kernel_ulong_t)&byt_8250_desc}, { }, }; MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match); diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index c100d6343d50..cfef801a49d4 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -35,18 +35,8 @@ #include <linux/serial_8250.h> #include <asm/io.h> #include <asm/serial.h> -#ifdef CONFIG_FIX_EARLYCON_MEM -#include <asm/pgtable.h> -#include <asm/fixmap.h> -#endif -struct early_serial8250_device { - struct uart_port port; - char options[16]; /* e.g., 115200n8 */ - unsigned int baud; -}; - -static struct early_serial8250_device early_device; +static struct earlycon_device *early_device; unsigned int __weak __init serial8250_early_in(struct uart_port *port, int offset) { @@ -100,7 +90,7 @@ static void __init serial_putc(struct uart_port *port, int c) static void __init early_serial8250_write(struct console *console, const char *s, unsigned int count) { - struct uart_port *port = &early_device.port; + struct uart_port *port = &early_device->port; unsigned int ier; /* Save the IER and disable interrupts */ @@ -129,7 +119,7 @@ static unsigned int __init probe_baud(struct uart_port *port) return (port->uartclk / 16) / quot; } -static void __init init_port(struct early_serial8250_device *device) +static void __init init_port(struct earlycon_device *device) { struct uart_port *port = &device->port; unsigned int divisor; @@ -148,128 +138,42 @@ static void __init init_port(struct early_serial8250_device *device) serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB); } -static int __init parse_options(struct early_serial8250_device *device, - char *options) +static int __init early_serial8250_setup(struct earlycon_device *device, + const char *options) { - struct uart_port *port = &device->port; - int mmio, mmio32, length; - - if (!options) - return -ENODEV; - - port->uartclk = BASE_BAUD * 16; - - mmio = !strncmp(options, "mmio,", 5); - mmio32 = !strncmp(options, "mmio32,", 7); - if (mmio || mmio32) { - port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); - port->mapbase = simple_strtoul(options + (mmio ? 5 : 7), - &options, 0); - if (mmio32) - port->regshift = 2; -#ifdef CONFIG_FIX_EARLYCON_MEM - set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, - port->mapbase & PAGE_MASK); - port->membase = - (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); - port->membase += port->mapbase & ~PAGE_MASK; -#else - port->membase = ioremap_nocache(port->mapbase, 64); - if (!port->membase) { - printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n", - __func__, - (unsigned long long) port->mapbase); - return -ENOMEM; - } -#endif - } else if (!strncmp(options, "io,", 3)) { - port->iotype = UPIO_PORT; - port->iobase = simple_strtoul(options + 3, &options, 0); - mmio = 0; - } else - return -EINVAL; - - options = strchr(options, ','); - if (options) { - options++; - device->baud = simple_strtoul(options, NULL, 0); - length = min(strcspn(options, " ") + 1, - (size_t)(sizeof(device->options))); - strlcpy(device->options, options, length); - } else { - device->baud = probe_baud(port); - snprintf(device->options, sizeof(device->options), "%u", - device->baud); - } - - if (mmio || mmio32) - printk(KERN_INFO - "Early serial console at MMIO%s 0x%llx (options '%s')\n", - mmio32 ? "32" : "", - (unsigned long long)port->mapbase, - device->options); - else - printk(KERN_INFO - "Early serial console at I/O port 0x%lx (options '%s')\n", - port->iobase, - device->options); - - return 0; -} - -static struct console early_serial8250_console __initdata = { - .name = "uart", - .write = early_serial8250_write, - .flags = CON_PRINTBUFFER | CON_BOOT, - .index = -1, -}; - -static int __init early_serial8250_setup(char *options) -{ - struct early_serial8250_device *device = &early_device; - int err; - - if (device->port.membase || device->port.iobase) + if (!(device->port.membase || device->port.iobase)) return 0; - err = parse_options(device, options); - if (err < 0) - return err; + if (!device->baud) + device->baud = probe_baud(&device->port); init_port(device); + + early_device = device; + device->con->write = early_serial8250_write; return 0; } +EARLYCON_DECLARE(uart8250, early_serial8250_setup); +EARLYCON_DECLARE(uart, early_serial8250_setup); int __init setup_early_serial8250_console(char *cmdline) { - char *options; - int err; + char match[] = "uart8250"; - options = strstr(cmdline, "uart8250,"); - if (!options) { - options = strstr(cmdline, "uart,"); - if (!options) - return 0; - } + if (cmdline && cmdline[4] == ',') + match[4] = '\0'; - options = strchr(cmdline, ',') + 1; - err = early_serial8250_setup(options); - if (err < 0) - return err; - - register_console(&early_serial8250_console); - - return 0; + return setup_earlycon(cmdline, match, early_serial8250_setup); } int serial8250_find_port_for_earlycon(void) { - struct early_serial8250_device *device = &early_device; - struct uart_port *port = &device->port; + struct earlycon_device *device = early_device; + struct uart_port *port = device ? &device->port : NULL; int line; int ret; - if (!device->port.membase && !device->port.iobase) + if (!port || (!port->membase && !port->iobase)) return -ENODEV; line = serial8250_find_port(port); @@ -284,5 +188,3 @@ int serial8250_find_port_for_earlycon(void) return ret; } - -early_param("earlycon", setup_early_serial8250_console); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index b14bcba96c25..33137b3ba94d 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1753,6 +1753,8 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_VENDOR_ID_ADVANTECH 0x13fe #define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66 #define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620 +#define PCI_DEVICE_ID_ADVANTECH_PCI3618 0x3618 +#define PCI_DEVICE_ID_ADVANTECH_PCIf618 0xf618 #define PCI_DEVICE_ID_TITAN_200I 0x8028 #define PCI_DEVICE_ID_TITAN_400I 0x8048 #define PCI_DEVICE_ID_TITAN_800I 0x8088 @@ -1778,6 +1780,7 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_DEVICE_ID_WCH_CH352_2S 0x3253 #define PCI_DEVICE_ID_WCH_CH353_4S 0x3453 #define PCI_DEVICE_ID_WCH_CH353_2S1PF 0x5046 +#define PCI_DEVICE_ID_WCH_CH353_1S1P 0x5053 #define PCI_DEVICE_ID_WCH_CH353_2S1P 0x7053 #define PCI_VENDOR_ID_AGESTAR 0x5372 #define PCI_DEVICE_ID_AGESTAR_9375 0x6872 @@ -2410,6 +2413,14 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = pci_omegapci_setup, }, + /* WCH CH353 1S1P card (16550 clone) */ + { + .vendor = PCI_VENDOR_ID_WCH, + .device = PCI_DEVICE_ID_WCH_CH353_1S1P, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch353_setup, + }, /* WCH CH353 2S1P card (16550 clone) */ { .vendor = PCI_VENDOR_ID_WCH, @@ -3526,6 +3537,7 @@ static const struct pci_device_id blacklist[] = { /* multi-io cards handled by parport_serial */ { PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */ + { PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */ }; /* @@ -3880,6 +3892,13 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620, PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001, 0, 0, pbn_b2_8_921600 }, + /* Advantech also use 0x3618 and 0xf618 */ + { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3618, + PCI_DEVICE_ID_ADVANTECH_PCI3618, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCIf618, + PCI_DEVICE_ID_ADVANTECH_PCI3618, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 23329918f229..349ee598b34c 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -61,6 +61,7 @@ config SERIAL_8250_CONSOLE bool "Console on 8250/16550 and compatible serial port" depends on SERIAL_8250=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON ---help--- If you say Y here, it will be possible to use a serial port as the system console (the system console is the device which receives all @@ -90,11 +91,6 @@ config SERIAL_8250_CONSOLE If unsure, say N. -config FIX_EARLYCON_MEM - bool - depends on X86 - default y - config SERIAL_8250_GSC tristate depends on SERIAL_8250 && GSC diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 5d9b01aa54f4..fb57159bad3a 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -7,6 +7,13 @@ if TTY menu "Serial drivers" depends on HAS_IOMEM +config SERIAL_EARLYCON + bool + help + Support for early consoles with the earlycon parameter. This enables + the console before standard serial driver is probed. The console is + enabled when early_param is processed. + source "drivers/tty/serial/8250/Kconfig" comment "Non-8250 serial port support" @@ -53,6 +60,7 @@ config SERIAL_AMBA_PL011_CONSOLE bool "Support for console on AMBA serial port" depends on SERIAL_AMBA_PL011=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON ---help--- Say Y here if you wish to use an AMBA PrimeCell UART as the system console (the system console is the device which receives all kernel @@ -65,6 +73,18 @@ config SERIAL_AMBA_PL011_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) +config SERIAL_EARLYCON_ARM_SEMIHOST + bool "Early console using ARM semihosting" + depends on ARM64 || ARM + select SERIAL_CORE + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + help + Support for early debug console using ARM semihosting. This enables + the console before standard serial driver is probed. This is enabled + with "earlycon=smh" on the kernel command line. The console is + enabled when early_param is processed. + config SERIAL_SB1250_DUART tristate "BCM1xxx on-chip DUART serial support" depends on SIBYTE_SB1xxx_SOC=y @@ -97,6 +117,7 @@ config SERIAL_ATMEL bool "AT91 / AT32 on-chip serial port support" depends on ARCH_AT91 || AVR32 select SERIAL_CORE + select SERIAL_MCTRL_GPIO help This enables the driver for the on-chip UARTs of the Atmel AT91 and AT32 processors. @@ -1160,6 +1181,16 @@ config SERIAL_SCCNXP_CONSOLE help Support for console on SCCNXP serial ports. +config SERIAL_SC16IS7XX + tristate "SC16IS7xx serial support" + depends on I2C + select SERIAL_CORE + select REGMAP_I2C if I2C + help + This selects support for SC16IS7xx serial ports. + Supported ICs are SC16IS740, SC16IS741, SC16IS750, SC16IS752, + SC16IS760 and SC16IS762. + config SERIAL_BFIN_SPORT tristate "Blackfin SPORT emulate UART" depends on BLACKFIN @@ -1323,7 +1354,7 @@ config SERIAL_IFX6X60 config SERIAL_PCH_UART tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) UART" - depends on PCI + depends on PCI && (X86_32 || COMPILE_TEST) select SERIAL_CORE help This driver is for PCH(Platform controller Hub) UART of Intel EG20T @@ -1369,18 +1400,19 @@ config SERIAL_MXS_AUART_CONSOLE Enable a MXS AUART port to be the system console. config SERIAL_XILINX_PS_UART - tristate "Xilinx PS UART support" + tristate "Cadence (Xilinx Zynq) UART support" depends on OF select SERIAL_CORE help - This driver supports the Xilinx PS UART port. + This driver supports the Cadence UART. It is found e.g. in Xilinx + Zynq. config SERIAL_XILINX_PS_UART_CONSOLE - bool "Xilinx PS UART console support" + bool "Cadence UART console support" depends on SERIAL_XILINX_PS_UART=y select SERIAL_CORE_CONSOLE help - Enable a Xilinx PS UART port to be the system console. + Enable a Cadence UART port to be the system console. config SERIAL_AR933X tristate "AR933X serial port support" @@ -1479,6 +1511,7 @@ config SERIAL_RP2_NR_UARTS config SERIAL_FSL_LPUART tristate "Freescale lpuart serial port support" + depends on HAS_DMA select SERIAL_CORE help Support for the on-chip lpuart on some Freescale SOCs. @@ -1508,6 +1541,20 @@ config SERIAL_ST_ASC_CONSOLE depends on SERIAL_ST_ASC=y select SERIAL_CORE_CONSOLE +config SERIAL_MEN_Z135 + tristate "MEN 16z135 Support" + select SERIAL_CORE + depends on MCB + help + Say yes here to enable support for the MEN 16z135 High Speed UART IP-Core + on a MCB carrier. + + This driver can also be build as a module. If so, the module will be called + men_z135_uart.ko + endmenu +config SERIAL_MCTRL_GPIO + tristate + endif # TTY diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 3680854fef41..0080cc362e09 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -5,6 +5,9 @@ obj-$(CONFIG_SERIAL_CORE) += serial_core.o obj-$(CONFIG_SERIAL_21285) += 21285.o +obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o +obj-$(CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST) += earlycon-arm-semihost.o + # These Sparc drivers have to appear before others such as 8250 # which share ttySx minor node space. Otherwise console device # names change and other unplesantries. @@ -48,6 +51,7 @@ obj-$(CONFIG_SERIAL_MPSC) += mpsc.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o +obj-$(CONFIG_SERIAL_SC16IS7XX) += sc16is7xx.o obj-$(CONFIG_SERIAL_JSM) += jsm/ obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o @@ -87,3 +91,7 @@ obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o obj-$(CONFIG_SERIAL_ARC) += arc_uart.o obj-$(CONFIG_SERIAL_RP2) += rp2.o obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o +obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_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 dacf0a09ab24..908a6e3142a2 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -303,7 +303,7 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port * /* Optionally make use of an RX channel as well */ chan = dma_request_slave_channel(dev, "rx"); - + if (!chan && plat->dma_rx_param) { chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param); @@ -2045,6 +2045,35 @@ static struct console amba_console = { }; #define AMBA_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); + while (readl(port->membase + UART01x_FR) & UART01x_FR_BUSY) + ; +} + +static void pl011_early_write(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, pl011_putc); +} + +static int __init pl011_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + 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 #define AMBA_CONSOLE NULL #endif diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 53eeea13ff16..3fceae099c44 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -43,6 +43,9 @@ #include <linux/platform_data/atmel.h> #include <linux/timer.h> #include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/err.h> +#include <linux/irq.h> #include <asm/io.h> #include <asm/ioctls.h> @@ -57,6 +60,8 @@ #include <linux/serial_core.h> +#include "serial_mctrl_gpio.h" + static void atmel_start_rx(struct uart_port *port); static void atmel_stop_rx(struct uart_port *port); @@ -162,8 +167,10 @@ struct atmel_uart_port { struct circ_buf rx_ring; struct serial_rs485 rs485; /* rs485 settings */ - int rts_gpio; /* optional RTS GPIO */ + struct mctrl_gpios *gpios; + int gpio_irq[UART_GPIO_MAX]; unsigned int tx_done_mask; + bool ms_irq_enabled; bool is_usart; /* usart or uart */ struct timer_list uart_timer; /* uart timer */ int (*prepare_rx)(struct uart_port *port); @@ -237,6 +244,50 @@ static bool atmel_use_dma_rx(struct uart_port *port) return atmel_port->use_dma_rx; } +static unsigned int atmel_get_lines_status(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int status, ret = 0; + + status = UART_GET_CSR(port); + + mctrl_gpio_get(atmel_port->gpios, &ret); + + if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios, + UART_GPIO_CTS))) { + if (ret & TIOCM_CTS) + status &= ~ATMEL_US_CTS; + else + status |= ATMEL_US_CTS; + } + + if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios, + UART_GPIO_DSR))) { + if (ret & TIOCM_DSR) + status &= ~ATMEL_US_DSR; + else + status |= ATMEL_US_DSR; + } + + if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios, + UART_GPIO_RI))) { + if (ret & TIOCM_RI) + status &= ~ATMEL_US_RI; + else + status |= ATMEL_US_RI; + } + + if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios, + UART_GPIO_DCD))) { + if (ret & TIOCM_CD) + status &= ~ATMEL_US_DCD; + else + status |= ATMEL_US_DCD; + } + + return status; +} + /* Enable or disable the rs485 support */ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) { @@ -296,17 +347,6 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) unsigned int mode; struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - /* - * AT91RM9200 Errata #39: RTS0 is not internally connected - * to PA21. We need to drive the pin as a GPIO. - */ - if (gpio_is_valid(atmel_port->rts_gpio)) { - if (mctrl & TIOCM_RTS) - gpio_set_value(atmel_port->rts_gpio, 0); - else - gpio_set_value(atmel_port->rts_gpio, 1); - } - if (mctrl & TIOCM_RTS) control |= ATMEL_US_RTSEN; else @@ -319,6 +359,8 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) UART_PUT_CR(port, control); + mctrl_gpio_set(atmel_port->gpios, mctrl); + /* Local loopback mode? */ mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE; if (mctrl & TIOCM_LOOP) @@ -346,7 +388,8 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) */ static u_int atmel_get_mctrl(struct uart_port *port) { - unsigned int status, ret = 0; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int ret = 0, status; status = UART_GET_CSR(port); @@ -362,7 +405,7 @@ static u_int atmel_get_mctrl(struct uart_port *port) if (!(status & ATMEL_US_RI)) ret |= TIOCM_RI; - return ret; + return mctrl_gpio_get(atmel_port->gpios, &ret); } /* @@ -449,8 +492,38 @@ static void atmel_stop_rx(struct uart_port *port) */ static void atmel_enable_ms(struct uart_port *port) { - UART_PUT_IER(port, ATMEL_US_RIIC | ATMEL_US_DSRIC - | ATMEL_US_DCDIC | ATMEL_US_CTSIC); + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + uint32_t ier = 0; + + /* + * Interrupt should not be enabled twice + */ + if (atmel_port->ms_irq_enabled) + return; + + 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 + ier |= ATMEL_US_CTSIC; + + if (atmel_port->gpio_irq[UART_GPIO_DSR] >= 0) + enable_irq(atmel_port->gpio_irq[UART_GPIO_DSR]); + else + ier |= ATMEL_US_DSRIC; + + if (atmel_port->gpio_irq[UART_GPIO_RI] >= 0) + enable_irq(atmel_port->gpio_irq[UART_GPIO_RI]); + else + ier |= ATMEL_US_RIIC; + + if (atmel_port->gpio_irq[UART_GPIO_DCD] >= 0) + enable_irq(atmel_port->gpio_irq[UART_GPIO_DCD]); + else + ier |= ATMEL_US_DCDIC; + + UART_PUT_IER(port, ier); } /* @@ -1039,11 +1112,31 @@ atmel_handle_status(struct uart_port *port, unsigned int pending, 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, pass_counter = 0; + bool gpio_handled = false; do { - status = UART_GET_CSR(port); + status = atmel_get_lines_status(port); pending = status & UART_GET_IMR(port); + 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; @@ -1523,6 +1616,45 @@ 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 */ @@ -1539,6 +1671,7 @@ static int atmel_startup(struct uart_port *port) * handle an unexpected interrupt */ UART_PUT_IDR(port, -1); + atmel_port->ms_irq_enabled = false; /* * Allocate the IRQ @@ -1551,6 +1684,13 @@ static int atmel_startup(struct uart_port *port) } /* + * Get the GPIO lines IRQ + */ + retval = atmel_request_gpio_irq(port); + if (retval) + goto free_irq; + + /* * Initialize DMA (if necessary) */ atmel_init_property(atmel_port, pdev); @@ -1568,7 +1708,7 @@ static int atmel_startup(struct uart_port *port) } /* Save current CSR for comparison in atmel_tasklet_func() */ - atmel_port->irq_status_prev = UART_GET_CSR(port); + atmel_port->irq_status_prev = atmel_get_lines_status(port); atmel_port->irq_status = atmel_port->irq_status_prev; /* @@ -1614,6 +1754,11 @@ static int atmel_startup(struct uart_port *port) } return 0; + +free_irq: + free_irq(port->irq, port); + + return retval; } /* @@ -1661,9 +1806,12 @@ static void atmel_shutdown(struct uart_port *port) atmel_port->rx_ring.tail = 0; /* - * Free the interrupt + * Free the interrupts */ free_irq(port->irq, port); + atmel_free_gpio_irq(port); + + atmel_port->ms_irq_enabled = false; } /* @@ -2324,6 +2472,26 @@ 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(dev, 0); + if (IS_ERR_OR_NULL(p->gpios)) + return -1; + + 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 int atmel_serial_probe(struct platform_device *pdev) { struct atmel_uart_port *port; @@ -2359,25 +2527,11 @@ static int atmel_serial_probe(struct platform_device *pdev) port = &atmel_ports[ret]; port->backup_imr = 0; port->uart.line = ret; - port->rts_gpio = -EINVAL; /* Invalid, zero could be valid */ - if (pdata) - port->rts_gpio = pdata->rts_gpio; - else if (np) - port->rts_gpio = of_get_named_gpio(np, "rts-gpios", 0); - - if (gpio_is_valid(port->rts_gpio)) { - ret = devm_gpio_request(&pdev->dev, port->rts_gpio, "RTS"); - if (ret) { - dev_err(&pdev->dev, "error requesting RTS GPIO\n"); - goto err; - } - /* Default to 1 as RTS is active low */ - ret = gpio_direction_output(port->rts_gpio, 1); - if (ret) { - dev_err(&pdev->dev, "error setting up RTS GPIO\n"); - goto err; - } - } + + ret = atmel_init_gpios(port, &pdev->dev); + if (ret < 0) + dev_err(&pdev->dev, "%s", + "Failed to initialize GPIOs. The serial port may not work as expected"); ret = atmel_init_port(port, pdev); if (ret) diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index 7d76214612c7..aa60e6d13eca 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -971,7 +971,7 @@ static void cpm_uart_config_port(struct uart_port *port, int flags) * Note that this is called with interrupts already disabled */ static void cpm_uart_early_write(struct uart_cpm_port *pinfo, - const char *string, u_int count) + const char *string, u_int count, bool handle_linefeed) { unsigned int i; cbd_t __iomem *bdp, *bdbase; @@ -1013,7 +1013,7 @@ static void cpm_uart_early_write(struct uart_cpm_port *pinfo, bdp++; /* if a LF, also do CR... */ - if (*string == 10) { + if (handle_linefeed && *string == 10) { while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0) ; @@ -1111,7 +1111,7 @@ static void cpm_put_poll_char(struct uart_port *port, static char ch[2]; ch[0] = (char)c; - cpm_uart_early_write(pinfo, ch, 1); + cpm_uart_early_write(pinfo, ch, 1, false); } #endif /* CONFIG_CONSOLE_POLL */ @@ -1275,7 +1275,7 @@ static void cpm_uart_console_write(struct console *co, const char *s, spin_lock_irqsave(&pinfo->port.lock, flags); } - cpm_uart_early_write(pinfo, s, count); + cpm_uart_early_write(pinfo, s, count, true); if (unlikely(nolock)) { local_irq_restore(flags); diff --git a/drivers/tty/serial/earlycon-arm-semihost.c b/drivers/tty/serial/earlycon-arm-semihost.c new file mode 100644 index 000000000000..383db10fbb49 --- /dev/null +++ b/drivers/tty/serial/earlycon-arm-semihost.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012 ARM Ltd. + * Author: Marc Zyngier <marc.zyngier@arm.com> + * + * Adapted for ARM and earlycon: + * Copyright (C) 2014 Linaro Ltd. + * Author: Rob Herring <robh@kernel.org> + * + * 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. + * + * 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/kernel.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/serial_core.h> + +#ifdef CONFIG_THUMB2_KERNEL +#define SEMIHOST_SWI "0xab" +#else +#define SEMIHOST_SWI "0x123456" +#endif + +/* + * Semihosting-based debug console + */ +static void smh_putc(struct uart_port *port, int c) +{ +#ifdef CONFIG_ARM64 + asm volatile("mov x1, %0\n" + "mov x0, #3\n" + "hlt 0xf000\n" + : : "r" (&c) : "x0", "x1", "memory"); +#else + asm volatile("mov r1, %0\n" + "mov r0, #3\n" + "svc " SEMIHOST_SWI "\n" + : : "r" (&c) : "r0", "r1", "memory"); +#endif +} + +static void smh_write(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + uart_console_write(&dev->port, s, n, smh_putc); +} + +int __init early_smh_setup(struct earlycon_device *device, const char *opt) +{ + device->con->write = smh_write; + return 0; +} +EARLYCON_DECLARE(smh, early_smh_setup); diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c new file mode 100644 index 000000000000..5131b5ee6164 --- /dev/null +++ b/drivers/tty/serial/earlycon.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2014 Linaro Ltd. + * Author: Rob Herring <robh@kernel.org> + * + * Based on 8250 earlycon: + * (c) Copyright 2004 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas <bjorn.helgaas@hp.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/console.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/serial_core.h> +#include <linux/sizes.h> +#include <linux/mod_devicetable.h> + +#ifdef CONFIG_FIX_EARLYCON_MEM +#include <asm/fixmap.h> +#endif + +#include <asm/serial.h> + +static struct console early_con = { + .name = "earlycon", + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1, +}; + +static struct earlycon_device early_console_dev = { + .con = &early_con, +}; + +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; +#ifdef CONFIG_FIX_EARLYCON_MEM + set_fixmap_io(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK); + base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); + base += paddr & ~PAGE_MASK; +#else + base = ioremap(paddr, size); +#endif + if (!base) + pr_err("%s: Couldn't map 0x%llx\n", __func__, + (unsigned long long)paddr); + + return base; +} + +static int __init parse_options(struct earlycon_device *device, + char *options) +{ + struct uart_port *port = &device->port; + int mmio, mmio32, length; + unsigned long addr; + + if (!options) + return -ENODEV; + + mmio = !strncmp(options, "mmio,", 5); + mmio32 = !strncmp(options, "mmio32,", 7); + if (mmio || mmio32) { + port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); + options += mmio ? 5 : 7; + addr = simple_strtoul(options, NULL, 0); + port->mapbase = addr; + if (mmio32) + port->regshift = 2; + } else if (!strncmp(options, "io,", 3)) { + port->iotype = UPIO_PORT; + options += 3; + addr = simple_strtoul(options, NULL, 0); + port->iobase = addr; + mmio = 0; + } else if (!strncmp(options, "0x", 2)) { + port->iotype = UPIO_MEM; + addr = simple_strtoul(options, NULL, 0); + port->mapbase = addr; + } else { + return -EINVAL; + } + + port->uartclk = BASE_BAUD * 16; + + options = strchr(options, ','); + if (options) { + options++; + device->baud = simple_strtoul(options, NULL, 0); + length = min(strcspn(options, " ") + 1, + (size_t)(sizeof(device->options))); + strlcpy(device->options, options, length); + } + + if (mmio || mmio32) + pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n", + mmio32 ? "32" : "", + (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; +} + +int __init setup_earlycon(char *buf, const char *match, + int (*setup)(struct earlycon_device *, const char *)) +{ + int err; + size_t len; + struct uart_port *port = &early_console_dev.port; + + if (!buf || !match || !setup) + return 0; + + len = strlen(match); + if (strncmp(buf, match, len)) + return 0; + if (buf[len] && (buf[len] != ',')) + return 0; + + buf += len + 1; + + err = parse_options(&early_console_dev, buf); + /* On parsing error, pass the options buf to the setup function */ + if (!err) + buf = NULL; + + if (port->mapbase) + port->membase = earlycon_map(port->mapbase, 64); + + early_console_dev.con->data = &early_console_dev; + err = setup(&early_console_dev, buf); + if (err < 0) + return err; + if (!early_console_dev.con->write) + return -ENODEV; + + register_console(early_console_dev.con); + return 0; +} + +int __init of_setup_earlycon(unsigned long addr, + int (*setup)(struct earlycon_device *, const char *)) +{ + int err; + struct uart_port *port = &early_console_dev.port; + + port->iotype = UPIO_MEM; + port->mapbase = addr; + port->uartclk = BASE_BAUD * 16; + port->membase = earlycon_map(addr, SZ_4K); + + early_console_dev.con->data = &early_console_dev; + err = setup(&early_console_dev, NULL); + if (err < 0) + return err; + if (!early_console_dev.con->write) + return -ENODEV; + + + register_console(early_console_dev.con); + return 0; +} diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c index c167a710dc39..b373f6416e8c 100644 --- a/drivers/tty/serial/efm32-uart.c +++ b/drivers/tty/serial/efm32-uart.c @@ -842,6 +842,7 @@ static void __exit efm32_uart_exit(void) platform_driver_unregister(&efm32_uart_driver); uart_unregister_driver(&efm32_uart_reg); } +module_exit(efm32_uart_exit); MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>"); MODULE_DESCRIPTION("EFM32 UART/USART driver"); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 3b6c1a2e25de..e2f93874989b 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -116,7 +116,7 @@ #define UCR3_DSR (1<<10) /* Data set ready */ #define UCR3_DCD (1<<9) /* Data carrier detect */ #define UCR3_RI (1<<8) /* Ring indicator */ -#define UCR3_TIMEOUTEN (1<<7) /* Timeout interrupt enable */ +#define UCR3_ADNIMP (1<<7) /* Autobaud Detection Not Improved */ #define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */ #define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */ #define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */ @@ -444,6 +444,10 @@ static void imx_stop_rx(struct uart_port *port) temp = readl(sport->port.membase + UCR2); writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2); + + /* disable the `Receiver Ready Interrrupt` */ + temp = readl(sport->port.membase + UCR1); + writel(temp & ~UCR1_RRDYEN, sport->port.membase + UCR1); } /* @@ -1070,7 +1074,7 @@ static void imx_disable_dma(struct imx_port *sport) static int imx_startup(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; - int retval; + int retval, i; unsigned long flags, temp; retval = clk_prepare_enable(sport->clk_per); @@ -1098,17 +1102,15 @@ static int imx_startup(struct uart_port *port) writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); - if (USE_IRDA(sport)) { - /* reset fifo's and state machines */ - int i = 100; - temp = readl(sport->port.membase + UCR2); - temp &= ~UCR2_SRST; - writel(temp, sport->port.membase + UCR2); - while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && - (--i > 0)) { - udelay(1); - } - } + /* Reset fifo's and state machines */ + i = 100; + + temp = readl(sport->port.membase + UCR2); + temp &= ~UCR2_SRST; + writel(temp, sport->port.membase + UCR2); + + while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0)) + udelay(1); /* * Allocate the IRQ(s) i.MX1 has three interrupts whereas later @@ -1163,18 +1165,9 @@ static int imx_startup(struct uart_port *port) temp |= UCR2_IRTS; writel(temp, sport->port.membase + UCR2); - if (USE_IRDA(sport)) { - /* clear RX-FIFO */ - int i = 64; - while ((--i > 0) && - (readl(sport->port.membase + URXD0) & URXD_CHARRDY)) { - barrier(); - } - } - if (!is_imx1_uart(sport)) { temp = readl(sport->port.membase + UCR3); - temp |= IMX21_UCR3_RXDMUXSEL; + temp |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP; writel(temp, sport->port.membase + UCR3); } diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c index 5f673b7ca50e..cfadf2971b12 100644 --- a/drivers/tty/serial/kgdb_nmi.c +++ b/drivers/tty/serial/kgdb_nmi.c @@ -44,13 +44,22 @@ MODULE_PARM_DESC(magic, "magic sequence to enter NMI debugger (default $3#33)"); static bool kgdb_nmi_tty_enabled; +static int kgdb_nmi_console_setup(struct console *co, char *options) +{ + /* The NMI console uses the dbg_io_ops to issue console messages. To + * avoid duplicate messages during kdb sessions we must inform kdb's + * I/O utilities that messages sent to the console will automatically + * be displayed on the dbg_io. + */ + dbg_io_ops->is_console = true; + + return 0; +} + static void kgdb_nmi_console_write(struct console *co, const char *s, uint c) { int i; - if (!kgdb_nmi_tty_enabled || atomic_read(&kgdb_active) >= 0) - return; - for (i = 0; i < c; i++) dbg_io_ops->write_char(s[i]); } @@ -65,6 +74,7 @@ static struct tty_driver *kgdb_nmi_console_device(struct console *co, int *idx) static struct console kgdb_nmi_console = { .name = "ttyNMI", + .setup = kgdb_nmi_console_setup, .write = kgdb_nmi_console_write, .device = kgdb_nmi_console_device, .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED, @@ -80,29 +90,10 @@ static struct console kgdb_nmi_console = { struct kgdb_nmi_tty_priv { struct tty_port port; - struct tasklet_struct tlet; + struct timer_list timer; STRUCT_KFIFO(char, KGDB_NMI_FIFO_SIZE) fifo; }; -static struct kgdb_nmi_tty_priv *kgdb_nmi_port_to_priv(struct tty_port *port) -{ - return container_of(port, struct kgdb_nmi_tty_priv, port); -} - -/* - * Our debugging console is polled in a tasklet, so we'll check for input - * every tick. In HZ-less mode, we should program the next tick. We have - * to use the lowlevel stuff as no locks should be grabbed. - */ -#ifdef CONFIG_HIGH_RES_TIMERS -static void kgdb_tty_poke(void) -{ - tick_program_event(ktime_get(), 0); -} -#else -static inline void kgdb_tty_poke(void) {} -#endif - static struct tty_port *kgdb_nmi_port; static void kgdb_tty_recv(int ch) @@ -113,14 +104,13 @@ static void kgdb_tty_recv(int ch) if (!kgdb_nmi_port || ch < 0) return; /* - * Can't use port->tty->driver_data as tty might be not there. Tasklet + * Can't use port->tty->driver_data as tty might be not there. Timer * will check for tty and will get the ref, but here we don't have to * do that, and actually, we can't: we're in NMI context, no locks are * possible. */ - priv = kgdb_nmi_port_to_priv(kgdb_nmi_port); + priv = container_of(kgdb_nmi_port, struct kgdb_nmi_tty_priv, port); kfifo_in(&priv->fifo, &c, 1); - kgdb_tty_poke(); } static int kgdb_nmi_poll_one_knock(void) @@ -204,7 +194,8 @@ static void kgdb_nmi_tty_receiver(unsigned long data) struct kgdb_nmi_tty_priv *priv = (void *)data; char ch; - tasklet_schedule(&priv->tlet); + priv->timer.expires = jiffies + (HZ/100); + add_timer(&priv->timer); if (likely(!kgdb_nmi_tty_enabled || !kfifo_len(&priv->fifo))) return; @@ -216,18 +207,22 @@ static void kgdb_nmi_tty_receiver(unsigned long data) static int kgdb_nmi_tty_activate(struct tty_port *port, struct tty_struct *tty) { - struct kgdb_nmi_tty_priv *priv = tty->driver_data; + struct kgdb_nmi_tty_priv *priv = + container_of(port, struct kgdb_nmi_tty_priv, port); kgdb_nmi_port = port; - tasklet_schedule(&priv->tlet); + priv->timer.expires = jiffies + (HZ/100); + add_timer(&priv->timer); + return 0; } static void kgdb_nmi_tty_shutdown(struct tty_port *port) { - struct kgdb_nmi_tty_priv *priv = port->tty->driver_data; + struct kgdb_nmi_tty_priv *priv = + container_of(port, struct kgdb_nmi_tty_priv, port); - tasklet_kill(&priv->tlet); + del_timer(&priv->timer); kgdb_nmi_port = NULL; } @@ -246,7 +241,7 @@ static int kgdb_nmi_tty_install(struct tty_driver *drv, struct tty_struct *tty) return -ENOMEM; INIT_KFIFO(priv->fifo); - tasklet_init(&priv->tlet, kgdb_nmi_tty_receiver, (unsigned long)priv); + setup_timer(&priv->timer, kgdb_nmi_tty_receiver, (unsigned long)priv); tty_port_init(&priv->port); priv->port.ops = &kgdb_nmi_tty_port_ops; tty->driver_data = priv; diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c new file mode 100644 index 000000000000..c9d18548783a --- /dev/null +++ b/drivers/tty/serial/men_z135_uart.c @@ -0,0 +1,867 @@ +/* + * MEN 16z135 High Speed UART + * + * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) + * Author: Johannes Thumshirn <johannes.thumshirn@men.de> + * + * 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; version 2 of the License. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ":" fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/serial_core.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/tty_flip.h> +#include <linux/bitops.h> +#include <linux/mcb.h> + +#define MEN_Z135_MAX_PORTS 12 +#define MEN_Z135_BASECLK 29491200 +#define MEN_Z135_FIFO_SIZE 1024 +#define MEN_Z135_NUM_MSI_VECTORS 2 +#define MEN_Z135_FIFO_WATERMARK 1020 + +#define MEN_Z135_STAT_REG 0x0 +#define MEN_Z135_RX_RAM 0x4 +#define MEN_Z135_TX_RAM 0x400 +#define MEN_Z135_RX_CTRL 0x800 +#define MEN_Z135_TX_CTRL 0x804 +#define MEN_Z135_CONF_REG 0x808 +#define MEN_Z135_UART_FREQ 0x80c +#define MEN_Z135_BAUD_REG 0x810 +#define MENZ135_TIMEOUT 0x814 + +#define MEN_Z135_MEM_SIZE 0x818 + +#define IS_IRQ(x) ((x) & 1) +#define IRQ_ID(x) (((x) >> 1) & 7) + +#define MEN_Z135_IER_RXCIEN BIT(0) /* RX Space IRQ */ +#define MEN_Z135_IER_TXCIEN BIT(1) /* TX Space IRQ */ +#define MEN_Z135_IER_RLSIEN BIT(2) /* Receiver Line Status IRQ */ +#define MEN_Z135_IER_MSIEN BIT(3) /* Modem Status IRQ */ +#define MEN_Z135_ALL_IRQS (MEN_Z135_IER_RXCIEN \ + | MEN_Z135_IER_RLSIEN \ + | MEN_Z135_IER_MSIEN \ + | MEN_Z135_IER_TXCIEN) + +#define MEN_Z135_MCR_DTR BIT(24) +#define MEN_Z135_MCR_RTS BIT(25) +#define MEN_Z135_MCR_OUT1 BIT(26) +#define MEN_Z135_MCR_OUT2 BIT(27) +#define MEN_Z135_MCR_LOOP BIT(28) +#define MEN_Z135_MCR_RCFC BIT(29) + +#define MEN_Z135_MSR_DCTS BIT(0) +#define MEN_Z135_MSR_DDSR BIT(1) +#define MEN_Z135_MSR_DRI BIT(2) +#define MEN_Z135_MSR_DDCD BIT(3) +#define MEN_Z135_MSR_CTS BIT(4) +#define MEN_Z135_MSR_DSR BIT(5) +#define MEN_Z135_MSR_RI BIT(6) +#define MEN_Z135_MSR_DCD BIT(7) + +#define MEN_Z135_LCR_SHIFT 8 /* LCR shift mask */ + +#define MEN_Z135_WL5 0 /* CS5 */ +#define MEN_Z135_WL6 1 /* CS6 */ +#define MEN_Z135_WL7 2 /* CS7 */ +#define MEN_Z135_WL8 3 /* CS8 */ + +#define MEN_Z135_STB_SHIFT 2 /* Stopbits */ +#define MEN_Z135_NSTB1 0 +#define MEN_Z135_NSTB2 1 + +#define MEN_Z135_PEN_SHIFT 3 /* Parity enable */ +#define MEN_Z135_PAR_DIS 0 +#define MEN_Z135_PAR_ENA 1 + +#define MEN_Z135_PTY_SHIFT 4 /* Parity type */ +#define MEN_Z135_PTY_ODD 0 +#define MEN_Z135_PTY_EVN 1 + +#define MEN_Z135_LSR_DR BIT(0) +#define MEN_Z135_LSR_OE BIT(1) +#define MEN_Z135_LSR_PE BIT(2) +#define MEN_Z135_LSR_FE BIT(3) +#define MEN_Z135_LSR_BI BIT(4) +#define MEN_Z135_LSR_THEP BIT(5) +#define MEN_Z135_LSR_TEXP BIT(6) +#define MEN_Z135_LSR_RXFIFOERR BIT(7) + +#define MEN_Z135_IRQ_ID_MST 0 +#define MEN_Z135_IRQ_ID_TSA 1 +#define MEN_Z135_IRQ_ID_RDA 2 +#define MEN_Z135_IRQ_ID_RLS 3 +#define MEN_Z135_IRQ_ID_CTI 6 + +#define LCR(x) (((x) >> MEN_Z135_LCR_SHIFT) & 0xff) + +#define BYTES_TO_ALIGN(x) ((x) & 0x3) + +static int line; + +static int txlvl = 5; +module_param(txlvl, int, S_IRUGO); +MODULE_PARM_DESC(txlvl, "TX IRQ trigger level 0-7, default 5 (128 byte)"); + +static int rxlvl = 6; +module_param(rxlvl, int, S_IRUGO); +MODULE_PARM_DESC(rxlvl, "RX IRQ trigger level 0-7, default 6 (256 byte)"); + +static int align; +module_param(align, int, S_IRUGO); +MODULE_PARM_DESC(align, "Keep hardware FIFO write pointer aligned, default 0"); + +struct men_z135_port { + struct uart_port port; + struct mcb_device *mdev; + unsigned char *rxbuf; + u32 stat_reg; + spinlock_t lock; +}; +#define to_men_z135(port) container_of((port), struct men_z135_port, port) + +/** + * men_z135_reg_set() - Set value in register + * @uart: The UART port + * @addr: Register address + * @val: value to set + */ +static inline void men_z135_reg_set(struct men_z135_port *uart, + u32 addr, u32 val) +{ + struct uart_port *port = &uart->port; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&uart->lock, flags); + + reg = ioread32(port->membase + addr); + reg |= val; + iowrite32(reg, port->membase + addr); + + spin_unlock_irqrestore(&uart->lock, flags); +} + +/** + * men_z135_reg_clr() - Unset value in register + * @uart: The UART port + * @addr: Register address + * @val: value to clear + */ +static inline void men_z135_reg_clr(struct men_z135_port *uart, + u32 addr, u32 val) +{ + struct uart_port *port = &uart->port; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&uart->lock, flags); + + reg = ioread32(port->membase + addr); + reg &= ~val; + iowrite32(reg, port->membase + addr); + + spin_unlock_irqrestore(&uart->lock, flags); +} + +/** + * men_z135_handle_modem_status() - Handle change of modem status + * @port: The UART port + * + * Handle change of modem status register. This is done by reading the "delta" + * versions of DCD (Data Carrier Detect) and CTS (Clear To Send). + */ +static void men_z135_handle_modem_status(struct men_z135_port *uart) +{ + if (uart->stat_reg & MEN_Z135_MSR_DDCD) + uart_handle_dcd_change(&uart->port, + uart->stat_reg & ~MEN_Z135_MSR_DCD); + if (uart->stat_reg & MEN_Z135_MSR_DCTS) + uart_handle_cts_change(&uart->port, + uart->stat_reg & ~MEN_Z135_MSR_CTS); +} + +static void men_z135_handle_lsr(struct men_z135_port *uart) +{ + struct uart_port *port = &uart->port; + u8 lsr; + + lsr = (uart->stat_reg >> 16) & 0xff; + + if (lsr & MEN_Z135_LSR_OE) + port->icount.overrun++; + if (lsr & MEN_Z135_LSR_PE) + port->icount.parity++; + if (lsr & MEN_Z135_LSR_FE) + port->icount.frame++; + if (lsr & MEN_Z135_LSR_BI) { + port->icount.brk++; + uart_handle_break(port); + } +} + +/** + * get_rx_fifo_content() - Get the number of bytes in RX FIFO + * @uart: The UART port + * + * Read RXC register from hardware and return current FIFO fill size. + */ +static u16 get_rx_fifo_content(struct men_z135_port *uart) +{ + struct uart_port *port = &uart->port; + u32 stat_reg; + u16 rxc; + u8 rxc_lo; + u8 rxc_hi; + + stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG); + rxc_lo = stat_reg >> 24; + rxc_hi = (stat_reg & 0xC0) >> 6; + + rxc = rxc_lo | (rxc_hi << 8); + + return rxc; +} + +/** + * men_z135_handle_rx() - RX tasklet routine + * @arg: Pointer to struct men_z135_port + * + * Copy from RX FIFO and acknowledge number of bytes copied. + */ +static void men_z135_handle_rx(struct men_z135_port *uart) +{ + struct uart_port *port = &uart->port; + struct tty_port *tport = &port->state->port; + int copied; + u16 size; + int room; + + size = get_rx_fifo_content(uart); + + if (size == 0) + return; + + /* Avoid accidently accessing TX FIFO instead of RX FIFO. Last + * longword in RX FIFO cannot be read.(0x004-0x3FF) + */ + if (size > MEN_Z135_FIFO_WATERMARK) + size = MEN_Z135_FIFO_WATERMARK; + + room = tty_buffer_request_room(tport, size); + if (room != size) + dev_warn(&uart->mdev->dev, + "Not enough room in flip buffer, truncating to %d\n", + room); + + if (room == 0) + return; + + memcpy_fromio(uart->rxbuf, port->membase + MEN_Z135_RX_RAM, room); + /* Be sure to first copy all data and then acknowledge it */ + mb(); + iowrite32(room, port->membase + MEN_Z135_RX_CTRL); + + copied = tty_insert_flip_string(tport, uart->rxbuf, room); + if (copied != room) + dev_warn(&uart->mdev->dev, + "Only copied %d instead of %d bytes\n", + copied, room); + + port->icount.rx += copied; + + tty_flip_buffer_push(tport); + +} + +/** + * men_z135_handle_tx() - TX tasklet routine + * @arg: Pointer to struct men_z135_port + * + */ +static void men_z135_handle_tx(struct men_z135_port *uart) +{ + struct uart_port *port = &uart->port; + struct circ_buf *xmit = &port->state->xmit; + u32 txc; + u32 wptr; + int qlen; + int n; + int txfree; + int head; + int tail; + int s; + + if (uart_circ_empty(xmit)) + goto out; + + if (uart_tx_stopped(port)) + goto out; + + if (port->x_char) + goto out; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + /* calculate bytes to copy */ + qlen = uart_circ_chars_pending(xmit); + if (qlen <= 0) + goto out; + + wptr = ioread32(port->membase + MEN_Z135_TX_CTRL); + txc = (wptr >> 16) & 0x3ff; + wptr &= 0x3ff; + + if (txc > MEN_Z135_FIFO_WATERMARK) + txc = MEN_Z135_FIFO_WATERMARK; + + txfree = MEN_Z135_FIFO_WATERMARK - txc; + if (txfree <= 0) { + pr_err("Not enough room in TX FIFO have %d, need %d\n", + txfree, qlen); + goto irq_en; + } + + /* if we're not aligned, it's better to copy only 1 or 2 bytes and + * then the rest. + */ + if (align && qlen >= 3 && BYTES_TO_ALIGN(wptr)) + n = 4 - BYTES_TO_ALIGN(wptr); + else if (qlen > txfree) + n = txfree; + else + n = qlen; + + if (n <= 0) + goto irq_en; + + head = xmit->head & (UART_XMIT_SIZE - 1); + tail = xmit->tail & (UART_XMIT_SIZE - 1); + + s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail; + n = min(n, s); + + memcpy_toio(port->membase + MEN_Z135_TX_RAM, &xmit->buf[xmit->tail], n); + xmit->tail = (xmit->tail + n) & (UART_XMIT_SIZE - 1); + mmiowb(); + + iowrite32(n & 0x3ff, port->membase + MEN_Z135_TX_CTRL); + + port->icount.tx += n; + +irq_en: + if (!uart_circ_empty(xmit)) + men_z135_reg_set(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN); + else + men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN); + +out: + return; + +} + +/** + * men_z135_intr() - Handle legacy IRQs + * @irq: The IRQ number + * @data: Pointer to UART port + * + * Check IIR register to see which tasklet to start. + */ +static irqreturn_t men_z135_intr(int irq, void *data) +{ + struct men_z135_port *uart = (struct men_z135_port *)data; + struct uart_port *port = &uart->port; + int irq_id; + + uart->stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG); + /* IRQ pending is low active */ + if (IS_IRQ(uart->stat_reg)) + return IRQ_NONE; + + irq_id = IRQ_ID(uart->stat_reg); + switch (irq_id) { + case MEN_Z135_IRQ_ID_MST: + men_z135_handle_modem_status(uart); + break; + case MEN_Z135_IRQ_ID_TSA: + men_z135_handle_tx(uart); + break; + case MEN_Z135_IRQ_ID_CTI: + dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n"); + /* Fallthrough */ + case MEN_Z135_IRQ_ID_RDA: + /* Reading data clears RX IRQ */ + men_z135_handle_rx(uart); + break; + case MEN_Z135_IRQ_ID_RLS: + men_z135_handle_lsr(uart); + break; + default: + dev_warn(&uart->mdev->dev, "Unknown IRQ id %d\n", irq_id); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +/** + * men_z135_request_irq() - Request IRQ for 16z135 core + * @uart: z135 private uart port structure + * + * Request an IRQ for 16z135 to use. First try using MSI, if it fails + * fall back to using legacy interrupts. + */ +static int men_z135_request_irq(struct men_z135_port *uart) +{ + struct device *dev = &uart->mdev->dev; + struct uart_port *port = &uart->port; + int err = 0; + + err = request_irq(port->irq, men_z135_intr, IRQF_SHARED, + "men_z135_intr", uart); + if (err) + dev_err(dev, "Error %d getting interrupt\n", err); + + return err; +} + +/** + * men_z135_tx_empty() - Handle tx_empty call + * @port: The UART port + * + * This function tests whether the TX FIFO and shifter for the port + * described by @port is empty. + */ +static unsigned int men_z135_tx_empty(struct uart_port *port) +{ + u32 wptr; + u16 txc; + + wptr = ioread32(port->membase + MEN_Z135_TX_CTRL); + txc = (wptr >> 16) & 0x3ff; + + if (txc == 0) + return TIOCSER_TEMT; + else + return 0; +} + +/** + * men_z135_set_mctrl() - Set modem control lines + * @port: The UART port + * @mctrl: The modem control lines + * + * This function sets the modem control lines for a port described by @port + * to the state described by @mctrl + */ +static void men_z135_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct men_z135_port *uart = to_men_z135(port); + u32 conf_reg = 0; + + if (mctrl & TIOCM_RTS) + conf_reg |= MEN_Z135_MCR_RTS; + if (mctrl & TIOCM_DTR) + conf_reg |= MEN_Z135_MCR_DTR; + if (mctrl & TIOCM_OUT1) + conf_reg |= MEN_Z135_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + conf_reg |= MEN_Z135_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + conf_reg |= MEN_Z135_MCR_LOOP; + + men_z135_reg_set(uart, MEN_Z135_CONF_REG, conf_reg); +} + +/** + * men_z135_get_mctrl() - Get modem control lines + * @port: The UART port + * + * Retruns the current state of modem control inputs. + */ +static unsigned int men_z135_get_mctrl(struct uart_port *port) +{ + unsigned int mctrl = 0; + u32 stat_reg; + u8 msr; + + stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG); + + msr = ~((stat_reg >> 8) & 0xff); + + if (msr & MEN_Z135_MSR_CTS) + mctrl |= TIOCM_CTS; + if (msr & MEN_Z135_MSR_DSR) + mctrl |= TIOCM_DSR; + if (msr & MEN_Z135_MSR_RI) + mctrl |= TIOCM_RI; + if (msr & MEN_Z135_MSR_DCD) + mctrl |= TIOCM_CAR; + + return mctrl; +} + +/** + * men_z135_stop_tx() - Stop transmitting characters + * @port: The UART port + * + * Stop transmitting characters. This might be due to CTS line becomming + * inactive or the tty layer indicating we want to stop transmission due to + * an XOFF character. + */ +static void men_z135_stop_tx(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + + men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN); +} + +/** + * men_z135_start_tx() - Start transmitting characters + * @port: The UART port + * + * Start transmitting character. This actually doesn't transmit anything, but + * fires off the TX tasklet. + */ +static void men_z135_start_tx(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + + men_z135_handle_tx(uart); +} + +/** + * men_z135_stop_rx() - Stop receiving characters + * @port: The UART port + * + * Stop receiving characters; the port is in the process of being closed. + */ +static void men_z135_stop_rx(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + + men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_RXCIEN); +} + +/** + * men_z135_enable_ms() - Enable Modem Status + * port: + * + * Enable Modem Status IRQ. + */ +static void men_z135_enable_ms(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + + men_z135_reg_set(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_MSIEN); +} + +static int men_z135_startup(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + int err; + u32 conf_reg = 0; + + err = men_z135_request_irq(uart); + if (err) + return -ENODEV; + + conf_reg = ioread32(port->membase + MEN_Z135_CONF_REG); + + /* Activate all but TX space available IRQ */ + conf_reg |= MEN_Z135_ALL_IRQS & ~MEN_Z135_IER_TXCIEN; + conf_reg &= ~(0xff << 16); + conf_reg |= (txlvl << 16); + conf_reg |= (rxlvl << 20); + + iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG); + + return 0; +} + +static void men_z135_shutdown(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + u32 conf_reg = 0; + + conf_reg |= MEN_Z135_ALL_IRQS; + + men_z135_reg_clr(uart, MEN_Z135_CONF_REG, conf_reg); + + free_irq(uart->port.irq, uart); +} + +static void men_z135_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud; + u32 conf_reg; + u32 bd_reg; + u32 uart_freq; + u8 lcr; + + conf_reg = ioread32(port->membase + MEN_Z135_CONF_REG); + lcr = LCR(conf_reg); + + /* byte size */ + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr |= MEN_Z135_WL5; + break; + case CS6: + lcr |= MEN_Z135_WL6; + break; + case CS7: + lcr |= MEN_Z135_WL7; + break; + case CS8: + lcr |= MEN_Z135_WL8; + break; + } + + /* stop bits */ + if (termios->c_cflag & CSTOPB) + lcr |= MEN_Z135_NSTB2 << MEN_Z135_STB_SHIFT; + + /* parity */ + if (termios->c_cflag & PARENB) { + lcr |= MEN_Z135_PAR_ENA << MEN_Z135_PEN_SHIFT; + + if (termios->c_cflag & PARODD) + lcr |= MEN_Z135_PTY_ODD << MEN_Z135_PTY_SHIFT; + else + lcr |= MEN_Z135_PTY_EVN << MEN_Z135_PTY_SHIFT; + } else + lcr |= MEN_Z135_PAR_DIS << MEN_Z135_PEN_SHIFT; + + termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */ + + conf_reg |= lcr << MEN_Z135_LCR_SHIFT; + iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG); + + uart_freq = ioread32(port->membase + MEN_Z135_UART_FREQ); + if (uart_freq == 0) + uart_freq = MEN_Z135_BASECLK; + + baud = uart_get_baud_rate(port, termios, old, 0, uart_freq / 16); + + spin_lock(&port->lock); + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + + bd_reg = uart_freq / (4 * baud); + iowrite32(bd_reg, port->membase + MEN_Z135_BAUD_REG); + + uart_update_timeout(port, termios->c_cflag, baud); + spin_unlock(&port->lock); +} + +static const char *men_z135_type(struct uart_port *port) +{ + return KBUILD_MODNAME; +} + +static void men_z135_release_port(struct uart_port *port) +{ + iounmap(port->membase); + port->membase = NULL; + + release_mem_region(port->mapbase, MEN_Z135_MEM_SIZE); +} + +static int men_z135_request_port(struct uart_port *port) +{ + int size = MEN_Z135_MEM_SIZE; + + if (!request_mem_region(port->mapbase, size, "men_z135_port")) + return -EBUSY; + + port->membase = ioremap(port->mapbase, MEN_Z135_MEM_SIZE); + if (port->membase == NULL) { + release_mem_region(port->mapbase, MEN_Z135_MEM_SIZE); + return -ENOMEM; + } + + return 0; +} + +static void men_z135_config_port(struct uart_port *port, int type) +{ + port->type = PORT_MEN_Z135; + men_z135_request_port(port); +} + +static int men_z135_verify_port(struct uart_port *port, + struct serial_struct *serinfo) +{ + return -EINVAL; +} + +static struct uart_ops men_z135_ops = { + .tx_empty = men_z135_tx_empty, + .set_mctrl = men_z135_set_mctrl, + .get_mctrl = men_z135_get_mctrl, + .stop_tx = men_z135_stop_tx, + .start_tx = men_z135_start_tx, + .stop_rx = men_z135_stop_rx, + .enable_ms = men_z135_enable_ms, + .startup = men_z135_startup, + .shutdown = men_z135_shutdown, + .set_termios = men_z135_set_termios, + .type = men_z135_type, + .release_port = men_z135_release_port, + .request_port = men_z135_request_port, + .config_port = men_z135_config_port, + .verify_port = men_z135_verify_port, +}; + +static struct uart_driver men_z135_driver = { + .owner = THIS_MODULE, + .driver_name = KBUILD_MODNAME, + .dev_name = "ttyHSU", + .major = 0, + .minor = 0, + .nr = MEN_Z135_MAX_PORTS, +}; + +/** + * men_z135_probe() - Probe a z135 instance + * @mdev: The MCB device + * @id: The MCB device ID + * + * men_z135_probe does the basic setup of hardware resources and registers the + * new uart port to the tty layer. + */ +static int men_z135_probe(struct mcb_device *mdev, + const struct mcb_device_id *id) +{ + struct men_z135_port *uart; + struct resource *mem; + struct device *dev; + int err; + + dev = &mdev->dev; + + uart = devm_kzalloc(dev, sizeof(struct men_z135_port), GFP_KERNEL); + if (!uart) + return -ENOMEM; + + uart->rxbuf = (unsigned char *)__get_free_page(GFP_KERNEL); + if (!uart->rxbuf) + return -ENOMEM; + + mem = &mdev->mem; + + mcb_set_drvdata(mdev, uart); + + uart->port.uartclk = MEN_Z135_BASECLK * 16; + uart->port.fifosize = MEN_Z135_FIFO_SIZE; + uart->port.iotype = UPIO_MEM; + uart->port.ops = &men_z135_ops; + uart->port.irq = mcb_get_irq(mdev); + uart->port.iotype = UPIO_MEM; + uart->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; + uart->port.line = line++; + uart->port.dev = dev; + uart->port.type = PORT_MEN_Z135; + uart->port.mapbase = mem->start; + uart->port.membase = NULL; + uart->mdev = mdev; + + spin_lock_init(&uart->port.lock); + spin_lock_init(&uart->lock); + + err = uart_add_one_port(&men_z135_driver, &uart->port); + if (err) + goto err; + + return 0; + +err: + free_page((unsigned long) uart->rxbuf); + dev_err(dev, "Failed to add UART: %d\n", err); + + return err; +} + +/** + * men_z135_remove() - Remove a z135 instance from the system + * + * @mdev: The MCB device + */ +static void men_z135_remove(struct mcb_device *mdev) +{ + struct men_z135_port *uart = mcb_get_drvdata(mdev); + + line--; + uart_remove_one_port(&men_z135_driver, &uart->port); + free_page((unsigned long) uart->rxbuf); +} + +static const struct mcb_device_id men_z135_ids[] = { + { .device = 0x87 }, +}; +MODULE_DEVICE_TABLE(mcb, men_z135_ids); + +static struct mcb_driver mcb_driver = { + .driver = { + .name = "z135-uart", + .owner = THIS_MODULE, + }, + .probe = men_z135_probe, + .remove = men_z135_remove, + .id_table = men_z135_ids, +}; + +/** + * men_z135_init() - Driver Registration Routine + * + * men_z135_init is the first routine called when the driver is loaded. All it + * does is register with the legacy MEN Chameleon subsystem. + */ +static int __init men_z135_init(void) +{ + int err; + + err = uart_register_driver(&men_z135_driver); + if (err) { + pr_err("Failed to register UART: %d\n", err); + return err; + } + + err = mcb_register_driver(&mcb_driver); + if (err) { + pr_err("Failed to register MCB driver: %d\n", err); + uart_unregister_driver(&men_z135_driver); + return err; + } + + return 0; +} +module_init(men_z135_init); + +/** + * men_z135_exit() - Driver Exit Routine + * + * men_z135_exit is called just before the driver is removed from memory. + */ +static void __exit men_z135_exit(void) +{ + mcb_unregister_driver(&mcb_driver); + uart_unregister_driver(&men_z135_driver); +} +module_exit(men_z135_exit); + +MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MEN 16z135 High Speed UART"); +MODULE_ALIAS("mcb:16z135"); diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 053b98eb46c8..778e376f197e 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -52,7 +52,6 @@ struct msm_port { struct clk *clk; struct clk *pclk; unsigned int imr; - void __iomem *gsbi_base; int is_uartdm; unsigned int old_snap_state; }; @@ -599,9 +598,7 @@ static const char *msm_type(struct uart_port *port) static void msm_release_port(struct uart_port *port) { struct platform_device *pdev = to_platform_device(port->dev); - struct msm_port *msm_port = UART_TO_MSM(port); struct resource *uart_resource; - struct resource *gsbi_resource; resource_size_t size; uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -612,28 +609,12 @@ static void msm_release_port(struct uart_port *port) release_mem_region(port->mapbase, size); iounmap(port->membase); port->membase = NULL; - - if (msm_port->gsbi_base) { - writel_relaxed(GSBI_PROTOCOL_IDLE, - msm_port->gsbi_base + GSBI_CONTROL); - - gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (unlikely(!gsbi_resource)) - return; - - size = resource_size(gsbi_resource); - release_mem_region(gsbi_resource->start, size); - iounmap(msm_port->gsbi_base); - msm_port->gsbi_base = NULL; - } } static int msm_request_port(struct uart_port *port) { - struct msm_port *msm_port = UART_TO_MSM(port); struct platform_device *pdev = to_platform_device(port->dev); struct resource *uart_resource; - struct resource *gsbi_resource; resource_size_t size; int ret; @@ -652,30 +633,8 @@ static int msm_request_port(struct uart_port *port) goto fail_release_port; } - gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); - /* Is this a GSBI-based port? */ - if (gsbi_resource) { - size = resource_size(gsbi_resource); - - if (!request_mem_region(gsbi_resource->start, size, - "msm_serial")) { - ret = -EBUSY; - goto fail_release_port_membase; - } - - msm_port->gsbi_base = ioremap(gsbi_resource->start, size); - if (!msm_port->gsbi_base) { - ret = -EBUSY; - goto fail_release_gsbi; - } - } - return 0; -fail_release_gsbi: - release_mem_region(gsbi_resource->start, size); -fail_release_port_membase: - iounmap(port->membase); fail_release_port: release_mem_region(port->mapbase, size); return ret; @@ -683,7 +642,6 @@ fail_release_port: static void msm_config_port(struct uart_port *port, int flags) { - struct msm_port *msm_port = UART_TO_MSM(port); int ret; if (flags & UART_CONFIG_TYPE) { port->type = PORT_MSM; @@ -691,9 +649,6 @@ static void msm_config_port(struct uart_port *port, int flags) if (ret) return; } - if (msm_port->gsbi_base) - writel_relaxed(GSBI_PROTOCOL_UART, - msm_port->gsbi_base + GSBI_CONTROL); } static int msm_verify_port(struct uart_port *port, struct serial_struct *ser) @@ -1110,6 +1065,7 @@ static struct of_device_id msm_match_table[] = { static struct platform_driver msm_platform_driver = { .remove = msm_serial_remove, + .probe = msm_serial_probe, .driver = { .name = "msm_serial", .owner = THIS_MODULE, @@ -1125,7 +1081,7 @@ static int __init msm_serial_init(void) if (unlikely(ret)) return ret; - ret = platform_driver_probe(&msm_platform_driver, msm_serial_probe); + ret = platform_driver_register(&msm_platform_driver); if (unlikely(ret)) uart_unregister_driver(&msm_uart_driver); diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h index 1e9b68b6f9eb..d98d45efdf86 100644 --- a/drivers/tty/serial/msm_serial.h +++ b/drivers/tty/serial/msm_serial.h @@ -109,11 +109,6 @@ #define UART_ISR 0x0014 #define UART_ISR_TX_READY (1 << 7) -#define GSBI_CONTROL 0x0 -#define GSBI_PROTOCOL_CODE 0x30 -#define GSBI_PROTOCOL_UART 0x40 -#define GSBI_PROTOCOL_IDLE 0x0 - #define UARTDM_RXFS 0x50 #define UARTDM_RXFS_BUF_SHIFT 0x7 #define UARTDM_RXFS_BUF_MASK 0x7 diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c index 7fd6aaaacd8e..be127d0da32c 100644 --- a/drivers/tty/serial/mux.c +++ b/drivers/tty/serial/mux.c @@ -29,7 +29,7 @@ #include <asm/irq.h> #include <asm/parisc-device.h> -#ifdef CONFIG_MAGIC_SYSRQ +#if defined(CONFIG_SERIAL_MUX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #include <linux/sysrq.h> #define SUPPORT_SYSRQ #endif @@ -613,7 +613,7 @@ static void __exit mux_exit(void) { /* Delete the Mux timer. */ if(port_cnt > 0) { - del_timer(&mux_timer); + del_timer_sync(&mux_timer); #ifdef CONFIG_SERIAL_MUX_CONSOLE unregister_console(&mux_console); #endif diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index aa97fd845b4d..4b5b3c2fe328 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -200,7 +200,7 @@ static void dma_tx_callback(void *param) /* clear the bit used to serialize the DMA tx. */ clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); /* wake up the possible processes. */ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -275,7 +275,7 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s) mxs_auart_dma_tx(s, i); } else { clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } return; } diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 99246606a256..68d4455f3cf9 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -173,6 +173,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev) { struct uart_8250_port port8250; memset(&port8250, 0, sizeof(port8250)); + port.type = port_type; port8250.port = port; if (port.fifosize) @@ -182,6 +183,10 @@ static int of_platform_serial_probe(struct platform_device *ofdev) "auto-flow-control")) port8250.capabilities |= UART_CAP_AFE; + if (of_property_read_bool(ofdev->dev.of_node, + "has-hw-flow-control")) + port8250.port.flags |= UPF_HARD_FLOW; + ret = serial8250_register_8250_port(&port8250); break; } diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 08b6b9419f0d..d017cec8a34a 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -163,10 +163,6 @@ struct uart_omap_port { u8 wakeups_enabled; u32 features; - int DTR_gpio; - int DTR_inverted; - int DTR_active; - struct serial_rs485 rs485; int rts_gpio; @@ -184,8 +180,6 @@ static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS]; /* Forward declaration of functions */ static void serial_omap_mdr1_errataset(struct uart_omap_port *up, u8 mdr1); -static struct workqueue_struct *serial_omap_uart_wq; - static inline unsigned int serial_in(struct uart_omap_port *up, int offset) { offset <<= up->port.regshift; @@ -398,11 +392,8 @@ static void transmit_chars(struct uart_omap_port *up, unsigned int lsr) break; } while (--count > 0); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) { - spin_unlock(&up->port.lock); + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&up->port); - spin_lock(&up->port.lock); - } if (uart_circ_empty(xmit)) serial_omap_stop_tx(&up->port); @@ -697,16 +688,6 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl) serial_out(up, UART_MCR, up->mcr); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); - - if (gpio_is_valid(up->DTR_gpio) && - !!(mctrl & TIOCM_DTR) != up->DTR_active) { - up->DTR_active = !up->DTR_active; - if (gpio_cansleep(up->DTR_gpio)) - schedule_work(&up->qos_work); - else - gpio_set_value(up->DTR_gpio, - up->DTR_active != up->DTR_inverted); - } } static void serial_omap_break_ctl(struct uart_port *port, int break_state) @@ -850,9 +831,6 @@ static void serial_omap_uart_qos_work(struct work_struct *work) qos_work); pm_qos_update_request(&up->pm_qos_request, up->latency); - if (gpio_is_valid(up->DTR_gpio)) - gpio_set_value_cansleep(up->DTR_gpio, - up->DTR_active != up->DTR_inverted); } static void @@ -1420,7 +1398,7 @@ serial_omap_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) switch (cmd) { case TIOCSRS485: - if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, + if (copy_from_user(&rs485conf, (void __user *) arg, sizeof(rs485conf))) return -EFAULT; @@ -1428,7 +1406,7 @@ serial_omap_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) break; case TIOCGRS485: - if (copy_to_user((struct serial_rs485 *) arg, + if (copy_to_user((void __user *) arg, &(to_uart_omap_port(port)->rs485), sizeof(rs485conf))) return -EFAULT; @@ -1614,7 +1592,7 @@ static int serial_omap_probe_rs485(struct uart_omap_port *up, /* check for tx enable gpio */ up->rts_gpio = of_get_named_gpio_flags(np, "rts-gpio", 0, &flags); if (gpio_is_valid(up->rts_gpio)) { - ret = gpio_request(up->rts_gpio, "omap-serial"); + ret = devm_gpio_request(up->dev, up->rts_gpio, "omap-serial"); if (ret < 0) return ret; ret = gpio_direction_output(up->rts_gpio, @@ -1644,10 +1622,13 @@ static int serial_omap_probe_rs485(struct uart_omap_port *up, static int serial_omap_probe(struct platform_device *pdev) { - struct uart_omap_port *up; - struct resource *mem, *irq; struct omap_uart_port_info *omap_up_info = dev_get_platdata(&pdev->dev); - int ret, uartirq = 0, wakeirq = 0; + struct uart_omap_port *up; + struct resource *mem; + void __iomem *base; + int uartirq = 0; + int wakeirq = 0; + int ret; /* The optional wakeirq may be specified in the board dts file */ if (pdev->dev.of_node) { @@ -1658,48 +1639,19 @@ static int serial_omap_probe(struct platform_device *pdev) omap_up_info = of_get_uart_port_info(&pdev->dev); pdev->dev.platform_data = omap_up_info; } else { - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq) { - dev_err(&pdev->dev, "no irq resource?\n"); - return -ENODEV; - } - uartirq = irq->start; - } - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "no mem resource?\n"); - return -ENODEV; - } - - if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem), - pdev->dev.driver->name)) { - dev_err(&pdev->dev, "memory region already claimed\n"); - return -EBUSY; - } - - if (gpio_is_valid(omap_up_info->DTR_gpio) && - omap_up_info->DTR_present) { - ret = gpio_request(omap_up_info->DTR_gpio, "omap-serial"); - if (ret < 0) - return ret; - ret = gpio_direction_output(omap_up_info->DTR_gpio, - omap_up_info->DTR_inverted); - if (ret < 0) - return ret; + uartirq = platform_get_irq(pdev, 0); + if (uartirq < 0) + return -EPROBE_DEFER; } up = devm_kzalloc(&pdev->dev, sizeof(*up), GFP_KERNEL); if (!up) return -ENOMEM; - if (gpio_is_valid(omap_up_info->DTR_gpio) && - omap_up_info->DTR_present) { - up->DTR_gpio = omap_up_info->DTR_gpio; - up->DTR_inverted = omap_up_info->DTR_inverted; - } else - up->DTR_gpio = -EINVAL; - up->DTR_active = 0; + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(base)) + return PTR_ERR(base); up->dev = &pdev->dev; up->port.dev = &pdev->dev; @@ -1733,14 +1685,7 @@ static int serial_omap_probe(struct platform_device *pdev) sprintf(up->name, "OMAP UART%d", up->port.line); up->port.mapbase = mem->start; - up->port.membase = devm_ioremap(&pdev->dev, mem->start, - resource_size(mem)); - if (!up->port.membase) { - dev_err(&pdev->dev, "can't ioremap UART\n"); - ret = -ENOMEM; - goto err_ioremap; - } - + up->port.membase = base; up->port.flags = omap_up_info->flags; up->port.uartclk = omap_up_info->uartclk; if (!up->port.uartclk) { @@ -1754,12 +1699,12 @@ static int serial_omap_probe(struct platform_device *pdev) up->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; pm_qos_add_request(&up->pm_qos_request, PM_QOS_CPU_DMA_LATENCY, up->latency); - serial_omap_uart_wq = create_singlethread_workqueue(up->name); INIT_WORK(&up->qos_work, serial_omap_uart_qos_work); platform_set_drvdata(pdev, up); if (omap_up_info->autosuspend_timeout == 0) omap_up_info->autosuspend_timeout = -1; + device_init_wakeup(up->dev, true); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, @@ -1786,7 +1731,6 @@ static int serial_omap_probe(struct platform_device *pdev) err_add_port: pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); -err_ioremap: err_rs485: err_port_line: dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 0931b3fe9edf..0cb6a8e52bd0 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -257,6 +257,8 @@ struct eg20t_port { dma_addr_t rx_buf_dma; struct dentry *debugfs; +#define IRQ_NAME_SIZE 17 + char irq_name[IRQ_NAME_SIZE]; /* protect the eg20t_port private structure and io access to membase */ spinlock_t lock; @@ -1343,7 +1345,7 @@ static int pch_uart_startup(struct uart_port *port) return ret; ret = request_irq(priv->port.irq, pch_uart_interrupt, IRQF_SHARED, - KBUILD_MODNAME, priv); + priv->irq_name, priv); if (ret < 0) return ret; @@ -1588,13 +1590,8 @@ static void pch_uart_put_poll_char(struct uart_port *port, wait_for_xmitr(priv, UART_LSR_THRE); /* * Send the character out. - * If a LF, also do CR... */ iowrite8(c, priv->membase + PCH_UART_THR); - if (c == 10) { - wait_for_xmitr(priv, UART_LSR_THRE); - iowrite8(13, priv->membase + PCH_UART_THR); - } /* * Finally, wait for transmitter to become empty @@ -1818,6 +1815,10 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, priv->port.line = board->line_no; priv->trigger = PCH_UART_HAL_TRIGGER_M; + snprintf(priv->irq_name, IRQ_NAME_SIZE, + KBUILD_MODNAME ":" PCH_UART_DRIVER_DEVICE "%d", + priv->port.line); + spin_lock_init(&priv->port.lock); pci_set_drvdata(pdev, priv); diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index f9f20f383760..9e7ee39f8b2a 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -711,13 +711,8 @@ static void serial_pxa_put_poll_char(struct uart_port *port, wait_for_xmitr(up); /* * Send the character out. - * If a LF, also do CR... */ serial_out(up, UART_TX, c); - if (c == 10) { - wait_for_xmitr(up); - serial_out(up, UART_TX, 13); - } /* * Finally, wait for transmitter to become empty diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 1f5505e7f90d..329337711bb0 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -53,6 +53,29 @@ #include "samsung.h" +#if defined(CONFIG_SERIAL_SAMSUNG_DEBUG) && \ + defined(CONFIG_DEBUG_LL) && \ + !defined(MODULE) + +extern void printascii(const char *); + +__printf(1, 2) +static void dbg(const char *fmt, ...) +{ + va_list va; + char buff[256]; + + va_start(va, fmt); + vscnprintf(buff, sizeof(buf), fmt, va); + va_end(va); + + printascii(buff); +} + +#else +#define dbg(fmt, ...) do { if (0) no_printk(fmt, ##__VA_ARGS__); } while (0) +#endif + /* UART name and device definitions */ #define S3C24XX_SERIAL_NAME "ttySAC" @@ -468,8 +491,8 @@ static int s3c24xx_serial_startup(struct uart_port *port) struct s3c24xx_uart_port *ourport = to_ourport(port); int ret; - dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n", - port->mapbase, port->membase); + dbg("s3c24xx_serial_startup: port=%p (%08llx,%p)\n", + port, (unsigned long long)port->mapbase, port->membase); rx_enabled(port) = 1; @@ -514,8 +537,8 @@ static int s3c64xx_serial_startup(struct uart_port *port) struct s3c24xx_uart_port *ourport = to_ourport(port); int ret; - dbg("s3c64xx_serial_startup: port=%p (%08lx,%p)\n", - port->mapbase, port->membase); + dbg("s3c64xx_serial_startup: port=%p (%08llx,%p)\n", + port, (unsigned long long)port->mapbase, port->membase); wr_regl(port, S3C64XX_UINTM, 0xf); @@ -1160,7 +1183,7 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, return -EINVAL; } - dbg("resource %p (%lx..%lx)\n", res, res->start, res->end); + dbg("resource %pR)\n", res); port->membase = devm_ioremap(port->dev, res->start, resource_size(res)); if (!port->membase) { @@ -1203,7 +1226,7 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, wr_regl(port, S3C64XX_UINTSP, 0xf); } - dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n", + dbg("port: map=%08x, mem=%p, irq=%d (%d,%d), clock=%u\n", port->mapbase, port->membase, port->irq, ourport->rx_irq, ourport->tx_irq, port->uartclk); diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h index 8827e5424cef..eb071dd19b2d 100644 --- a/drivers/tty/serial/samsung.h +++ b/drivers/tty/serial/samsung.h @@ -1,3 +1,6 @@ +#ifndef __SAMSUNG_H +#define __SAMSUNG_H + /* * Driver for Samsung SoC onboard UARTs. * @@ -77,24 +80,4 @@ struct s3c24xx_uart_port { #define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg)) #define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg)) -#if defined(CONFIG_SERIAL_SAMSUNG_DEBUG) && \ - defined(CONFIG_DEBUG_LL) && \ - !defined(MODULE) - -extern void printascii(const char *); - -static void dbg(const char *fmt, ...) -{ - va_list va; - char buff[256]; - - va_start(va, fmt); - vsprintf(buff, fmt, va); - va_end(va); - - printascii(buff); -} - -#else -#define dbg(x...) do { } while (0) #endif diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c new file mode 100644 index 000000000000..1b6a77c4b2cb --- /dev/null +++ b/drivers/tty/serial/sc16is7xx.c @@ -0,0 +1,1277 @@ +/* + * SC16IS7xx tty serial driver - Copyright (C) 2014 GridPoint + * Author: Jon Ringle <jringle@gridpoint.com> + * + * Based on max310x.c, by Alexander Shiyan <shc_work@mail.ru> + * + * 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/bitops.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/uaccess.h> + +#define SC16IS7XX_NAME "sc16is7xx" + +/* SC16IS7XX register definitions */ +#define SC16IS7XX_RHR_REG (0x00) /* RX FIFO */ +#define SC16IS7XX_THR_REG (0x00) /* TX FIFO */ +#define SC16IS7XX_IER_REG (0x01) /* Interrupt enable */ +#define SC16IS7XX_IIR_REG (0x02) /* Interrupt Identification */ +#define SC16IS7XX_FCR_REG (0x02) /* FIFO control */ +#define SC16IS7XX_LCR_REG (0x03) /* Line Control */ +#define SC16IS7XX_MCR_REG (0x04) /* Modem Control */ +#define SC16IS7XX_LSR_REG (0x05) /* Line Status */ +#define SC16IS7XX_MSR_REG (0x06) /* Modem Status */ +#define SC16IS7XX_SPR_REG (0x07) /* Scratch Pad */ +#define SC16IS7XX_TXLVL_REG (0x08) /* TX FIFO level */ +#define SC16IS7XX_RXLVL_REG (0x09) /* RX FIFO level */ +#define SC16IS7XX_IODIR_REG (0x0a) /* I/O Direction + * - only on 75x/76x + */ +#define SC16IS7XX_IOSTATE_REG (0x0b) /* I/O State + * - only on 75x/76x + */ +#define SC16IS7XX_IOINTENA_REG (0x0c) /* I/O Interrupt Enable + * - only on 75x/76x + */ +#define SC16IS7XX_IOCONTROL_REG (0x0e) /* I/O Control + * - only on 75x/76x + */ +#define SC16IS7XX_EFCR_REG (0x0f) /* Extra Features Control */ + +/* TCR/TLR Register set: Only if ((MCR[2] == 1) && (EFR[4] == 1)) */ +#define SC16IS7XX_TCR_REG (0x06) /* Transmit control */ +#define SC16IS7XX_TLR_REG (0x07) /* Trigger level */ + +/* Special Register set: Only if ((LCR[7] == 1) && (LCR != 0xBF)) */ +#define SC16IS7XX_DLL_REG (0x00) /* Divisor Latch Low */ +#define SC16IS7XX_DLH_REG (0x01) /* Divisor Latch High */ + +/* Enhanced Register set: Only if (LCR == 0xBF) */ +#define SC16IS7XX_EFR_REG (0x02) /* Enhanced Features */ +#define SC16IS7XX_XON1_REG (0x04) /* Xon1 word */ +#define SC16IS7XX_XON2_REG (0x05) /* Xon2 word */ +#define SC16IS7XX_XOFF1_REG (0x06) /* Xoff1 word */ +#define SC16IS7XX_XOFF2_REG (0x07) /* Xoff2 word */ + +/* IER register bits */ +#define SC16IS7XX_IER_RDI_BIT (1 << 0) /* Enable RX data interrupt */ +#define SC16IS7XX_IER_THRI_BIT (1 << 1) /* Enable TX holding register + * interrupt */ +#define SC16IS7XX_IER_RLSI_BIT (1 << 2) /* Enable RX line status + * interrupt */ +#define SC16IS7XX_IER_MSI_BIT (1 << 3) /* Enable Modem status + * interrupt */ + +/* IER register bits - write only if (EFR[4] == 1) */ +#define SC16IS7XX_IER_SLEEP_BIT (1 << 4) /* Enable Sleep mode */ +#define SC16IS7XX_IER_XOFFI_BIT (1 << 5) /* Enable Xoff interrupt */ +#define SC16IS7XX_IER_RTSI_BIT (1 << 6) /* Enable nRTS interrupt */ +#define SC16IS7XX_IER_CTSI_BIT (1 << 7) /* Enable nCTS interrupt */ + +/* FCR register bits */ +#define SC16IS7XX_FCR_FIFO_BIT (1 << 0) /* Enable FIFO */ +#define SC16IS7XX_FCR_RXRESET_BIT (1 << 1) /* Reset RX FIFO */ +#define SC16IS7XX_FCR_TXRESET_BIT (1 << 2) /* Reset TX FIFO */ +#define SC16IS7XX_FCR_RXLVLL_BIT (1 << 6) /* RX Trigger level LSB */ +#define SC16IS7XX_FCR_RXLVLH_BIT (1 << 7) /* RX Trigger level MSB */ + +/* FCR register bits - write only if (EFR[4] == 1) */ +#define SC16IS7XX_FCR_TXLVLL_BIT (1 << 4) /* TX Trigger level LSB */ +#define SC16IS7XX_FCR_TXLVLH_BIT (1 << 5) /* TX Trigger level MSB */ + +/* IIR register bits */ +#define SC16IS7XX_IIR_NO_INT_BIT (1 << 0) /* No interrupts pending */ +#define SC16IS7XX_IIR_ID_MASK 0x3e /* Mask for the interrupt ID */ +#define SC16IS7XX_IIR_THRI_SRC 0x02 /* TX holding register empty */ +#define SC16IS7XX_IIR_RDI_SRC 0x04 /* RX data interrupt */ +#define SC16IS7XX_IIR_RLSE_SRC 0x06 /* RX line status error */ +#define SC16IS7XX_IIR_RTOI_SRC 0x0c /* RX time-out interrupt */ +#define SC16IS7XX_IIR_MSI_SRC 0x00 /* Modem status interrupt + * - only on 75x/76x + */ +#define SC16IS7XX_IIR_INPIN_SRC 0x30 /* Input pin change of state + * - only on 75x/76x + */ +#define SC16IS7XX_IIR_XOFFI_SRC 0x10 /* Received Xoff */ +#define SC16IS7XX_IIR_CTSRTS_SRC 0x20 /* nCTS,nRTS change of state + * from active (LOW) + * to inactive (HIGH) + */ +/* LCR register bits */ +#define SC16IS7XX_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */ +#define SC16IS7XX_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1 + * + * Word length bits table: + * 00 -> 5 bit words + * 01 -> 6 bit words + * 10 -> 7 bit words + * 11 -> 8 bit words + */ +#define SC16IS7XX_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit + * + * STOP length bit table: + * 0 -> 1 stop bit + * 1 -> 1-1.5 stop bits if + * word length is 5, + * 2 stop bits otherwise + */ +#define SC16IS7XX_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */ +#define SC16IS7XX_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */ +#define SC16IS7XX_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ +#define SC16IS7XX_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ +#define SC16IS7XX_LCR_DLAB_BIT (1 << 7) /* Divisor Latch enable */ +#define SC16IS7XX_LCR_WORD_LEN_5 (0x00) +#define SC16IS7XX_LCR_WORD_LEN_6 (0x01) +#define SC16IS7XX_LCR_WORD_LEN_7 (0x02) +#define SC16IS7XX_LCR_WORD_LEN_8 (0x03) +#define SC16IS7XX_LCR_CONF_MODE_A SC16IS7XX_LCR_DLAB_BIT /* Special + * reg set */ +#define SC16IS7XX_LCR_CONF_MODE_B 0xBF /* Enhanced + * reg set */ + +/* MCR register bits */ +#define SC16IS7XX_MCR_DTR_BIT (1 << 0) /* DTR complement + * - only on 75x/76x + */ +#define SC16IS7XX_MCR_RTS_BIT (1 << 1) /* RTS complement */ +#define SC16IS7XX_MCR_TCRTLR_BIT (1 << 2) /* TCR/TLR register enable */ +#define SC16IS7XX_MCR_LOOP_BIT (1 << 4) /* Enable loopback test mode */ +#define SC16IS7XX_MCR_XONANY_BIT (1 << 5) /* Enable Xon Any + * - write enabled + * if (EFR[4] == 1) + */ +#define SC16IS7XX_MCR_IRDA_BIT (1 << 6) /* Enable IrDA mode + * - write enabled + * if (EFR[4] == 1) + */ +#define SC16IS7XX_MCR_CLKSEL_BIT (1 << 7) /* Divide clock by 4 + * - write enabled + * if (EFR[4] == 1) + */ + +/* LSR register bits */ +#define SC16IS7XX_LSR_DR_BIT (1 << 0) /* Receiver data ready */ +#define SC16IS7XX_LSR_OE_BIT (1 << 1) /* Overrun Error */ +#define SC16IS7XX_LSR_PE_BIT (1 << 2) /* Parity Error */ +#define SC16IS7XX_LSR_FE_BIT (1 << 3) /* Frame Error */ +#define SC16IS7XX_LSR_BI_BIT (1 << 4) /* Break Interrupt */ +#define SC16IS7XX_LSR_BRK_ERROR_MASK 0x1E /* BI, FE, PE, OE bits */ +#define SC16IS7XX_LSR_THRE_BIT (1 << 5) /* TX holding register empty */ +#define SC16IS7XX_LSR_TEMT_BIT (1 << 6) /* Transmitter empty */ +#define SC16IS7XX_LSR_FIFOE_BIT (1 << 7) /* Fifo Error */ + +/* MSR register bits */ +#define SC16IS7XX_MSR_DCTS_BIT (1 << 0) /* Delta CTS Clear To Send */ +#define SC16IS7XX_MSR_DDSR_BIT (1 << 1) /* Delta DSR Data Set Ready + * or (IO4) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_DRI_BIT (1 << 2) /* Delta RI Ring Indicator + * or (IO7) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_DCD_BIT (1 << 3) /* Delta CD Carrier Detect + * or (IO6) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_CTS_BIT (1 << 0) /* CTS */ +#define SC16IS7XX_MSR_DSR_BIT (1 << 1) /* DSR (IO4) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_RI_BIT (1 << 2) /* RI (IO7) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_CD_BIT (1 << 3) /* CD (IO6) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_DELTA_MASK 0x0F /* Any of the delta bits! */ + +/* + * TCR register bits + * TCR trigger levels are available from 0 to 60 characters with a granularity + * of four. + * The programmer must program the TCR such that TCR[3:0] > TCR[7:4]. There is + * no built-in hardware check to make sure this condition is met. Also, the TCR + * must be programmed with this condition before auto RTS or software flow + * control is enabled to avoid spurious operation of the device. + */ +#define SC16IS7XX_TCR_RX_HALT(words) ((((words) / 4) & 0x0f) << 0) +#define SC16IS7XX_TCR_RX_RESUME(words) ((((words) / 4) & 0x0f) << 4) + +/* + * TLR register bits + * If TLR[3:0] or TLR[7:4] are logical 0, the selectable trigger levels via the + * FIFO Control Register (FCR) are used for the transmit and receive FIFO + * trigger levels. Trigger levels from 4 characters to 60 characters are + * available with a granularity of four. + * + * When the trigger level setting in TLR is zero, the SC16IS740/750/760 uses the + * trigger level setting defined in FCR. If TLR has non-zero trigger level value + * the trigger level defined in FCR is discarded. This applies to both transmit + * FIFO and receive FIFO trigger level setting. + * + * When TLR is used for RX trigger level control, FCR[7:6] should be left at the + * default state, that is, '00'. + */ +#define SC16IS7XX_TLR_TX_TRIGGER(words) ((((words) / 4) & 0x0f) << 0) +#define SC16IS7XX_TLR_RX_TRIGGER(words) ((((words) / 4) & 0x0f) << 4) + +/* 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_SRESET_BIT (1 << 3) /* Software Reset */ + +/* EFCR register bits */ +#define SC16IS7XX_EFCR_9BIT_MODE_BIT (1 << 0) /* Enable 9-bit or Multidrop + * mode (RS485) */ +#define SC16IS7XX_EFCR_RXDISABLE_BIT (1 << 1) /* Disable receiver */ +#define SC16IS7XX_EFCR_TXDISABLE_BIT (1 << 2) /* Disable transmitter */ +#define SC16IS7XX_EFCR_AUTO_RS485_BIT (1 << 4) /* Auto RS485 RTS direction */ +#define SC16IS7XX_EFCR_RTS_INVERT_BIT (1 << 5) /* RTS output inversion */ +#define SC16IS7XX_EFCR_IRDA_MODE_BIT (1 << 7) /* IrDA mode + * 0 = rate upto 115.2 kbit/s + * - Only 750/760 + * 1 = rate upto 1.152 Mbit/s + * - Only 760 + */ + +/* EFR register bits */ +#define SC16IS7XX_EFR_AUTORTS_BIT (1 << 6) /* Auto RTS flow ctrl enable */ +#define SC16IS7XX_EFR_AUTOCTS_BIT (1 << 7) /* Auto CTS flow ctrl enable */ +#define SC16IS7XX_EFR_XOFF2_DETECT_BIT (1 << 5) /* Enable Xoff2 detection */ +#define SC16IS7XX_EFR_ENABLE_BIT (1 << 4) /* Enable enhanced functions + * and writing to IER[7:4], + * FCR[5:4], MCR[7:5] + */ +#define SC16IS7XX_EFR_SWFLOW3_BIT (1 << 3) /* SWFLOW bit 3 */ +#define SC16IS7XX_EFR_SWFLOW2_BIT (1 << 2) /* SWFLOW bit 2 + * + * SWFLOW bits 3 & 2 table: + * 00 -> no transmitter flow + * control + * 01 -> transmitter generates + * XON2 and XOFF2 + * 10 -> transmitter generates + * XON1 and XOFF1 + * 11 -> transmitter generates + * XON1, XON2, XOFF1 and + * XOFF2 + */ +#define SC16IS7XX_EFR_SWFLOW1_BIT (1 << 1) /* SWFLOW bit 2 */ +#define SC16IS7XX_EFR_SWFLOW0_BIT (1 << 0) /* SWFLOW bit 3 + * + * SWFLOW bits 3 & 2 table: + * 00 -> no received flow + * control + * 01 -> receiver compares + * XON2 and XOFF2 + * 10 -> receiver compares + * XON1 and XOFF1 + * 11 -> receiver compares + * XON1, XON2, XOFF1 and + * XOFF2 + */ + +/* Misc definitions */ +#define SC16IS7XX_FIFO_SIZE (64) +#define SC16IS7XX_REG_SHIFT 2 + +struct sc16is7xx_devtype { + char name[10]; + int nr_gpio; + int nr_uart; +}; + +struct sc16is7xx_one { + struct uart_port port; + struct work_struct tx_work; + struct work_struct md_work; + + struct serial_rs485 rs485; +}; + +struct sc16is7xx_port { + struct uart_driver uart; + struct sc16is7xx_devtype *devtype; + struct regmap *regmap; + struct mutex mutex; + struct clk *clk; +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio; +#endif + unsigned char buf[SC16IS7XX_FIFO_SIZE]; + struct sc16is7xx_one p[0]; +}; + +#define to_sc16is7xx_one(p,e) ((container_of((p), struct sc16is7xx_one, e))) + +static u8 sc16is7xx_port_read(struct uart_port *port, u8 reg) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + unsigned int val = 0; + + regmap_read(s->regmap, + (reg << SC16IS7XX_REG_SHIFT) | port->line, &val); + + return val; +} + +static void sc16is7xx_port_write(struct uart_port *port, u8 reg, u8 val) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + + regmap_write(s->regmap, + (reg << SC16IS7XX_REG_SHIFT) | port->line, val); +} + +static void sc16is7xx_port_update(struct uart_port *port, u8 reg, + u8 mask, u8 val) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + + regmap_update_bits(s->regmap, + (reg << SC16IS7XX_REG_SHIFT) | port->line, + mask, val); +} + + +static void sc16is7xx_power(struct uart_port *port, int on) +{ + sc16is7xx_port_update(port, SC16IS7XX_IER_REG, + SC16IS7XX_IER_SLEEP_BIT, + on ? 0 : SC16IS7XX_IER_SLEEP_BIT); +} + +static const struct sc16is7xx_devtype sc16is74x_devtype = { + .name = "SC16IS74X", + .nr_gpio = 0, + .nr_uart = 1, +}; + +static const struct sc16is7xx_devtype sc16is750_devtype = { + .name = "SC16IS750", + .nr_gpio = 8, + .nr_uart = 1, +}; + +static const struct sc16is7xx_devtype sc16is752_devtype = { + .name = "SC16IS752", + .nr_gpio = 8, + .nr_uart = 2, +}; + +static const struct sc16is7xx_devtype sc16is760_devtype = { + .name = "SC16IS760", + .nr_gpio = 8, + .nr_uart = 1, +}; + +static const struct sc16is7xx_devtype sc16is762_devtype = { + .name = "SC16IS762", + .nr_gpio = 8, + .nr_uart = 2, +}; + +static bool sc16is7xx_regmap_volatile(struct device *dev, unsigned int reg) +{ + switch (reg >> SC16IS7XX_REG_SHIFT) { + case SC16IS7XX_RHR_REG: + case SC16IS7XX_IIR_REG: + case SC16IS7XX_LSR_REG: + case SC16IS7XX_MSR_REG: + case SC16IS7XX_TXLVL_REG: + case SC16IS7XX_RXLVL_REG: + case SC16IS7XX_IOSTATE_REG: + return true; + default: + break; + } + + return false; +} + +static bool sc16is7xx_regmap_precious(struct device *dev, unsigned int reg) +{ + switch (reg >> SC16IS7XX_REG_SHIFT) { + case SC16IS7XX_RHR_REG: + return true; + default: + break; + } + + return false; +} + +static int sc16is7xx_set_baud(struct uart_port *port, int baud) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + u8 lcr; + u8 prescaler = 0; + unsigned long clk = port->uartclk, div = clk / 16 / baud; + + if (div > 0xffff) { + prescaler = SC16IS7XX_MCR_CLKSEL_BIT; + div /= 4; + } + + lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG); + + /* Open the LCR divisors for configuration */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_CONF_MODE_B); + + /* Enable enhanced features */ + regcache_cache_bypass(s->regmap, true); + sc16is7xx_port_write(port, SC16IS7XX_EFR_REG, + SC16IS7XX_EFR_ENABLE_BIT); + regcache_cache_bypass(s->regmap, false); + + /* Put LCR back to the normal mode */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); + + sc16is7xx_port_update(port, SC16IS7XX_MCR_REG, + SC16IS7XX_MCR_CLKSEL_BIT, + prescaler); + + /* Open the LCR divisors for configuration */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_CONF_MODE_A); + + /* Write the new divisor */ + regcache_cache_bypass(s->regmap, true); + sc16is7xx_port_write(port, SC16IS7XX_DLH_REG, div / 256); + sc16is7xx_port_write(port, SC16IS7XX_DLL_REG, div % 256); + regcache_cache_bypass(s->regmap, false); + + /* Put LCR back to the normal mode */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); + + return DIV_ROUND_CLOSEST(clk / 16, div); +} + +static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, + unsigned int iir) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + unsigned int lsr = 0, ch, flag, bytes_read, i; + bool read_lsr = (iir == SC16IS7XX_IIR_RLSE_SRC) ? true : false; + + if (unlikely(rxlen >= sizeof(s->buf))) { + dev_warn_ratelimited(port->dev, + "Port %i: Possible RX FIFO overrun: %d\n", + port->line, rxlen); + port->icount.buf_overrun++; + /* Ensure sanity of RX level */ + rxlen = sizeof(s->buf); + } + + while (rxlen) { + /* Only read lsr if there are possible errors in FIFO */ + if (read_lsr) { + lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG); + if (!(lsr & SC16IS7XX_LSR_FIFOE_BIT)) + read_lsr = false; /* No errors left in FIFO */ + } else + lsr = 0; + + if (read_lsr) { + s->buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG); + bytes_read = 1; + } else { + regcache_cache_bypass(s->regmap, true); + regmap_raw_read(s->regmap, SC16IS7XX_RHR_REG, + s->buf, rxlen); + regcache_cache_bypass(s->regmap, false); + bytes_read = rxlen; + } + + lsr &= SC16IS7XX_LSR_BRK_ERROR_MASK; + + port->icount.rx++; + flag = TTY_NORMAL; + + if (unlikely(lsr)) { + if (lsr & SC16IS7XX_LSR_BI_BIT) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (lsr & SC16IS7XX_LSR_PE_BIT) + port->icount.parity++; + else if (lsr & SC16IS7XX_LSR_FE_BIT) + port->icount.frame++; + else if (lsr & SC16IS7XX_LSR_OE_BIT) + port->icount.overrun++; + + lsr &= port->read_status_mask; + if (lsr & SC16IS7XX_LSR_BI_BIT) + flag = TTY_BREAK; + else if (lsr & SC16IS7XX_LSR_PE_BIT) + flag = TTY_PARITY; + else if (lsr & SC16IS7XX_LSR_FE_BIT) + flag = TTY_FRAME; + else if (lsr & SC16IS7XX_LSR_OE_BIT) + flag = TTY_OVERRUN; + } + + for (i = 0; i < bytes_read; ++i) { + ch = s->buf[i]; + if (uart_handle_sysrq_char(port, ch)) + continue; + + if (lsr & port->ignore_status_mask) + continue; + + uart_insert_char(port, lsr, SC16IS7XX_LSR_OE_BIT, ch, + flag); + } + rxlen -= bytes_read; + } + + tty_flip_buffer_push(&port->state->port); +} + +static void sc16is7xx_handle_tx(struct uart_port *port) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + struct circ_buf *xmit = &port->state->xmit; + unsigned int txlen, to_send, i; + + if (unlikely(port->x_char)) { + sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return; + + /* Get length of data pending in circular buffer */ + to_send = uart_circ_chars_pending(xmit); + if (likely(to_send)) { + /* Limit to size of TX FIFO */ + txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); + to_send = (to_send > txlen) ? txlen : to_send; + + /* Add data to send */ + port->icount.tx += to_send; + + /* Convert to linear buffer */ + for (i = 0; i < to_send; ++i) { + s->buf[i] = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } + regcache_cache_bypass(s->regmap, true); + regmap_raw_write(s->regmap, SC16IS7XX_THR_REG, s->buf, to_send); + regcache_cache_bypass(s->regmap, false); + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) +{ + struct uart_port *port = &s->p[portno].port; + + do { + unsigned int iir, msr, rxlen; + + iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG); + if (iir & SC16IS7XX_IIR_NO_INT_BIT) + break; + + iir &= SC16IS7XX_IIR_ID_MASK; + + switch (iir) { + case SC16IS7XX_IIR_RDI_SRC: + case SC16IS7XX_IIR_RLSE_SRC: + case SC16IS7XX_IIR_RTOI_SRC: + case SC16IS7XX_IIR_XOFFI_SRC: + rxlen = sc16is7xx_port_read(port, SC16IS7XX_RXLVL_REG); + if (rxlen) + sc16is7xx_handle_rx(port, rxlen, iir); + break; + + case SC16IS7XX_IIR_CTSRTS_SRC: + msr = sc16is7xx_port_read(port, SC16IS7XX_MSR_REG); + uart_handle_cts_change(port, + !!(msr & SC16IS7XX_MSR_CTS_BIT)); + break; + case SC16IS7XX_IIR_THRI_SRC: + mutex_lock(&s->mutex); + sc16is7xx_handle_tx(port); + mutex_unlock(&s->mutex); + break; + default: + dev_err_ratelimited(port->dev, + "Port %i: Unexpected interrupt: %x", + port->line, iir); + break; + } + } while (1); +} + +static irqreturn_t sc16is7xx_ist(int irq, void *dev_id) +{ + struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id; + int i; + + for (i = 0; i < s->uart.nr; ++i) + sc16is7xx_port_irq(s, i); + + return IRQ_HANDLED; +} + +static void sc16is7xx_wq_proc(struct work_struct *ws) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(ws, tx_work); + struct sc16is7xx_port *s = dev_get_drvdata(one->port.dev); + + mutex_lock(&s->mutex); + sc16is7xx_handle_tx(&one->port); + mutex_unlock(&s->mutex); +} + +static void sc16is7xx_stop_tx(struct uart_port* port) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + struct circ_buf *xmit = &one->port.state->xmit; + + /* handle rs485 */ + if (one->rs485.flags & SER_RS485_ENABLED) { + /* do nothing if current tx not yet completed */ + int lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG); + if (!(lsr & SC16IS7XX_LSR_TEMT_BIT)) + return; + + if (uart_circ_empty(xmit) && + (one->rs485.delay_rts_after_send > 0)) + mdelay(one->rs485.delay_rts_after_send); + } + + sc16is7xx_port_update(port, SC16IS7XX_IER_REG, + SC16IS7XX_IER_THRI_BIT, + 0); +} + +static void sc16is7xx_stop_rx(struct uart_port* port) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + one->port.read_status_mask &= ~SC16IS7XX_LSR_DR_BIT; + sc16is7xx_port_update(port, SC16IS7XX_IER_REG, + SC16IS7XX_LSR_DR_BIT, + 0); +} + +static void sc16is7xx_start_tx(struct uart_port *port) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + /* handle rs485 */ + if ((one->rs485.flags & SER_RS485_ENABLED) && + (one->rs485.delay_rts_before_send > 0)) { + mdelay(one->rs485.delay_rts_before_send); + } + + if (!work_pending(&one->tx_work)) + schedule_work(&one->tx_work); +} + +static unsigned int sc16is7xx_tx_empty(struct uart_port *port) +{ + unsigned int lvl, lsr; + + lvl = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); + lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG); + + return ((lsr & SC16IS7XX_LSR_THRE_BIT) && !lvl) ? TIOCSER_TEMT : 0; +} + +static unsigned int sc16is7xx_get_mctrl(struct uart_port *port) +{ + /* DCD and DSR are not wired and CTS/RTS is handled automatically + * so just indicate DSR and CAR asserted + */ + return TIOCM_DSR | TIOCM_CAR; +} + +static void sc16is7xx_md_proc(struct work_struct *ws) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(ws, md_work); + + sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG, + SC16IS7XX_MCR_LOOP_BIT, + (one->port.mctrl & TIOCM_LOOP) ? + SC16IS7XX_MCR_LOOP_BIT : 0); +} + +static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + schedule_work(&one->md_work); +} + +static void sc16is7xx_break_ctl(struct uart_port *port, int break_state) +{ + sc16is7xx_port_update(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_TXBREAK_BIT, + break_state ? SC16IS7XX_LCR_TXBREAK_BIT : 0); +} + +static void sc16is7xx_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + unsigned int lcr, flow = 0; + int baud; + + /* Mask termios capabilities we don't support */ + termios->c_cflag &= ~CMSPAR; + + /* Word size */ + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr = SC16IS7XX_LCR_WORD_LEN_5; + break; + case CS6: + lcr = SC16IS7XX_LCR_WORD_LEN_6; + break; + case CS7: + lcr = SC16IS7XX_LCR_WORD_LEN_7; + break; + case CS8: + lcr = SC16IS7XX_LCR_WORD_LEN_8; + break; + default: + lcr = SC16IS7XX_LCR_WORD_LEN_8; + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + break; + } + + /* Parity */ + if (termios->c_cflag & PARENB) { + lcr |= SC16IS7XX_LCR_PARITY_BIT; + if (!(termios->c_cflag & PARODD)) + lcr |= SC16IS7XX_LCR_EVENPARITY_BIT; + } + + /* Stop bits */ + if (termios->c_cflag & CSTOPB) + lcr |= SC16IS7XX_LCR_STOPLEN_BIT; /* 2 stops */ + + /* Set read status mask */ + port->read_status_mask = SC16IS7XX_LSR_OE_BIT; + if (termios->c_iflag & INPCK) + port->read_status_mask |= SC16IS7XX_LSR_PE_BIT | + SC16IS7XX_LSR_FE_BIT; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= SC16IS7XX_LSR_BI_BIT; + + /* Set status ignore mask */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNBRK) + port->ignore_status_mask |= SC16IS7XX_LSR_BI_BIT; + if (!(termios->c_cflag & CREAD)) + port->ignore_status_mask |= SC16IS7XX_LSR_BRK_ERROR_MASK; + + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_CONF_MODE_B); + + /* Configure flow control */ + regcache_cache_bypass(s->regmap, true); + sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]); + sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]); + if (termios->c_cflag & CRTSCTS) + flow |= SC16IS7XX_EFR_AUTOCTS_BIT | + SC16IS7XX_EFR_AUTORTS_BIT; + if (termios->c_iflag & IXON) + flow |= SC16IS7XX_EFR_SWFLOW3_BIT; + if (termios->c_iflag & IXOFF) + flow |= SC16IS7XX_EFR_SWFLOW1_BIT; + + sc16is7xx_port_write(port, SC16IS7XX_EFR_REG, flow); + regcache_cache_bypass(s->regmap, false); + + /* Update LCR register */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); + + /* Get baud rate generator configuration */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 4 / 0xffff, + port->uartclk / 16); + + /* Setup baudrate generator */ + baud = sc16is7xx_set_baud(port, baud); + + /* Update timeout according to new baud rate */ + uart_update_timeout(port, termios->c_cflag, baud); +} + +#if defined(TIOCSRS485) && defined(TIOCGRS485) +static void sc16is7xx_config_rs485(struct uart_port *port, + struct serial_rs485 *rs485) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + one->rs485 = *rs485; + + if (one->rs485.flags & SER_RS485_ENABLED) { + sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_AUTO_RS485_BIT, + SC16IS7XX_EFCR_AUTO_RS485_BIT); + } else { + sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_AUTO_RS485_BIT, + 0); + } +} +#endif + +static int sc16is7xx_ioctl(struct uart_port *port, unsigned int cmd, + unsigned long arg) +{ +#if defined(TIOCSRS485) && defined(TIOCGRS485) + struct serial_rs485 rs485; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485, (void __user *)arg, sizeof(rs485))) + return -EFAULT; + + sc16is7xx_config_rs485(port, &rs485); + return 0; + case TIOCGRS485: + if (copy_to_user((void __user *)arg, + &(to_sc16is7xx_one(port, port)->rs485), + sizeof(rs485))) + return -EFAULT; + return 0; + default: + break; + } +#endif + + return -ENOIOCTLCMD; +} + +static int sc16is7xx_startup(struct uart_port *port) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + unsigned int val; + + sc16is7xx_power(port, 1); + + /* Reset FIFOs*/ + val = SC16IS7XX_FCR_RXRESET_BIT | SC16IS7XX_FCR_TXRESET_BIT; + sc16is7xx_port_write(port, SC16IS7XX_FCR_REG, val); + udelay(5); + sc16is7xx_port_write(port, SC16IS7XX_FCR_REG, + SC16IS7XX_FCR_FIFO_BIT); + + /* Enable EFR */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_CONF_MODE_B); + + regcache_cache_bypass(s->regmap, true); + + /* Enable write access to enhanced features and internal clock div */ + sc16is7xx_port_write(port, SC16IS7XX_EFR_REG, + SC16IS7XX_EFR_ENABLE_BIT); + + /* Enable TCR/TLR */ + sc16is7xx_port_update(port, SC16IS7XX_MCR_REG, + SC16IS7XX_MCR_TCRTLR_BIT, + SC16IS7XX_MCR_TCRTLR_BIT); + + /* Configure flow control levels */ + /* Flow control halt level 48, resume level 24 */ + sc16is7xx_port_write(port, SC16IS7XX_TCR_REG, + SC16IS7XX_TCR_RX_RESUME(24) | + SC16IS7XX_TCR_RX_HALT(48)); + + regcache_cache_bypass(s->regmap, false); + + /* Now, initialize the UART */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_WORD_LEN_8); + + /* Enable the Rx and Tx FIFO */ + sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_RXDISABLE_BIT | + SC16IS7XX_EFCR_TXDISABLE_BIT, + 0); + + /* Enable RX, TX, CTS change interrupts */ + val = SC16IS7XX_IER_RDI_BIT | SC16IS7XX_IER_THRI_BIT | + SC16IS7XX_IER_CTSI_BIT; + sc16is7xx_port_write(port, SC16IS7XX_IER_REG, val); + + return 0; +} + +static void sc16is7xx_shutdown(struct uart_port *port) +{ + /* Disable all interrupts */ + sc16is7xx_port_write(port, SC16IS7XX_IER_REG, 0); + /* Disable TX/RX */ + sc16is7xx_port_write(port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_RXDISABLE_BIT | + SC16IS7XX_EFCR_TXDISABLE_BIT); + + sc16is7xx_power(port, 0); +} + +static const char *sc16is7xx_type(struct uart_port *port) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + + return (port->type == PORT_SC16IS7XX) ? s->devtype->name : NULL; +} + +static int sc16is7xx_request_port(struct uart_port *port) +{ + /* Do nothing */ + return 0; +} + +static void sc16is7xx_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_SC16IS7XX; +} + +static int sc16is7xx_verify_port(struct uart_port *port, + struct serial_struct *s) +{ + if ((s->type != PORT_UNKNOWN) && (s->type != PORT_SC16IS7XX)) + return -EINVAL; + if (s->irq != port->irq) + return -EINVAL; + + return 0; +} + +static void sc16is7xx_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + sc16is7xx_power(port, (state == UART_PM_STATE_ON) ? 1 : 0); +} + +static void sc16is7xx_null_void(struct uart_port *port) +{ + /* Do nothing */ +} + +static const struct uart_ops sc16is7xx_ops = { + .tx_empty = sc16is7xx_tx_empty, + .set_mctrl = sc16is7xx_set_mctrl, + .get_mctrl = sc16is7xx_get_mctrl, + .stop_tx = sc16is7xx_stop_tx, + .start_tx = sc16is7xx_start_tx, + .stop_rx = sc16is7xx_stop_rx, + .enable_ms = sc16is7xx_null_void, + .break_ctl = sc16is7xx_break_ctl, + .startup = sc16is7xx_startup, + .shutdown = sc16is7xx_shutdown, + .set_termios = sc16is7xx_set_termios, + .type = sc16is7xx_type, + .request_port = sc16is7xx_request_port, + .release_port = sc16is7xx_null_void, + .config_port = sc16is7xx_config_port, + .verify_port = sc16is7xx_verify_port, + .ioctl = sc16is7xx_ioctl, + .pm = sc16is7xx_pm, +}; + +#ifdef CONFIG_GPIOLIB +static int sc16is7xx_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + unsigned int val; + struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, + gpio); + struct uart_port *port = &s->p[0].port; + + val = sc16is7xx_port_read(port, SC16IS7XX_IOSTATE_REG); + + return !!(val & BIT(offset)); +} + +static void sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +{ + struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, + gpio); + struct uart_port *port = &s->p[0].port; + + sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset), + val ? BIT(offset) : 0); +} + +static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, + gpio); + struct uart_port *port = &s->p[0].port; + + sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset), 0); + + return 0; +} + +static int sc16is7xx_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int val) +{ + struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, + gpio); + struct uart_port *port = &s->p[0].port; + + sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset), + val ? BIT(offset) : 0); + sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset), + BIT(offset)); + + return 0; +} +#endif + +static int sc16is7xx_probe(struct device *dev, + struct sc16is7xx_devtype *devtype, + struct regmap *regmap, int irq, unsigned long flags) +{ + unsigned long freq, *pfreq = dev_get_platdata(dev); + struct clk *clk; + int i, ret; + struct sc16is7xx_port *s; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* Alloc port structure */ + s = devm_kzalloc(dev, sizeof(*s) + + sizeof(struct sc16is7xx_one) * devtype->nr_uart, + GFP_KERNEL); + if (!s) { + dev_err(dev, "Error allocating port structure\n"); + return -ENOMEM; + } + + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) { + if (pfreq) + freq = *pfreq; + else + return PTR_ERR(clk); + } else { + freq = clk_get_rate(clk); + } + + s->regmap = regmap; + s->devtype = devtype; + dev_set_drvdata(dev, s); + + /* Register UART driver */ + s->uart.owner = THIS_MODULE; + s->uart.dev_name = "ttySC"; + s->uart.nr = devtype->nr_uart; + ret = uart_register_driver(&s->uart); + if (ret) { + dev_err(dev, "Registering UART driver failed\n"); + goto out_clk; + } + +#ifdef CONFIG_GPIOLIB + if (devtype->nr_gpio) { + /* Setup GPIO cotroller */ + s->gpio.owner = THIS_MODULE; + s->gpio.dev = dev; + s->gpio.label = dev_name(dev); + s->gpio.direction_input = sc16is7xx_gpio_direction_input; + s->gpio.get = sc16is7xx_gpio_get; + s->gpio.direction_output = sc16is7xx_gpio_direction_output; + s->gpio.set = sc16is7xx_gpio_set; + s->gpio.base = -1; + s->gpio.ngpio = devtype->nr_gpio; + s->gpio.can_sleep = 1; + ret = gpiochip_add(&s->gpio); + if (ret) + goto out_uart; + } +#endif + + mutex_init(&s->mutex); + + for (i = 0; i < devtype->nr_uart; ++i) { + /* Initialize port data */ + s->p[i].port.line = i; + s->p[i].port.dev = dev; + s->p[i].port.irq = irq; + s->p[i].port.type = PORT_SC16IS7XX; + s->p[i].port.fifosize = SC16IS7XX_FIFO_SIZE; + s->p[i].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY; + s->p[i].port.iotype = UPIO_PORT; + s->p[i].port.uartclk = freq; + s->p[i].port.ops = &sc16is7xx_ops; + /* Disable all interrupts */ + sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_IER_REG, 0); + /* Disable TX/RX */ + sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_RXDISABLE_BIT | + SC16IS7XX_EFCR_TXDISABLE_BIT); + /* Initialize queue for start TX */ + INIT_WORK(&s->p[i].tx_work, sc16is7xx_wq_proc); + /* Initialize queue for changing mode */ + INIT_WORK(&s->p[i].md_work, sc16is7xx_md_proc); + /* Register port */ + uart_add_one_port(&s->uart, &s->p[i].port); + /* Go to suspend mode */ + sc16is7xx_power(&s->p[i].port, 0); + } + + /* Setup interrupt */ + ret = devm_request_threaded_irq(dev, irq, NULL, sc16is7xx_ist, + IRQF_ONESHOT | flags, dev_name(dev), s); + if (!ret) + return 0; + + mutex_destroy(&s->mutex); + +#ifdef CONFIG_GPIOLIB + if (devtype->nr_gpio) + WARN_ON(gpiochip_remove(&s->gpio)); + +out_uart: +#endif + uart_unregister_driver(&s->uart); + +out_clk: + if (!IS_ERR(s->clk)) + clk_disable_unprepare(s->clk); + + return ret; +} + +static int sc16is7xx_remove(struct device *dev) +{ + struct sc16is7xx_port *s = dev_get_drvdata(dev); + int i, ret = 0; + +#ifdef CONFIG_GPIOLIB + if (s->devtype->nr_gpio) { + ret = gpiochip_remove(&s->gpio); + if (ret) + return ret; + } +#endif + + for (i = 0; i < s->uart.nr; i++) { + cancel_work_sync(&s->p[i].tx_work); + cancel_work_sync(&s->p[i].md_work); + uart_remove_one_port(&s->uart, &s->p[i].port); + sc16is7xx_power(&s->p[i].port, 0); + } + + mutex_destroy(&s->mutex); + uart_unregister_driver(&s->uart); + if (!IS_ERR(s->clk)) + clk_disable_unprepare(s->clk); + + return ret; +} + +static const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = { + { .compatible = "nxp,sc16is740", .data = &sc16is74x_devtype, }, + { .compatible = "nxp,sc16is741", .data = &sc16is74x_devtype, }, + { .compatible = "nxp,sc16is750", .data = &sc16is750_devtype, }, + { .compatible = "nxp,sc16is752", .data = &sc16is752_devtype, }, + { .compatible = "nxp,sc16is760", .data = &sc16is760_devtype, }, + { .compatible = "nxp,sc16is762", .data = &sc16is762_devtype, }, + { } +}; +MODULE_DEVICE_TABLE(of, sc16is7xx_dt_ids); + +static struct regmap_config regcfg = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = sc16is7xx_regmap_volatile, + .precious_reg = sc16is7xx_regmap_precious, +}; + +static int sc16is7xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct sc16is7xx_devtype *devtype; + unsigned long flags = 0; + struct regmap *regmap; + + if (i2c->dev.of_node) { + const struct of_device_id *of_id = + of_match_device(sc16is7xx_dt_ids, &i2c->dev); + + devtype = (struct sc16is7xx_devtype *)of_id->data; + } else { + devtype = (struct sc16is7xx_devtype *)id->driver_data; + flags = IRQF_TRIGGER_FALLING; + } + + regcfg.max_register = (0xf << SC16IS7XX_REG_SHIFT) | + (devtype->nr_uart - 1); + regmap = devm_regmap_init_i2c(i2c, ®cfg); + + return sc16is7xx_probe(&i2c->dev, devtype, regmap, i2c->irq, flags); +} + +static int sc16is7xx_i2c_remove(struct i2c_client *client) +{ + return sc16is7xx_remove(&client->dev); +} + +static const struct i2c_device_id sc16is7xx_i2c_id_table[] = { + { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, + { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, + { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, + { "sc16is762", (kernel_ulong_t)&sc16is762_devtype, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table); + +static struct i2c_driver sc16is7xx_i2c_uart_driver = { + .driver = { + .name = SC16IS7XX_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sc16is7xx_dt_ids), + }, + .probe = sc16is7xx_i2c_probe, + .remove = sc16is7xx_i2c_remove, + .id_table = sc16is7xx_i2c_id_table, +}; +module_i2c_driver(sc16is7xx_i2c_uart_driver); +MODULE_ALIAS("i2c:sc16is7xx"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jon Ringle <jringle@gridpoint.com>"); +MODULE_DESCRIPTION("SC16IS7XX serial driver"); diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index a447f71538ef..5443b46345ed 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -474,9 +474,7 @@ static void sccnxp_timer(unsigned long data) sccnxp_handle_events(s); spin_unlock_irqrestore(&s->lock, flags); - if (!timer_pending(&s->timer)) - mod_timer(&s->timer, jiffies + - usecs_to_jiffies(s->pdata.poll_time_us)); + mod_timer(&s->timer, jiffies + usecs_to_jiffies(s->pdata.poll_time_us)); } static irqreturn_t sccnxp_ist(int irq, void *dev_id) @@ -674,6 +672,8 @@ static void sccnxp_set_termios(struct uart_port *port, port->ignore_status_mask = 0; if (termios->c_iflag & IGNBRK) port->ignore_status_mask |= SR_BRK; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SR_PE; if (!(termios->c_cflag & CREAD)) port->ignore_status_mask |= SR_PE | SR_OVR | SR_FE | SR_BRK; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index b68550d95a40..fbf6c5ad222f 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -174,8 +174,12 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, if (tty->termios.c_cflag & CBAUD) uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); } - - if (tty_port_cts_enabled(port)) { + /* + * if hw support flow control without software intervention, + * then skip the below check + */ + if (tty_port_cts_enabled(port) && + !(uport->flags & UPF_HARD_FLOW)) { spin_lock_irq(&uport->lock); if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) tty->hw_stopped = 1; @@ -2239,6 +2243,9 @@ static void uart_poll_put_char(struct tty_driver *driver, int line, char ch) return; port = state->uart_port; + + if (ch == '\n') + port->ops->poll_put_char(port, '\r'); port->ops->poll_put_char(port, ch); } #endif @@ -2772,7 +2779,9 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) uport->icount.cts++; - if (tty_port_cts_enabled(port)) { + /* skip below code if the hw flow control is supported */ + if (tty_port_cts_enabled(port) && + !(uport->flags & UPF_HARD_FLOW)) { if (tty->hw_stopped) { if (status) { tty->hw_stopped = 0; diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c new file mode 100644 index 000000000000..bf9560ffe3f4 --- /dev/null +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -0,0 +1,143 @@ +/* + * Helpers for controlling modem lines via GPIO + * + * Copyright (C) 2014 Paratronic S.A. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/err.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <uapi/asm-generic/termios.h> + +#include "serial_mctrl_gpio.h" + +struct mctrl_gpios { + struct gpio_desc *gpio[UART_GPIO_MAX]; +}; + +static const struct { + const char *name; + unsigned int mctrl; + bool dir_out; +} mctrl_gpios_desc[UART_GPIO_MAX] = { + { "cts", TIOCM_CTS, false, }, + { "dsr", TIOCM_DSR, false, }, + { "dcd", TIOCM_CD, false, }, + { "rng", TIOCM_RNG, false, }, + { "rts", TIOCM_RTS, true, }, + { "dtr", TIOCM_DTR, true, }, + { "out1", TIOCM_OUT1, true, }, + { "out2", TIOCM_OUT2, true, }, +}; + +void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) +{ + enum mctrl_gpio_idx i; + + if (IS_ERR_OR_NULL(gpios)) + return; + + for (i = 0; i < UART_GPIO_MAX; i++) + if (!IS_ERR_OR_NULL(gpios->gpio[i]) && + mctrl_gpios_desc[i].dir_out) + gpiod_set_value(gpios->gpio[i], + !!(mctrl & mctrl_gpios_desc[i].mctrl)); +} +EXPORT_SYMBOL_GPL(mctrl_gpio_set); + +struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, + enum mctrl_gpio_idx gidx) +{ + if (!IS_ERR_OR_NULL(gpios) && !IS_ERR_OR_NULL(gpios->gpio[gidx])) + return gpios->gpio[gidx]; + else + return NULL; +} +EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod); + +unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl) +{ + enum mctrl_gpio_idx i; + + /* + * return it unchanged if the structure is not allocated + */ + if (IS_ERR_OR_NULL(gpios)) + return *mctrl; + + for (i = 0; i < UART_GPIO_MAX; i++) { + if (!IS_ERR_OR_NULL(gpios->gpio[i]) && + !mctrl_gpios_desc[i].dir_out) { + if (gpiod_get_value(gpios->gpio[i])) + *mctrl |= mctrl_gpios_desc[i].mctrl; + else + *mctrl &= ~mctrl_gpios_desc[i].mctrl; + } + } + + return *mctrl; +} +EXPORT_SYMBOL_GPL(mctrl_gpio_get); + +struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx) +{ + struct mctrl_gpios *gpios; + enum mctrl_gpio_idx i; + int err; + + gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL); + if (!gpios) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < UART_GPIO_MAX; i++) { + gpios->gpio[i] = devm_gpiod_get_index(dev, + mctrl_gpios_desc[i].name, + idx); + + /* + * The GPIOs are maybe not all filled, + * this is not an error. + */ + if (IS_ERR_OR_NULL(gpios->gpio[i])) + continue; + + if (mctrl_gpios_desc[i].dir_out) + err = gpiod_direction_output(gpios->gpio[i], 0); + else + err = gpiod_direction_input(gpios->gpio[i]); + if (err) { + dev_dbg(dev, "Unable to set direction for %s GPIO", + mctrl_gpios_desc[i].name); + devm_gpiod_put(dev, gpios->gpio[i]); + gpios->gpio[i] = NULL; + } + } + + return gpios; +} +EXPORT_SYMBOL_GPL(mctrl_gpio_init); + +void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) +{ + enum mctrl_gpio_idx i; + + if (IS_ERR_OR_NULL(gpios)) + return; + + for (i = 0; i < UART_GPIO_MAX; i++) + if (!IS_ERR_OR_NULL(gpios->gpio[i])) + devm_gpiod_put(dev, gpios->gpio[i]); + devm_kfree(dev, gpios); +} +EXPORT_SYMBOL_GPL(mctrl_gpio_free); diff --git a/drivers/tty/serial/serial_mctrl_gpio.h b/drivers/tty/serial/serial_mctrl_gpio.h new file mode 100644 index 000000000000..400ba0494a17 --- /dev/null +++ b/drivers/tty/serial/serial_mctrl_gpio.h @@ -0,0 +1,110 @@ +/* + * Helpers for controlling modem lines via GPIO + * + * Copyright (C) 2014 Paratronic S.A. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __SERIAL_MCTRL_GPIO__ +#define __SERIAL_MCTRL_GPIO__ + +#include <linux/err.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> + +enum mctrl_gpio_idx { + UART_GPIO_CTS, + UART_GPIO_DSR, + UART_GPIO_DCD, + UART_GPIO_RNG, + UART_GPIO_RI = UART_GPIO_RNG, + UART_GPIO_RTS, + UART_GPIO_DTR, + UART_GPIO_OUT1, + UART_GPIO_OUT2, + UART_GPIO_MAX, +}; + +/* + * Opaque descriptor for modem lines controlled by GPIOs + */ +struct mctrl_gpios; + +#ifdef CONFIG_GPIOLIB + +/* + * Set state of the modem control output lines via GPIOs. + */ +void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl); + +/* + * Get state of the modem control output lines from GPIOs. + * The mctrl flags are updated and returned. + */ +unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl); + +/* + * Returns the associated struct gpio_desc to the modem line gidx + */ +struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, + enum mctrl_gpio_idx gidx); + +/* + * Request and set direction of modem control lines GPIOs. + * devm_* functions are used, so there's no need to call mctrl_gpio_free(). + * Returns a pointer to the allocated mctrl structure if ok, -ENOMEM on + * allocation error. + */ +struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx); + +/* + * Free the mctrl_gpios structure. + * Normally, this function will not be called, as the GPIOs will + * be disposed of by the resource management code. + */ +void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios); + +#else /* GPIOLIB */ + +static inline +void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) +{ +} + +static inline +unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl) +{ + return *mctrl; +} + +static inline +struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, + enum mctrl_gpio_idx gidx) +{ + return ERR_PTR(-ENOSYS); +} + +static inline +struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx) +{ + return ERR_PTR(-ENOSYS); +} + +static inline +void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) +{ +} + +#endif /* GPIOLIB */ + +#endif diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index 90a080b1f9ee..60f49b9d7e39 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -535,13 +535,8 @@ static void serial_txx9_put_poll_char(struct uart_port *port, unsigned char c) wait_for_xmitr(up); /* * Send the character out. - * If a LF, also do CR... */ sio_out(up, TXX9_SITFIFO, c); - if (c == 10) { - wait_for_xmitr(up); - sio_out(up, TXX9_SITFIFO, 13); - } /* * Finally, wait for transmitter to become empty diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 68b0fd4b9a6a..1f2be48c92ce 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -358,9 +358,11 @@ static irqreturn_t sirfsoc_uart_usp_cts_handler(int irq, void *dev_id) { struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id; struct uart_port *port = &sirfport->port; + spin_lock(&port->lock); if (gpio_is_valid(sirfport->cts_gpio) && sirfport->ms_enabled) uart_handle_cts_change(port, !gpio_get_value(sirfport->cts_gpio)); + spin_unlock(&port->lock); return IRQ_HANDLED; } @@ -428,10 +430,6 @@ sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count) sirfport->rx_io_count += rx_count; port->icount.rx += rx_count; - spin_unlock(&port->lock); - tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); - return rx_count; } @@ -465,6 +463,7 @@ static void sirfsoc_uart_tx_dma_complete_callback(void *param) struct circ_buf *xmit = &port->state->xmit; unsigned long flags; + spin_lock_irqsave(&port->lock, flags); xmit->tail = (xmit->tail + sirfport->transfer_size) & (UART_XMIT_SIZE - 1); port->icount.tx += sirfport->transfer_size; @@ -473,10 +472,9 @@ static void sirfsoc_uart_tx_dma_complete_callback(void *param) if (sirfport->tx_dma_addr) dma_unmap_single(port->dev, sirfport->tx_dma_addr, sirfport->transfer_size, DMA_TO_DEVICE); - spin_lock_irqsave(&sirfport->tx_lock, flags); sirfport->tx_dma_state = TX_DMA_IDLE; sirfsoc_uart_tx_with_dma(sirfport); - spin_unlock_irqrestore(&sirfport->tx_lock, flags); + spin_unlock_irqrestore(&port->lock, flags); } static void sirfsoc_uart_insert_rx_buf_to_tty( @@ -489,7 +487,6 @@ static void sirfsoc_uart_insert_rx_buf_to_tty( inserted = tty_insert_flip_string(tport, sirfport->rx_dma_items[sirfport->rx_completed].xmit.buf, count); port->icount.rx += inserted; - tty_flip_buffer_push(tport); } static void sirfsoc_rx_submit_one_dma_desc(struct uart_port *port, int index) @@ -525,7 +522,7 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param) unsigned long flags; struct dma_tx_state tx_state; - spin_lock_irqsave(&sirfport->rx_lock, flags); + spin_lock_irqsave(&port->lock, flags); while (DMA_COMPLETE == dmaengine_tx_status(sirfport->rx_dma_chan, sirfport->rx_dma_items[sirfport->rx_completed].cookie, &tx_state)) { sirfsoc_uart_insert_rx_buf_to_tty(sirfport, @@ -541,12 +538,8 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param) wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) | SIRFUART_IO_MODE); - spin_unlock_irqrestore(&sirfport->rx_lock, flags); - spin_lock(&port->lock); sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count); - spin_unlock(&port->lock); if (sirfport->rx_io_count == 4) { - spin_lock_irqsave(&sirfport->rx_lock, flags); sirfport->rx_io_count = 0; wr_regl(port, ureg->sirfsoc_int_st_reg, uint_st->sirfsoc_rx_done); @@ -557,11 +550,8 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param) else wr_regl(port, SIRFUART_INT_EN_CLR, uint_en->sirfsoc_rx_done_en); - spin_unlock_irqrestore(&sirfport->rx_lock, flags); - sirfsoc_uart_start_next_rx_dma(port); } else { - spin_lock_irqsave(&sirfport->rx_lock, flags); wr_regl(port, ureg->sirfsoc_int_st_reg, uint_st->sirfsoc_rx_done); if (!sirfport->is_marco) @@ -571,8 +561,9 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param) else wr_regl(port, ureg->sirfsoc_int_en_reg, uint_en->sirfsoc_rx_done_en); - spin_unlock_irqrestore(&sirfport->rx_lock, flags); } + spin_unlock_irqrestore(&port->lock, flags); + tty_flip_buffer_push(&port->state->port); } static void sirfsoc_uart_handle_rx_tmo(struct sirfsoc_uart_port *sirfport) @@ -581,8 +572,6 @@ static void sirfsoc_uart_handle_rx_tmo(struct sirfsoc_uart_port *sirfport) struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; struct dma_tx_state tx_state; - spin_lock(&sirfport->rx_lock); - dmaengine_tx_status(sirfport->rx_dma_chan, sirfport->rx_dma_items[sirfport->rx_issued].cookie, &tx_state); dmaengine_terminate_all(sirfport->rx_dma_chan); @@ -595,7 +584,6 @@ static void sirfsoc_uart_handle_rx_tmo(struct sirfsoc_uart_port *sirfport) else wr_regl(port, SIRFUART_INT_EN_CLR, uint_en->sirfsoc_rx_timeout_en); - spin_unlock(&sirfport->rx_lock); tasklet_schedule(&sirfport->rx_tmo_process_tasklet); } @@ -659,7 +647,6 @@ static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) intr_status &= port->read_status_mask; uart_insert_char(port, intr_status, uint_en->sirfsoc_rx_oflow_en, 0, flag); - tty_flip_buffer_push(&state->port); } recv_char: if ((sirfport->uart_reg->uart_type == SIRF_REAL_UART) && @@ -684,6 +671,9 @@ recv_char: sirfsoc_uart_pio_rx_chars(port, SIRFSOC_UART_IO_RX_MAX_CNT); } + spin_unlock(&port->lock); + tty_flip_buffer_push(&state->port); + spin_lock(&port->lock); if (intr_status & uint_st->sirfsoc_txfifo_empty) { if (sirfport->tx_dma_chan) sirfsoc_uart_tx_with_dma(sirfport); @@ -702,6 +692,7 @@ recv_char: } } spin_unlock(&port->lock); + return IRQ_HANDLED; } @@ -713,7 +704,7 @@ static void sirfsoc_uart_rx_dma_complete_tl(unsigned long param) struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; unsigned long flags; struct dma_tx_state tx_state; - spin_lock_irqsave(&sirfport->rx_lock, flags); + spin_lock_irqsave(&port->lock, flags); while (DMA_COMPLETE == dmaengine_tx_status(sirfport->rx_dma_chan, sirfport->rx_dma_items[sirfport->rx_completed].cookie, &tx_state)) { sirfsoc_uart_insert_rx_buf_to_tty(sirfport, @@ -726,17 +717,20 @@ static void sirfsoc_uart_rx_dma_complete_tl(unsigned long param) sirfport->rx_completed++; sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT; } - spin_unlock_irqrestore(&sirfport->rx_lock, flags); + spin_unlock_irqrestore(&port->lock, flags); + tty_flip_buffer_push(&port->state->port); } static void sirfsoc_uart_rx_dma_complete_callback(void *param) { struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param; - spin_lock(&sirfport->rx_lock); + unsigned long flags; + + spin_lock_irqsave(&sirfport->port.lock, flags); sirfport->rx_issued++; sirfport->rx_issued %= SIRFSOC_RX_LOOP_BUF_CNT; - spin_unlock(&sirfport->rx_lock); tasklet_schedule(&sirfport->rx_dma_complete_tasklet); + spin_unlock_irqrestore(&sirfport->port.lock, flags); } /* submit rx dma task into dmaengine */ @@ -745,18 +739,14 @@ static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port) struct sirfsoc_uart_port *sirfport = to_sirfport(port); struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - unsigned long flags; int i; - spin_lock_irqsave(&sirfport->rx_lock, flags); sirfport->rx_io_count = 0; wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) & ~SIRFUART_IO_MODE); - spin_unlock_irqrestore(&sirfport->rx_lock, flags); for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++) sirfsoc_rx_submit_one_dma_desc(port, i); sirfport->rx_completed = sirfport->rx_issued = 0; - spin_lock_irqsave(&sirfport->rx_lock, flags); if (!sirfport->is_marco) wr_regl(port, ureg->sirfsoc_int_en_reg, rd_regl(port, ureg->sirfsoc_int_en_reg) | @@ -764,7 +754,6 @@ static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port) else wr_regl(port, ureg->sirfsoc_int_en_reg, SIRFUART_RX_DMA_INT_EN(port, uint_en)); - spin_unlock_irqrestore(&sirfport->rx_lock, flags); } static void sirfsoc_uart_start_rx(struct uart_port *port) @@ -1228,7 +1217,7 @@ static void sirfsoc_uart_console_putchar(struct uart_port *port, int ch) while (rd_regl(port, ureg->sirfsoc_tx_fifo_status) & ufifo_st->ff_full(port->line)) cpu_relax(); - wr_regb(port, ureg->sirfsoc_tx_fifo_data, ch); + wr_regl(port, ureg->sirfsoc_tx_fifo_data, ch); } static void sirfsoc_uart_console_write(struct console *co, const char *s, @@ -1369,8 +1358,6 @@ usp_no_flow_control: ret = -EFAULT; goto err; } - spin_lock_init(&sirfport->rx_lock); - spin_lock_init(&sirfport->tx_lock); tasklet_init(&sirfport->rx_dma_complete_tasklet, sirfsoc_uart_rx_dma_complete_tl, (unsigned long)sirfport); tasklet_init(&sirfport->rx_tmo_process_tasklet, diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h index 8a6eddad2f3c..69a62ebd3afc 100644 --- a/drivers/tty/serial/sirfsoc_uart.h +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -424,8 +424,6 @@ struct sirfsoc_uart_port { struct dma_chan *tx_dma_chan; dma_addr_t tx_dma_addr; struct dma_async_tx_descriptor *tx_dma_desc; - spinlock_t rx_lock; - spinlock_t tx_lock; struct tasklet_struct rx_dma_complete_tasklet; struct tasklet_struct rx_tmo_process_tasklet; unsigned int rx_io_count; @@ -441,9 +439,7 @@ struct sirfsoc_uart_port { /* Register Access Control */ #define portaddr(port, reg) ((port)->membase + (reg)) -#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg))) #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg))) -#define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg)) #define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg)) /* UART Port Mask */ diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index dd3a96e07026..c7f61ac27132 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -194,9 +194,9 @@ static inline u32 asc_txfifo_is_empty(struct uart_port *port) return asc_in(port, ASC_STA) & ASC_STA_TE; } -static inline int asc_txfifo_is_full(struct uart_port *port) +static inline u32 asc_txfifo_is_half_empty(struct uart_port *port) { - return asc_in(port, ASC_STA) & ASC_STA_TF; + return asc_in(port, ASC_STA) & ASC_STA_THE; } static inline const char *asc_port_name(struct uart_port *port) @@ -628,7 +628,7 @@ static int asc_get_poll_char(struct uart_port *port) static void asc_put_poll_char(struct uart_port *port, unsigned char c) { - while (asc_txfifo_is_full(port)) + while (!asc_txfifo_is_half_empty(port)) cpu_relax(); asc_out(port, ASC_TXBUF, c); } @@ -783,7 +783,7 @@ static void asc_console_putchar(struct uart_port *port, int ch) unsigned int timeout = 1000000; /* Wait for upto 1 second in case flow control is stopping us. */ - while (--timeout && asc_txfifo_is_full(port)) + while (--timeout && !asc_txfifo_is_half_empty(port)) udelay(1); asc_out(port, ASC_TXBUF, ch); diff --git a/drivers/tty/serial/tilegx.c b/drivers/tty/serial/tilegx.c index f92d7e6bd876..613ccf09dc2a 100644 --- a/drivers/tty/serial/tilegx.c +++ b/drivers/tty/serial/tilegx.c @@ -359,8 +359,8 @@ static int tilegx_startup(struct uart_port *port) } /* Create our IRQs. */ - port->irq = create_irq(); - if (port->irq < 0) + port->irq = irq_alloc_hwirq(-1); + if (!port->irq) goto err_uart_dest; tile_irq_activate(port->irq, TILE_IRQ_PERCPU); @@ -395,7 +395,7 @@ static int tilegx_startup(struct uart_port *port) err_free_irq: free_irq(port->irq, port); err_dest_irq: - destroy_irq(port->irq); + irq_free_hwirq(port->irq); err_uart_dest: gxio_uart_destroy(context); ret = -ENXIO; @@ -435,7 +435,7 @@ static void tilegx_shutdown(struct uart_port *port) if (port->irq > 0) { free_irq(port->irq, port); - destroy_irq(port->irq); + irq_free_hwirq(port->irq); port->irq = 0; } diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 5f90ef24d475..dce27f34937e 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -418,14 +418,23 @@ static struct uart_ops ulite_ops = { #ifdef CONFIG_SERIAL_UARTLITE_CONSOLE static void ulite_console_wait_tx(struct uart_port *port) { - int i; u8 val; - - /* Spin waiting for TX fifo to have space available */ - for (i = 0; i < 100000; i++) { + unsigned long timeout; + + /* + * Spin waiting for TX fifo to have space available. + * When using the Microblaze Debug Module this can take up to 1s + */ + timeout = jiffies + msecs_to_jiffies(1000); + while (1) { val = uart_in32(ULITE_STATUS, port); if ((val & ULITE_STATUS_TXFULL) == 0) break; + if (time_after(jiffies, timeout)) { + dev_warn(port->dev, + "timeout waiting for TX buffer empty\n"); + break; + } cpu_relax(); } } diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index f619ad5b5eae..8809775e2ba3 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1,7 +1,7 @@ /* - * Xilinx PS UART driver + * Cadence UART driver (found in Xilinx Zynq) * - * 2011 - 2013 (C) Xilinx Inc. + * 2011 - 2014 (C) Xilinx Inc. * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public @@ -9,6 +9,9 @@ * either version 2 of the License, or (at your option) any * later version. * + * This driver has originally been pushed by Xilinx using a Zynq-branding. This + * still shows in the naming of this file, the kconfig symbols and some symbols + * in the code. */ #if defined(CONFIG_SERIAL_XILINX_PS_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) @@ -28,16 +31,16 @@ #include <linux/of.h> #include <linux/module.h> -#define XUARTPS_TTY_NAME "ttyPS" -#define XUARTPS_NAME "xuartps" -#define XUARTPS_MAJOR 0 /* use dynamic node allocation */ -#define XUARTPS_MINOR 0 /* works best with devtmpfs */ -#define XUARTPS_NR_PORTS 2 -#define XUARTPS_FIFO_SIZE 64 /* FIFO size */ -#define XUARTPS_REGISTER_SPACE 0xFFF +#define CDNS_UART_TTY_NAME "ttyPS" +#define CDNS_UART_NAME "xuartps" +#define CDNS_UART_MAJOR 0 /* use dynamic node allocation */ +#define CDNS_UART_MINOR 0 /* works best with devtmpfs */ +#define CDNS_UART_NR_PORTS 2 +#define CDNS_UART_FIFO_SIZE 64 /* FIFO size */ +#define CDNS_UART_REGISTER_SPACE 0xFFF -#define xuartps_readl(offset) ioread32(port->membase + offset) -#define xuartps_writel(val, offset) iowrite32(val, port->membase + offset) +#define cdns_uart_readl(offset) ioread32(port->membase + offset) +#define cdns_uart_writel(val, offset) iowrite32(val, port->membase + offset) /* Rx Trigger level */ static int rx_trigger_level = 56; @@ -49,77 +52,62 @@ static int rx_timeout = 10; module_param(rx_timeout, uint, S_IRUGO); MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); -/********************************Register Map********************************/ -/** UART - * - * Register offsets for the UART. - * - */ -#define XUARTPS_CR_OFFSET 0x00 /* Control Register [8:0] */ -#define XUARTPS_MR_OFFSET 0x04 /* Mode Register [10:0] */ -#define XUARTPS_IER_OFFSET 0x08 /* Interrupt Enable [10:0] */ -#define XUARTPS_IDR_OFFSET 0x0C /* Interrupt Disable [10:0] */ -#define XUARTPS_IMR_OFFSET 0x10 /* Interrupt Mask [10:0] */ -#define XUARTPS_ISR_OFFSET 0x14 /* Interrupt Status [10:0]*/ -#define XUARTPS_BAUDGEN_OFFSET 0x18 /* Baud Rate Generator [15:0] */ -#define XUARTPS_RXTOUT_OFFSET 0x1C /* RX Timeout [7:0] */ -#define XUARTPS_RXWM_OFFSET 0x20 /* RX FIFO Trigger Level [5:0] */ -#define XUARTPS_MODEMCR_OFFSET 0x24 /* Modem Control [5:0] */ -#define XUARTPS_MODEMSR_OFFSET 0x28 /* Modem Status [8:0] */ -#define XUARTPS_SR_OFFSET 0x2C /* Channel Status [11:0] */ -#define XUARTPS_FIFO_OFFSET 0x30 /* FIFO [15:0] or [7:0] */ -#define XUARTPS_BAUDDIV_OFFSET 0x34 /* Baud Rate Divider [7:0] */ -#define XUARTPS_FLOWDEL_OFFSET 0x38 /* Flow Delay [15:0] */ -#define XUARTPS_IRRX_PWIDTH_OFFSET 0x3C /* IR Minimum Received Pulse - Width [15:0] */ -#define XUARTPS_IRTX_PWIDTH_OFFSET 0x40 /* IR Transmitted pulse - Width [7:0] */ -#define XUARTPS_TXWM_OFFSET 0x44 /* TX FIFO Trigger Level [5:0] */ - -/** Control Register - * - * The Control register (CR) controls the major functions of the device. - * - * Control Register Bit Definitions - */ -#define XUARTPS_CR_STOPBRK 0x00000100 /* Stop TX break */ -#define XUARTPS_CR_STARTBRK 0x00000080 /* Set TX break */ -#define XUARTPS_CR_TX_DIS 0x00000020 /* TX disabled. */ -#define XUARTPS_CR_TX_EN 0x00000010 /* TX enabled */ -#define XUARTPS_CR_RX_DIS 0x00000008 /* RX disabled. */ -#define XUARTPS_CR_RX_EN 0x00000004 /* RX enabled */ -#define XUARTPS_CR_TXRST 0x00000002 /* TX logic reset */ -#define XUARTPS_CR_RXRST 0x00000001 /* RX logic reset */ -#define XUARTPS_CR_RST_TO 0x00000040 /* Restart Timeout Counter */ - -/** Mode Register - * +/* 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 */ + +/* Control Register Bit Definitions */ +#define CDNS_UART_CR_STOPBRK 0x00000100 /* Stop TX break */ +#define CDNS_UART_CR_STARTBRK 0x00000080 /* Set TX break */ +#define CDNS_UART_CR_TX_DIS 0x00000020 /* TX disabled. */ +#define CDNS_UART_CR_TX_EN 0x00000010 /* TX enabled */ +#define CDNS_UART_CR_RX_DIS 0x00000008 /* RX disabled. */ +#define CDNS_UART_CR_RX_EN 0x00000004 /* RX enabled */ +#define CDNS_UART_CR_TXRST 0x00000002 /* TX logic reset */ +#define CDNS_UART_CR_RXRST 0x00000001 /* RX logic reset */ +#define CDNS_UART_CR_RST_TO 0x00000040 /* Restart Timeout Counter */ + +/* + * Mode Register: * The mode register (MR) defines the mode of transfer as well as the data * format. If this register is modified during transmission or reception, * data validity cannot be guaranteed. - * - * Mode Register Bit Definitions - * */ -#define XUARTPS_MR_CLKSEL 0x00000001 /* Pre-scalar selection */ -#define XUARTPS_MR_CHMODE_L_LOOP 0x00000200 /* Local loop back mode */ -#define XUARTPS_MR_CHMODE_NORM 0x00000000 /* Normal mode */ +#define CDNS_UART_MR_CLKSEL 0x00000001 /* Pre-scalar selection */ +#define CDNS_UART_MR_CHMODE_L_LOOP 0x00000200 /* Local loop back mode */ +#define CDNS_UART_MR_CHMODE_NORM 0x00000000 /* Normal mode */ -#define XUARTPS_MR_STOPMODE_2_BIT 0x00000080 /* 2 stop bits */ -#define XUARTPS_MR_STOPMODE_1_BIT 0x00000000 /* 1 stop bit */ +#define CDNS_UART_MR_STOPMODE_2_BIT 0x00000080 /* 2 stop bits */ +#define CDNS_UART_MR_STOPMODE_1_BIT 0x00000000 /* 1 stop bit */ -#define XUARTPS_MR_PARITY_NONE 0x00000020 /* No parity mode */ -#define XUARTPS_MR_PARITY_MARK 0x00000018 /* Mark parity mode */ -#define XUARTPS_MR_PARITY_SPACE 0x00000010 /* Space parity mode */ -#define XUARTPS_MR_PARITY_ODD 0x00000008 /* Odd parity mode */ -#define XUARTPS_MR_PARITY_EVEN 0x00000000 /* Even parity mode */ +#define CDNS_UART_MR_PARITY_NONE 0x00000020 /* No parity mode */ +#define CDNS_UART_MR_PARITY_MARK 0x00000018 /* Mark parity mode */ +#define CDNS_UART_MR_PARITY_SPACE 0x00000010 /* Space parity mode */ +#define CDNS_UART_MR_PARITY_ODD 0x00000008 /* Odd parity mode */ +#define CDNS_UART_MR_PARITY_EVEN 0x00000000 /* Even parity mode */ -#define XUARTPS_MR_CHARLEN_6_BIT 0x00000006 /* 6 bits data */ -#define XUARTPS_MR_CHARLEN_7_BIT 0x00000004 /* 7 bits data */ -#define XUARTPS_MR_CHARLEN_8_BIT 0x00000000 /* 8 bits data */ +#define CDNS_UART_MR_CHARLEN_6_BIT 0x00000006 /* 6 bits data */ +#define CDNS_UART_MR_CHARLEN_7_BIT 0x00000004 /* 7 bits data */ +#define CDNS_UART_MR_CHARLEN_8_BIT 0x00000000 /* 8 bits data */ -/** Interrupt Registers - * +/* + * Interrupt Registers: * Interrupt control logic uses the interrupt enable register (IER) and the * interrupt disable register (IDR) to set the value of the bits in the * interrupt mask register (IMR). The IMR determines whether to pass an @@ -127,65 +115,65 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); * Writing a 1 to IER Enables an interrupt, writing a 1 to IDR disables an * interrupt. IMR and ISR are read only, and IER and IDR are write only. * Reading either IER or IDR returns 0x00. - * * All four registers have the same bit definitions. */ -#define XUARTPS_IXR_TOUT 0x00000100 /* RX Timeout error interrupt */ -#define XUARTPS_IXR_PARITY 0x00000080 /* Parity error interrupt */ -#define XUARTPS_IXR_FRAMING 0x00000040 /* Framing error interrupt */ -#define XUARTPS_IXR_OVERRUN 0x00000020 /* Overrun error interrupt */ -#define XUARTPS_IXR_TXFULL 0x00000010 /* TX FIFO Full interrupt */ -#define XUARTPS_IXR_TXEMPTY 0x00000008 /* TX FIFO empty interrupt */ -#define XUARTPS_ISR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt */ -#define XUARTPS_IXR_RXTRIG 0x00000001 /* RX FIFO trigger interrupt */ -#define XUARTPS_IXR_RXFULL 0x00000004 /* RX FIFO full interrupt. */ -#define XUARTPS_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */ -#define XUARTPS_IXR_MASK 0x00001FFF /* Valid bit mask */ +#define CDNS_UART_IXR_TOUT 0x00000100 /* RX Timeout error interrupt */ +#define CDNS_UART_IXR_PARITY 0x00000080 /* Parity error interrupt */ +#define CDNS_UART_IXR_FRAMING 0x00000040 /* Framing error interrupt */ +#define CDNS_UART_IXR_OVERRUN 0x00000020 /* Overrun error interrupt */ +#define CDNS_UART_IXR_TXFULL 0x00000010 /* TX FIFO Full interrupt */ +#define CDNS_UART_IXR_TXEMPTY 0x00000008 /* TX FIFO empty interrupt */ +#define CDNS_UART_ISR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt */ +#define CDNS_UART_IXR_RXTRIG 0x00000001 /* RX FIFO trigger interrupt */ +#define CDNS_UART_IXR_RXFULL 0x00000004 /* RX FIFO full interrupt. */ +#define CDNS_UART_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */ +#define CDNS_UART_IXR_MASK 0x00001FFF /* Valid bit mask */ /* Goes in read_status_mask for break detection as the HW doesn't do it*/ -#define XUARTPS_IXR_BRK 0x80000000 +#define CDNS_UART_IXR_BRK 0x80000000 -/** Channel Status Register - * +/* + * Channel Status Register: * The channel status register (CSR) is provided to enable the control logic * to monitor the status of bits in the channel interrupt status register, * even if these are masked out by the interrupt mask register. */ -#define XUARTPS_SR_RXEMPTY 0x00000002 /* RX FIFO empty */ -#define XUARTPS_SR_TXEMPTY 0x00000008 /* TX FIFO empty */ -#define XUARTPS_SR_TXFULL 0x00000010 /* TX FIFO full */ -#define XUARTPS_SR_RXTRIG 0x00000001 /* Rx Trigger */ +#define CDNS_UART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */ +#define CDNS_UART_SR_TXEMPTY 0x00000008 /* TX FIFO empty */ +#define CDNS_UART_SR_TXFULL 0x00000010 /* TX FIFO full */ +#define CDNS_UART_SR_RXTRIG 0x00000001 /* Rx Trigger */ /* baud dividers min/max values */ -#define XUARTPS_BDIV_MIN 4 -#define XUARTPS_BDIV_MAX 255 -#define XUARTPS_CD_MAX 65535 +#define CDNS_UART_BDIV_MIN 4 +#define CDNS_UART_BDIV_MAX 255 +#define CDNS_UART_CD_MAX 65535 /** - * struct xuartps - device data - * @port Pointer to the UART port - * @refclk Reference clock - * @aperclk APB clock - * @baud Current baud rate - * @clk_rate_change_nb Notifier block for clock changes + * struct cdns_uart - device data + * @port: Pointer to the UART port + * @uartclk: Reference clock + * @pclk: APB clock + * @baud: Current baud rate + * @clk_rate_change_nb: Notifier block for clock changes */ -struct xuartps { +struct cdns_uart { struct uart_port *port; - struct clk *refclk; - struct clk *aperclk; + struct clk *uartclk; + struct clk *pclk; unsigned int baud; struct notifier_block clk_rate_change_nb; }; -#define to_xuartps(_nb) container_of(_nb, struct xuartps, clk_rate_change_nb); +#define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \ + clk_rate_change_nb); /** - * xuartps_isr - Interrupt handler + * cdns_uart_isr - Interrupt handler * @irq: Irq number * @dev_id: Id of the port * - * Returns IRQHANDLED - **/ -static irqreturn_t xuartps_isr(int irq, void *dev_id) + * 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; @@ -198,43 +186,42 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) /* Read the interrupt status register to determine which * interrupt(s) is/are active. */ - isrstatus = xuartps_readl(XUARTPS_ISR_OFFSET); + isrstatus = cdns_uart_readl(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 & XUARTPS_IXR_FRAMING) { - while (!(xuartps_readl(XUARTPS_SR_OFFSET) & - XUARTPS_SR_RXEMPTY)) { - if (!xuartps_readl(XUARTPS_FIFO_OFFSET)) { - port->read_status_mask |= XUARTPS_IXR_BRK; - isrstatus &= ~XUARTPS_IXR_FRAMING; + if (isrstatus & CDNS_UART_IXR_FRAMING) { + while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & + CDNS_UART_SR_RXEMPTY)) { + if (!cdns_uart_readl(CDNS_UART_FIFO_OFFSET)) { + port->read_status_mask |= CDNS_UART_IXR_BRK; + isrstatus &= ~CDNS_UART_IXR_FRAMING; } } - xuartps_writel(XUARTPS_IXR_FRAMING, XUARTPS_ISR_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_FRAMING, CDNS_UART_ISR_OFFSET); } /* drop byte with parity error if IGNPAR specified */ - if (isrstatus & port->ignore_status_mask & XUARTPS_IXR_PARITY) - isrstatus &= ~(XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT); + if (isrstatus & port->ignore_status_mask & CDNS_UART_IXR_PARITY) + isrstatus &= ~(CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT); isrstatus &= port->read_status_mask; isrstatus &= ~port->ignore_status_mask; - if ((isrstatus & XUARTPS_IXR_TOUT) || - (isrstatus & XUARTPS_IXR_RXTRIG)) { + if ((isrstatus & CDNS_UART_IXR_TOUT) || + (isrstatus & CDNS_UART_IXR_RXTRIG)) { /* Receive Timeout Interrupt */ - while ((xuartps_readl(XUARTPS_SR_OFFSET) & - XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) { - data = xuartps_readl(XUARTPS_FIFO_OFFSET); + while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & + CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) { + data = cdns_uart_readl(CDNS_UART_FIFO_OFFSET); /* Non-NULL byte after BREAK is garbage (99%) */ if (data && (port->read_status_mask & - XUARTPS_IXR_BRK)) { - port->read_status_mask &= ~XUARTPS_IXR_BRK; + CDNS_UART_IXR_BRK)) { + port->read_status_mask &= ~CDNS_UART_IXR_BRK; port->icount.brk++; if (uart_handle_break(port)) continue; @@ -258,16 +245,17 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) port->icount.rx++; - if (isrstatus & XUARTPS_IXR_PARITY) { + if (isrstatus & CDNS_UART_IXR_PARITY) { port->icount.parity++; status = TTY_PARITY; - } else if (isrstatus & XUARTPS_IXR_FRAMING) { + } else if (isrstatus & CDNS_UART_IXR_FRAMING) { port->icount.frame++; status = TTY_FRAME; - } else if (isrstatus & XUARTPS_IXR_OVERRUN) + } else if (isrstatus & CDNS_UART_IXR_OVERRUN) { port->icount.overrun++; + } - uart_insert_char(port, isrstatus, XUARTPS_IXR_OVERRUN, + uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN, data, status); } spin_unlock(&port->lock); @@ -276,10 +264,10 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) } /* Dispatch an appropriate handler */ - if ((isrstatus & XUARTPS_IXR_TXEMPTY) == XUARTPS_IXR_TXEMPTY) { + if ((isrstatus & CDNS_UART_IXR_TXEMPTY) == CDNS_UART_IXR_TXEMPTY) { if (uart_circ_empty(&port->state->xmit)) { - xuartps_writel(XUARTPS_IXR_TXEMPTY, - XUARTPS_IDR_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_TXEMPTY, + CDNS_UART_IDR_OFFSET); } else { numbytes = port->fifosize; /* Break if no more data available in the UART buffer */ @@ -287,12 +275,12 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) if (uart_circ_empty(&port->state->xmit)) break; /* Get the data from the UART circular buffer - * and write it to the xuartps's TX_FIFO + * and write it to the cdns_uart's TX_FIFO * register. */ - xuartps_writel( + cdns_uart_writel( port->state->xmit.buf[port->state->xmit. - tail], XUARTPS_FIFO_OFFSET); + tail], CDNS_UART_FIFO_OFFSET); port->icount.tx++; @@ -300,7 +288,7 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) * the buffer if it reaches limit. */ port->state->xmit.tail = - (port->state->xmit.tail + 1) & \ + (port->state->xmit.tail + 1) & (UART_XMIT_SIZE - 1); } @@ -310,7 +298,7 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) } } - xuartps_writel(isrstatus, XUARTPS_ISR_OFFSET); + cdns_uart_writel(isrstatus, CDNS_UART_ISR_OFFSET); /* be sure to release the lock and tty before leaving */ spin_unlock_irqrestore(&port->lock, flags); @@ -319,13 +307,13 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) } /** - * xuartps_calc_baud_divs - Calculate baud rate divisors + * cdns_uart_calc_baud_divs - Calculate baud rate divisors * @clk: UART module input clock * @baud: Desired baud rate * @rbdiv: BDIV value (return value) * @rcd: CD value (return value) * @div8: Value for clk_sel bit in mod (return value) - * Returns baud rate, requested baud when possible, or actual baud when there + * Return: baud rate, requested baud when possible, or actual baud when there * was too much error, zero if no valid divisors are found. * * Formula to obtain baud rate is @@ -338,8 +326,8 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) * baud rate generate register * baud rate clock divisor register */ -static unsigned int xuartps_calc_baud_divs(unsigned int clk, unsigned int baud, - u32 *rbdiv, u32 *rcd, int *div8) +static unsigned int cdns_uart_calc_baud_divs(unsigned int clk, + unsigned int baud, u32 *rbdiv, u32 *rcd, int *div8) { u32 cd, bdiv; unsigned int calc_baud; @@ -347,16 +335,16 @@ static unsigned int xuartps_calc_baud_divs(unsigned int clk, unsigned int baud, unsigned int bauderror; unsigned int besterror = ~0; - if (baud < clk / ((XUARTPS_BDIV_MAX + 1) * XUARTPS_CD_MAX)) { + if (baud < clk / ((CDNS_UART_BDIV_MAX + 1) * CDNS_UART_CD_MAX)) { *div8 = 1; clk /= 8; } else { *div8 = 0; } - for (bdiv = XUARTPS_BDIV_MIN; bdiv <= XUARTPS_BDIV_MAX; bdiv++) { + for (bdiv = CDNS_UART_BDIV_MIN; bdiv <= CDNS_UART_BDIV_MAX; bdiv++) { cd = DIV_ROUND_CLOSEST(clk, baud * (bdiv + 1)); - if (cd < 1 || cd > XUARTPS_CD_MAX) + if (cd < 1 || cd > CDNS_UART_CD_MAX) continue; calc_baud = clk / (cd * (bdiv + 1)); @@ -381,47 +369,47 @@ static unsigned int xuartps_calc_baud_divs(unsigned int clk, unsigned int baud, } /** - * xuartps_set_baud_rate - Calculate and set the baud rate + * cdns_uart_set_baud_rate - Calculate and set the baud rate * @port: Handle to the uart port structure * @baud: Baud rate to set - * Returns baud rate, requested baud when possible, or actual baud when there + * Return: baud rate, requested baud when possible, or actual baud when there * was too much error, zero if no valid divisors are found. */ -static unsigned int xuartps_set_baud_rate(struct uart_port *port, +static unsigned int cdns_uart_set_baud_rate(struct uart_port *port, unsigned int baud) { unsigned int calc_baud; u32 cd = 0, bdiv = 0; u32 mreg; int div8; - struct xuartps *xuartps = port->private_data; + struct cdns_uart *cdns_uart = port->private_data; - calc_baud = xuartps_calc_baud_divs(port->uartclk, baud, &bdiv, &cd, + calc_baud = cdns_uart_calc_baud_divs(port->uartclk, baud, &bdiv, &cd, &div8); /* Write new divisors to hardware */ - mreg = xuartps_readl(XUARTPS_MR_OFFSET); + mreg = cdns_uart_readl(CDNS_UART_MR_OFFSET); if (div8) - mreg |= XUARTPS_MR_CLKSEL; + mreg |= CDNS_UART_MR_CLKSEL; else - mreg &= ~XUARTPS_MR_CLKSEL; - xuartps_writel(mreg, XUARTPS_MR_OFFSET); - xuartps_writel(cd, XUARTPS_BAUDGEN_OFFSET); - xuartps_writel(bdiv, XUARTPS_BAUDDIV_OFFSET); - xuartps->baud = baud; + mreg &= ~CDNS_UART_MR_CLKSEL; + cdns_uart_writel(mreg, CDNS_UART_MR_OFFSET); + cdns_uart_writel(cd, CDNS_UART_BAUDGEN_OFFSET); + cdns_uart_writel(bdiv, CDNS_UART_BAUDDIV_OFFSET); + cdns_uart->baud = baud; return calc_baud; } #ifdef CONFIG_COMMON_CLK /** - * xuartps_clk_notitifer_cb - Clock notifier callback + * cdns_uart_clk_notitifer_cb - Clock notifier callback * @nb: Notifier block * @event: Notify event * @data: Notifier data - * Returns NOTIFY_OK on success, NOTIFY_BAD on error. + * Return: NOTIFY_OK or NOTIFY_DONE on success, NOTIFY_BAD on error. */ -static int xuartps_clk_notifier_cb(struct notifier_block *nb, +static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, unsigned long event, void *data) { u32 ctrl_reg; @@ -429,35 +417,36 @@ static int xuartps_clk_notifier_cb(struct notifier_block *nb, int locked = 0; struct clk_notifier_data *ndata = data; unsigned long flags = 0; - struct xuartps *xuartps = to_xuartps(nb); + struct cdns_uart *cdns_uart = to_cdns_uart(nb); - port = xuartps->port; + port = cdns_uart->port; if (port->suspended) return NOTIFY_OK; switch (event) { case PRE_RATE_CHANGE: { - u32 bdiv; - u32 cd; + u32 bdiv, cd; int div8; /* * Find out if current baud-rate can be achieved with new clock * frequency. */ - if (!xuartps_calc_baud_divs(ndata->new_rate, xuartps->baud, - &bdiv, &cd, &div8)) + if (!cdns_uart_calc_baud_divs(ndata->new_rate, cdns_uart->baud, + &bdiv, &cd, &div8)) { + dev_warn(port->dev, "clock rate change rejected\n"); return NOTIFY_BAD; + } - spin_lock_irqsave(&xuartps->port->lock, flags); + spin_lock_irqsave(&cdns_uart->port->lock, flags); /* Disable the TX and RX to set baud rate */ - xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | - (XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS), - XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); - spin_unlock_irqrestore(&xuartps->port->lock, flags); + spin_unlock_irqrestore(&cdns_uart->port->lock, flags); return NOTIFY_OK; } @@ -467,25 +456,25 @@ static int xuartps_clk_notifier_cb(struct notifier_block *nb, * frequency. */ - spin_lock_irqsave(&xuartps->port->lock, flags); + spin_lock_irqsave(&cdns_uart->port->lock, flags); locked = 1; port->uartclk = ndata->new_rate; - xuartps->baud = xuartps_set_baud_rate(xuartps->port, - xuartps->baud); + cdns_uart->baud = cdns_uart_set_baud_rate(cdns_uart->port, + cdns_uart->baud); /* fall through */ case ABORT_RATE_CHANGE: if (!locked) - spin_lock_irqsave(&xuartps->port->lock, flags); + spin_lock_irqsave(&cdns_uart->port->lock, flags); /* Set TX/RX Reset */ - xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | - (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST), - XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); - while (xuartps_readl(XUARTPS_CR_OFFSET) & - (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST)) + while (cdns_uart_readl(CDNS_UART_CR_OFFSET) & + (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) cpu_relax(); /* @@ -493,14 +482,13 @@ static int xuartps_clk_notifier_cb(struct notifier_block *nb, * enable bit and RX enable bit to enable the transmitter and * receiver. */ - xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET); - ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); - xuartps_writel( - (ctrl_reg & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) | - (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN), - XUARTPS_CR_OFFSET); + cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); + ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); - spin_unlock_irqrestore(&xuartps->port->lock, flags); + spin_unlock_irqrestore(&cdns_uart->port->lock, flags); return NOTIFY_OK; default: @@ -509,40 +497,36 @@ static int xuartps_clk_notifier_cb(struct notifier_block *nb, } #endif -/*----------------------Uart Operations---------------------------*/ - /** - * xuartps_start_tx - Start transmitting bytes + * cdns_uart_start_tx - Start transmitting bytes * @port: Handle to the uart port structure - * - **/ -static void xuartps_start_tx(struct uart_port *port) + */ +static void cdns_uart_start_tx(struct uart_port *port) { unsigned int status, numbytes = port->fifosize; if (uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port)) return; - status = xuartps_readl(XUARTPS_CR_OFFSET); + status = cdns_uart_readl(CDNS_UART_CR_OFFSET); /* Set the TX enable bit and clear the TX disable bit to enable the * transmitter. */ - xuartps_writel((status & ~XUARTPS_CR_TX_DIS) | XUARTPS_CR_TX_EN, - XUARTPS_CR_OFFSET); - - while (numbytes-- && ((xuartps_readl(XUARTPS_SR_OFFSET) - & XUARTPS_SR_TXFULL)) != XUARTPS_SR_TXFULL) { + cdns_uart_writel((status & ~CDNS_UART_CR_TX_DIS) | CDNS_UART_CR_TX_EN, + CDNS_UART_CR_OFFSET); + while (numbytes-- && ((cdns_uart_readl(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; /* Get the data from the UART circular buffer and - * write it to the xuartps's TX_FIFO register. + * write it to the cdns_uart's TX_FIFO register. */ - xuartps_writel( + cdns_uart_writel( port->state->xmit.buf[port->state->xmit.tail], - XUARTPS_FIFO_OFFSET); + CDNS_UART_FIFO_OFFSET); port->icount.tx++; /* Adjust the tail of the UART buffer and wrap @@ -551,94 +535,90 @@ static void xuartps_start_tx(struct uart_port *port) port->state->xmit.tail = (port->state->xmit.tail + 1) & (UART_XMIT_SIZE - 1); } - xuartps_writel(XUARTPS_IXR_TXEMPTY, XUARTPS_ISR_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_TXEMPTY, CDNS_UART_ISR_OFFSET); /* Enable the TX Empty interrupt */ - xuartps_writel(XUARTPS_IXR_TXEMPTY, XUARTPS_IER_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_TXEMPTY, CDNS_UART_IER_OFFSET); if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS) uart_write_wakeup(port); } /** - * xuartps_stop_tx - Stop TX + * cdns_uart_stop_tx - Stop TX * @port: Handle to the uart port structure - * - **/ -static void xuartps_stop_tx(struct uart_port *port) + */ +static void cdns_uart_stop_tx(struct uart_port *port) { unsigned int regval; - regval = xuartps_readl(XUARTPS_CR_OFFSET); - regval |= XUARTPS_CR_TX_DIS; + regval = cdns_uart_readl(CDNS_UART_CR_OFFSET); + regval |= CDNS_UART_CR_TX_DIS; /* Disable the transmitter */ - xuartps_writel(regval, XUARTPS_CR_OFFSET); + cdns_uart_writel(regval, CDNS_UART_CR_OFFSET); } /** - * xuartps_stop_rx - Stop RX + * cdns_uart_stop_rx - Stop RX * @port: Handle to the uart port structure - * - **/ -static void xuartps_stop_rx(struct uart_port *port) + */ +static void cdns_uart_stop_rx(struct uart_port *port) { unsigned int regval; - regval = xuartps_readl(XUARTPS_CR_OFFSET); - regval |= XUARTPS_CR_RX_DIS; + regval = cdns_uart_readl(CDNS_UART_CR_OFFSET); + regval |= CDNS_UART_CR_RX_DIS; /* Disable the receiver */ - xuartps_writel(regval, XUARTPS_CR_OFFSET); + cdns_uart_writel(regval, CDNS_UART_CR_OFFSET); } /** - * xuartps_tx_empty - Check whether TX is empty + * cdns_uart_tx_empty - Check whether TX is empty * @port: Handle to the uart port structure * - * Returns TIOCSER_TEMT on success, 0 otherwise - **/ -static unsigned int xuartps_tx_empty(struct uart_port *port) + * Return: TIOCSER_TEMT on success, 0 otherwise + */ +static unsigned int cdns_uart_tx_empty(struct uart_port *port) { unsigned int status; - status = xuartps_readl(XUARTPS_ISR_OFFSET) & XUARTPS_IXR_TXEMPTY; + status = cdns_uart_readl(CDNS_UART_ISR_OFFSET) & CDNS_UART_IXR_TXEMPTY; return status ? TIOCSER_TEMT : 0; } /** - * xuartps_break_ctl - Based on the input ctl we have to start or stop + * cdns_uart_break_ctl - Based on the input ctl we have to start or stop * transmitting char breaks * @port: Handle to the uart port structure * @ctl: Value based on which start or stop decision is taken - * - **/ -static void xuartps_break_ctl(struct uart_port *port, int ctl) + */ +static void cdns_uart_break_ctl(struct uart_port *port, int ctl) { unsigned int status; unsigned long flags; spin_lock_irqsave(&port->lock, flags); - status = xuartps_readl(XUARTPS_CR_OFFSET); + status = cdns_uart_readl(CDNS_UART_CR_OFFSET); if (ctl == -1) - xuartps_writel(XUARTPS_CR_STARTBRK | status, - XUARTPS_CR_OFFSET); + cdns_uart_writel(CDNS_UART_CR_STARTBRK | status, + CDNS_UART_CR_OFFSET); else { - if ((status & XUARTPS_CR_STOPBRK) == 0) - xuartps_writel(XUARTPS_CR_STOPBRK | status, - XUARTPS_CR_OFFSET); + if ((status & CDNS_UART_CR_STOPBRK) == 0) + cdns_uart_writel(CDNS_UART_CR_STOPBRK | status, + CDNS_UART_CR_OFFSET); } spin_unlock_irqrestore(&port->lock, flags); } /** - * xuartps_set_termios - termios operations, handling data length, parity, + * cdns_uart_set_termios - termios operations, handling data length, parity, * stop bits, flow control, baud rate * @port: Handle to the uart port structure * @termios: Handle to the input termios structure * @old: Values of the previously saved termios structure - * - **/ -static void xuartps_set_termios(struct uart_port *port, + */ +static void cdns_uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { unsigned int cval = 0; @@ -649,81 +629,79 @@ static void xuartps_set_termios(struct uart_port *port, spin_lock_irqsave(&port->lock, flags); /* Empty the receive FIFO 1st before making changes */ - while ((xuartps_readl(XUARTPS_SR_OFFSET) & - XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) { - xuartps_readl(XUARTPS_FIFO_OFFSET); + while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & + CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) { + cdns_uart_readl(CDNS_UART_FIFO_OFFSET); } /* Disable the TX and RX to set baud rate */ - xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | - (XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS), - XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); /* * Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk * min and max baud should be calculated here based on port->uartclk. * this way we get a valid baud and can safely call set_baud() */ - minbaud = port->uartclk / ((XUARTPS_BDIV_MAX + 1) * XUARTPS_CD_MAX * 8); - maxbaud = port->uartclk / (XUARTPS_BDIV_MIN + 1); + minbaud = port->uartclk / + ((CDNS_UART_BDIV_MAX + 1) * CDNS_UART_CD_MAX * 8); + maxbaud = port->uartclk / (CDNS_UART_BDIV_MIN + 1); baud = uart_get_baud_rate(port, termios, old, minbaud, maxbaud); - baud = xuartps_set_baud_rate(port, baud); + baud = cdns_uart_set_baud_rate(port, baud); if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); - /* - * Update the per-port timeout. - */ + /* Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); /* Set TX/RX Reset */ - xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | - (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST), - XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); - ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); - - /* Clear the RX disable and TX disable bits and then set the TX enable + /* + * 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. */ - xuartps_writel( - (ctrl_reg & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) - | (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN), - XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); + ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); - xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET); + cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); - port->read_status_mask = XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RXTRIG | - XUARTPS_IXR_OVERRUN | XUARTPS_IXR_TOUT; + port->read_status_mask = CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_RXTRIG | + CDNS_UART_IXR_OVERRUN | CDNS_UART_IXR_TOUT; port->ignore_status_mask = 0; if (termios->c_iflag & INPCK) - port->read_status_mask |= XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING; + port->read_status_mask |= CDNS_UART_IXR_PARITY | + CDNS_UART_IXR_FRAMING; if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN; + port->ignore_status_mask |= CDNS_UART_IXR_PARITY | + CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN; /* ignore all characters if CREAD is not set */ if ((termios->c_cflag & CREAD) == 0) - port->ignore_status_mask |= XUARTPS_IXR_RXTRIG | - XUARTPS_IXR_TOUT | XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN; + port->ignore_status_mask |= CDNS_UART_IXR_RXTRIG | + CDNS_UART_IXR_TOUT | CDNS_UART_IXR_PARITY | + CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN; - mode_reg = xuartps_readl(XUARTPS_MR_OFFSET); + mode_reg = cdns_uart_readl(CDNS_UART_MR_OFFSET); /* Handling Data Size */ switch (termios->c_cflag & CSIZE) { case CS6: - cval |= XUARTPS_MR_CHARLEN_6_BIT; + cval |= CDNS_UART_MR_CHARLEN_6_BIT; break; case CS7: - cval |= XUARTPS_MR_CHARLEN_7_BIT; + cval |= CDNS_UART_MR_CHARLEN_7_BIT; break; default: case CS8: - cval |= XUARTPS_MR_CHARLEN_8_BIT; + cval |= CDNS_UART_MR_CHARLEN_8_BIT; termios->c_cflag &= ~CSIZE; termios->c_cflag |= CS8; break; @@ -731,134 +709,135 @@ static void xuartps_set_termios(struct uart_port *port, /* Handling Parity and Stop Bits length */ if (termios->c_cflag & CSTOPB) - cval |= XUARTPS_MR_STOPMODE_2_BIT; /* 2 STOP bits */ + cval |= CDNS_UART_MR_STOPMODE_2_BIT; /* 2 STOP bits */ else - cval |= XUARTPS_MR_STOPMODE_1_BIT; /* 1 STOP bit */ + cval |= CDNS_UART_MR_STOPMODE_1_BIT; /* 1 STOP bit */ if (termios->c_cflag & PARENB) { /* Mark or Space parity */ if (termios->c_cflag & CMSPAR) { if (termios->c_cflag & PARODD) - cval |= XUARTPS_MR_PARITY_MARK; + cval |= CDNS_UART_MR_PARITY_MARK; else - cval |= XUARTPS_MR_PARITY_SPACE; + cval |= CDNS_UART_MR_PARITY_SPACE; } else { if (termios->c_cflag & PARODD) - cval |= XUARTPS_MR_PARITY_ODD; + cval |= CDNS_UART_MR_PARITY_ODD; else - cval |= XUARTPS_MR_PARITY_EVEN; + cval |= CDNS_UART_MR_PARITY_EVEN; } } else { - cval |= XUARTPS_MR_PARITY_NONE; + cval |= CDNS_UART_MR_PARITY_NONE; } cval |= mode_reg & 1; - xuartps_writel(cval, XUARTPS_MR_OFFSET); + cdns_uart_writel(cval, CDNS_UART_MR_OFFSET); spin_unlock_irqrestore(&port->lock, flags); } /** - * xuartps_startup - Called when an application opens a xuartps port + * cdns_uart_startup - Called when an application opens a cdns_uart port * @port: Handle to the uart port structure * - * Returns 0 on success, negative error otherwise - **/ -static int xuartps_startup(struct uart_port *port) + * Return: 0 on success, negative errno otherwise + */ +static int cdns_uart_startup(struct uart_port *port) { unsigned int retval = 0, status = 0; - retval = request_irq(port->irq, xuartps_isr, 0, XUARTPS_NAME, + retval = request_irq(port->irq, cdns_uart_isr, 0, CDNS_UART_NAME, (void *)port); if (retval) return retval; /* Disable the TX and RX */ - xuartps_writel(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS, - XUARTPS_CR_OFFSET); + cdns_uart_writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, + CDNS_UART_CR_OFFSET); /* Set the Control Register with TX/RX Enable, TX/RX Reset, * no break chars. */ - xuartps_writel(XUARTPS_CR_TXRST | XUARTPS_CR_RXRST, - XUARTPS_CR_OFFSET); + cdns_uart_writel(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST, + CDNS_UART_CR_OFFSET); - status = xuartps_readl(XUARTPS_CR_OFFSET); + status = cdns_uart_readl(CDNS_UART_CR_OFFSET); /* 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. */ - xuartps_writel((status & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) - | (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN | - XUARTPS_CR_STOPBRK), XUARTPS_CR_OFFSET); + cdns_uart_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), CDNS_UART_CR_OFFSET); /* Set the Mode Register with normal mode,8 data bits,1 stop bit, * no parity. */ - xuartps_writel(XUARTPS_MR_CHMODE_NORM | XUARTPS_MR_STOPMODE_1_BIT - | XUARTPS_MR_PARITY_NONE | XUARTPS_MR_CHARLEN_8_BIT, - XUARTPS_MR_OFFSET); + cdns_uart_writel(CDNS_UART_MR_CHMODE_NORM | CDNS_UART_MR_STOPMODE_1_BIT + | CDNS_UART_MR_PARITY_NONE | CDNS_UART_MR_CHARLEN_8_BIT, + CDNS_UART_MR_OFFSET); /* * Set the RX FIFO Trigger level to use most of the FIFO, but it * can be tuned with a module parameter */ - xuartps_writel(rx_trigger_level, XUARTPS_RXWM_OFFSET); + cdns_uart_writel(rx_trigger_level, CDNS_UART_RXWM_OFFSET); /* * Receive Timeout register is enabled but it * can be tuned with a module parameter */ - xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET); + cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); /* Clear out any pending interrupts before enabling them */ - xuartps_writel(xuartps_readl(XUARTPS_ISR_OFFSET), XUARTPS_ISR_OFFSET); + cdns_uart_writel(cdns_uart_readl(CDNS_UART_ISR_OFFSET), + CDNS_UART_ISR_OFFSET); /* Set the Interrupt Registers with desired interrupts */ - xuartps_writel(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN | - XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT, XUARTPS_IER_OFFSET); + cdns_uart_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, + CDNS_UART_IER_OFFSET); return retval; } /** - * xuartps_shutdown - Called when an application closes a xuartps port + * cdns_uart_shutdown - Called when an application closes a cdns_uart port * @port: Handle to the uart port structure - * - **/ -static void xuartps_shutdown(struct uart_port *port) + */ +static void cdns_uart_shutdown(struct uart_port *port) { int status; /* Disable interrupts */ - status = xuartps_readl(XUARTPS_IMR_OFFSET); - xuartps_writel(status, XUARTPS_IDR_OFFSET); + status = cdns_uart_readl(CDNS_UART_IMR_OFFSET); + cdns_uart_writel(status, CDNS_UART_IDR_OFFSET); /* Disable the TX and RX */ - xuartps_writel(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS, - XUARTPS_CR_OFFSET); + cdns_uart_writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, + CDNS_UART_CR_OFFSET); free_irq(port->irq, port); } /** - * xuartps_type - Set UART type to xuartps port + * cdns_uart_type - Set UART type to cdns_uart port * @port: Handle to the uart port structure * - * Returns string on success, NULL otherwise - **/ -static const char *xuartps_type(struct uart_port *port) + * Return: string on success, NULL otherwise + */ +static const char *cdns_uart_type(struct uart_port *port) { - return port->type == PORT_XUARTPS ? XUARTPS_NAME : NULL; + return port->type == PORT_XUARTPS ? CDNS_UART_NAME : NULL; } /** - * xuartps_verify_port - Verify the port params + * cdns_uart_verify_port - Verify the port params * @port: Handle to the uart port structure * @ser: Handle to the structure whose members are compared * - * Returns 0 if success otherwise -EINVAL - **/ -static int xuartps_verify_port(struct uart_port *port, + * Return: 0 on success, negative errno otherwise. + */ +static int cdns_uart_verify_port(struct uart_port *port, struct serial_struct *ser) { if (ser->type != PORT_UNKNOWN && ser->type != PORT_XUARTPS) @@ -875,187 +854,170 @@ static int xuartps_verify_port(struct uart_port *port, } /** - * xuartps_request_port - Claim the memory region attached to xuartps port, - * called when the driver adds a xuartps port via + * cdns_uart_request_port - Claim the memory region attached to cdns_uart port, + * called when the driver adds a cdns_uart port via * uart_add_one_port() * @port: Handle to the uart port structure * - * Returns 0, -ENOMEM if request fails - **/ -static int xuartps_request_port(struct uart_port *port) + * Return: 0 on success, negative errno otherwise. + */ +static int cdns_uart_request_port(struct uart_port *port) { - if (!request_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE, - XUARTPS_NAME)) { + if (!request_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE, + CDNS_UART_NAME)) { return -ENOMEM; } - port->membase = ioremap(port->mapbase, XUARTPS_REGISTER_SPACE); + port->membase = ioremap(port->mapbase, CDNS_UART_REGISTER_SPACE); if (!port->membase) { dev_err(port->dev, "Unable to map registers\n"); - release_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE); + release_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE); return -ENOMEM; } return 0; } /** - * xuartps_release_port - Release the memory region attached to a xuartps - * port, called when the driver removes a xuartps - * port via uart_remove_one_port(). + * cdns_uart_release_port - Release UART port * @port: Handle to the uart port structure * - **/ -static void xuartps_release_port(struct uart_port *port) + * Release the memory region attached to a cdns_uart port. Called when the + * driver removes a cdns_uart port via uart_remove_one_port(). + */ +static void cdns_uart_release_port(struct uart_port *port) { - release_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE); + release_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE); iounmap(port->membase); port->membase = NULL; } /** - * xuartps_config_port - Configure xuartps, called when the driver adds a - * xuartps port + * cdns_uart_config_port - Configure UART port * @port: Handle to the uart port structure * @flags: If any - * - **/ -static void xuartps_config_port(struct uart_port *port, int flags) + */ +static void cdns_uart_config_port(struct uart_port *port, int flags) { - if (flags & UART_CONFIG_TYPE && xuartps_request_port(port) == 0) + if (flags & UART_CONFIG_TYPE && cdns_uart_request_port(port) == 0) port->type = PORT_XUARTPS; } /** - * xuartps_get_mctrl - Get the modem control state - * + * cdns_uart_get_mctrl - Get the modem control state * @port: Handle to the uart port structure * - * Returns the modem control state - * - **/ -static unsigned int xuartps_get_mctrl(struct uart_port *port) + * Return: the modem control state + */ +static unsigned int cdns_uart_get_mctrl(struct uart_port *port) { return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; } -static void xuartps_set_mctrl(struct uart_port *port, unsigned int mctrl) +static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { /* N/A */ } -static void xuartps_enable_ms(struct uart_port *port) +static void cdns_uart_enable_ms(struct uart_port *port) { /* N/A */ } #ifdef CONFIG_CONSOLE_POLL -static int xuartps_poll_get_char(struct uart_port *port) +static int cdns_uart_poll_get_char(struct uart_port *port) { u32 imr; int c; /* Disable all interrupts */ - imr = xuartps_readl(XUARTPS_IMR_OFFSET); - xuartps_writel(imr, XUARTPS_IDR_OFFSET); + imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET); + cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET); /* Check if FIFO is empty */ - if (xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_RXEMPTY) + if (cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_RXEMPTY) c = NO_POLL_CHAR; else /* Read a character */ - c = (unsigned char) xuartps_readl(XUARTPS_FIFO_OFFSET); + c = (unsigned char) cdns_uart_readl(CDNS_UART_FIFO_OFFSET); /* Enable interrupts */ - xuartps_writel(imr, XUARTPS_IER_OFFSET); + cdns_uart_writel(imr, CDNS_UART_IER_OFFSET); return c; } -static void xuartps_poll_put_char(struct uart_port *port, unsigned char c) +static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) { u32 imr; /* Disable all interrupts */ - imr = xuartps_readl(XUARTPS_IMR_OFFSET); - xuartps_writel(imr, XUARTPS_IDR_OFFSET); + imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET); + cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET); /* Wait until FIFO is empty */ - while (!(xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_TXEMPTY)) + while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY)) cpu_relax(); /* Write a character */ - xuartps_writel(c, XUARTPS_FIFO_OFFSET); + cdns_uart_writel(c, CDNS_UART_FIFO_OFFSET); /* Wait until FIFO is empty */ - while (!(xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_TXEMPTY)) + while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY)) cpu_relax(); /* Enable interrupts */ - xuartps_writel(imr, XUARTPS_IER_OFFSET); + cdns_uart_writel(imr, CDNS_UART_IER_OFFSET); return; } #endif -/** The UART operations structure - */ -static struct uart_ops xuartps_ops = { - .set_mctrl = xuartps_set_mctrl, - .get_mctrl = xuartps_get_mctrl, - .enable_ms = xuartps_enable_ms, - - .start_tx = xuartps_start_tx, /* Start transmitting */ - .stop_tx = xuartps_stop_tx, /* Stop transmission */ - .stop_rx = xuartps_stop_rx, /* Stop reception */ - .tx_empty = xuartps_tx_empty, /* Transmitter busy? */ - .break_ctl = xuartps_break_ctl, /* Start/stop - * transmitting break - */ - .set_termios = xuartps_set_termios, /* Set termios */ - .startup = xuartps_startup, /* App opens xuartps */ - .shutdown = xuartps_shutdown, /* App closes xuartps */ - .type = xuartps_type, /* Set UART type */ - .verify_port = xuartps_verify_port, /* Verification of port - * params - */ - .request_port = xuartps_request_port, /* Claim resources - * associated with a - * xuartps port - */ - .release_port = xuartps_release_port, /* Release resources - * associated with a - * xuartps port - */ - .config_port = xuartps_config_port, /* Configure when driver - * adds a xuartps port - */ +static struct uart_ops cdns_uart_ops = { + .set_mctrl = cdns_uart_set_mctrl, + .get_mctrl = cdns_uart_get_mctrl, + .enable_ms = cdns_uart_enable_ms, + .start_tx = cdns_uart_start_tx, + .stop_tx = cdns_uart_stop_tx, + .stop_rx = cdns_uart_stop_rx, + .tx_empty = cdns_uart_tx_empty, + .break_ctl = cdns_uart_break_ctl, + .set_termios = cdns_uart_set_termios, + .startup = cdns_uart_startup, + .shutdown = cdns_uart_shutdown, + .type = cdns_uart_type, + .verify_port = cdns_uart_verify_port, + .request_port = cdns_uart_request_port, + .release_port = cdns_uart_release_port, + .config_port = cdns_uart_config_port, #ifdef CONFIG_CONSOLE_POLL - .poll_get_char = xuartps_poll_get_char, - .poll_put_char = xuartps_poll_put_char, + .poll_get_char = cdns_uart_poll_get_char, + .poll_put_char = cdns_uart_poll_put_char, #endif }; -static struct uart_port xuartps_port[2]; +static struct uart_port cdns_uart_port[2]; /** - * xuartps_get_port - Configure the port from the platform device resource - * info + * cdns_uart_get_port - Configure the port from platform device resource info + * @id: Port id * - * Returns a pointer to a uart_port or NULL for failure - **/ -static struct uart_port *xuartps_get_port(void) + * Return: a pointer to a uart_port or NULL for failure + */ +static struct uart_port *cdns_uart_get_port(int id) { struct uart_port *port; - int id; - /* Find the next unused port */ - for (id = 0; id < XUARTPS_NR_PORTS; id++) - if (xuartps_port[id].mapbase == 0) - break; + /* Try the given port id if failed use default method */ + if (cdns_uart_port[id].mapbase != 0) { + /* Find the next unused port */ + for (id = 0; id < CDNS_UART_NR_PORTS; id++) + if (cdns_uart_port[id].mapbase == 0) + break; + } - if (id >= XUARTPS_NR_PORTS) + if (id >= CDNS_UART_NR_PORTS) return NULL; - port = &xuartps_port[id]; + port = &cdns_uart_port[id]; /* At this point, we've got an empty uart_port struct, initialize it */ spin_lock_init(&port->lock); @@ -1065,50 +1027,46 @@ static struct uart_port *xuartps_get_port(void) port->type = PORT_UNKNOWN; port->iotype = UPIO_MEM32; port->flags = UPF_BOOT_AUTOCONF; - port->ops = &xuartps_ops; - port->fifosize = XUARTPS_FIFO_SIZE; + port->ops = &cdns_uart_ops; + port->fifosize = CDNS_UART_FIFO_SIZE; port->line = id; port->dev = NULL; return port; } -/*-----------------------Console driver operations--------------------------*/ - #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE /** - * xuartps_console_wait_tx - Wait for the TX to be full + * cdns_uart_console_wait_tx - Wait for the TX to be full * @port: Handle to the uart port structure - * - **/ -static void xuartps_console_wait_tx(struct uart_port *port) + */ +static void cdns_uart_console_wait_tx(struct uart_port *port) { - while ((xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_TXEMPTY) - != XUARTPS_SR_TXEMPTY) + while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY) + != CDNS_UART_SR_TXEMPTY) barrier(); } /** - * xuartps_console_putchar - write the character to the FIFO buffer + * cdns_uart_console_putchar - write the character to the FIFO buffer * @port: Handle to the uart port structure * @ch: Character to be written - * - **/ -static void xuartps_console_putchar(struct uart_port *port, int ch) + */ +static void cdns_uart_console_putchar(struct uart_port *port, int ch) { - xuartps_console_wait_tx(port); - xuartps_writel(ch, XUARTPS_FIFO_OFFSET); + cdns_uart_console_wait_tx(port); + cdns_uart_writel(ch, CDNS_UART_FIFO_OFFSET); } /** - * xuartps_console_write - perform write operation - * @port: Handle to the uart port structure + * cdns_uart_console_write - perform write operation + * @co: Console handle * @s: Pointer to character array * @count: No of characters - **/ -static void xuartps_console_write(struct console *co, const char *s, + */ +static void cdns_uart_console_write(struct console *co, const char *s, unsigned int count) { - struct uart_port *port = &xuartps_port[co->index]; + struct uart_port *port = &cdns_uart_port[co->index]; unsigned long flags; unsigned int imr, ctrl; int locked = 1; @@ -1119,49 +1077,45 @@ static void xuartps_console_write(struct console *co, const char *s, spin_lock_irqsave(&port->lock, flags); /* save and disable interrupt */ - imr = xuartps_readl(XUARTPS_IMR_OFFSET); - xuartps_writel(imr, XUARTPS_IDR_OFFSET); + imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET); + cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET); /* * Make sure that the tx part is enabled. Set the TX enable bit and * clear the TX disable bit to enable the transmitter. */ - ctrl = xuartps_readl(XUARTPS_CR_OFFSET); - xuartps_writel((ctrl & ~XUARTPS_CR_TX_DIS) | XUARTPS_CR_TX_EN, - XUARTPS_CR_OFFSET); + ctrl = cdns_uart_readl(CDNS_UART_CR_OFFSET); + cdns_uart_writel((ctrl & ~CDNS_UART_CR_TX_DIS) | CDNS_UART_CR_TX_EN, + CDNS_UART_CR_OFFSET); - uart_console_write(port, s, count, xuartps_console_putchar); - xuartps_console_wait_tx(port); + uart_console_write(port, s, count, cdns_uart_console_putchar); + cdns_uart_console_wait_tx(port); - xuartps_writel(ctrl, XUARTPS_CR_OFFSET); + cdns_uart_writel(ctrl, CDNS_UART_CR_OFFSET); - /* restore interrupt state, it seems like there may be a h/w bug - * in that the interrupt enable register should not need to be - * written based on the data sheet - */ - xuartps_writel(~imr, XUARTPS_IDR_OFFSET); - xuartps_writel(imr, XUARTPS_IER_OFFSET); + /* restore interrupt state */ + cdns_uart_writel(imr, CDNS_UART_IER_OFFSET); if (locked) spin_unlock_irqrestore(&port->lock, flags); } /** - * xuartps_console_setup - Initialize the uart to default config + * cdns_uart_console_setup - Initialize the uart to default config * @co: Console handle * @options: Initial settings of uart * - * Returns 0, -ENODEV if no device - **/ -static int __init xuartps_console_setup(struct console *co, char *options) + * Return: 0 on success, negative errno otherwise. + */ +static int __init cdns_uart_console_setup(struct console *co, char *options) { - struct uart_port *port = &xuartps_port[co->index]; + struct uart_port *port = &cdns_uart_port[co->index]; int baud = 9600; int bits = 8; int parity = 'n'; int flow = 'n'; - if (co->index < 0 || co->index >= XUARTPS_NR_PORTS) + if (co->index < 0 || co->index >= CDNS_UART_NR_PORTS) return -EINVAL; if (!port->mapbase) { @@ -1175,55 +1129,53 @@ static int __init xuartps_console_setup(struct console *co, char *options) return uart_set_options(port, co, baud, parity, bits, flow); } -static struct uart_driver xuartps_uart_driver; +static struct uart_driver cdns_uart_uart_driver; -static struct console xuartps_console = { - .name = XUARTPS_TTY_NAME, - .write = xuartps_console_write, +static struct console cdns_uart_console = { + .name = CDNS_UART_TTY_NAME, + .write = cdns_uart_console_write, .device = uart_console_device, - .setup = xuartps_console_setup, + .setup = cdns_uart_console_setup, .flags = CON_PRINTBUFFER, .index = -1, /* Specified on the cmdline (e.g. console=ttyPS ) */ - .data = &xuartps_uart_driver, + .data = &cdns_uart_uart_driver, }; /** - * xuartps_console_init - Initialization call + * cdns_uart_console_init - Initialization call * - * Returns 0 on success, negative error otherwise - **/ -static int __init xuartps_console_init(void) + * Return: 0 on success, negative errno otherwise + */ +static int __init cdns_uart_console_init(void) { - register_console(&xuartps_console); + register_console(&cdns_uart_console); return 0; } -console_initcall(xuartps_console_init); +console_initcall(cdns_uart_console_init); #endif /* CONFIG_SERIAL_XILINX_PS_UART_CONSOLE */ -/** Structure Definitions - */ -static struct uart_driver xuartps_uart_driver = { - .owner = THIS_MODULE, /* Owner */ - .driver_name = XUARTPS_NAME, /* Driver name */ - .dev_name = XUARTPS_TTY_NAME, /* Node name */ - .major = XUARTPS_MAJOR, /* Major number */ - .minor = XUARTPS_MINOR, /* Minor number */ - .nr = XUARTPS_NR_PORTS, /* Number of UART ports */ +static struct uart_driver cdns_uart_uart_driver = { + .owner = THIS_MODULE, + .driver_name = CDNS_UART_NAME, + .dev_name = CDNS_UART_TTY_NAME, + .major = CDNS_UART_MAJOR, + .minor = CDNS_UART_MINOR, + .nr = CDNS_UART_NR_PORTS, #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE - .cons = &xuartps_console, /* Console */ + .cons = &cdns_uart_console, #endif }; #ifdef CONFIG_PM_SLEEP /** - * xuartps_suspend - suspend event + * cdns_uart_suspend - suspend event * @device: Pointer to the device structure * - * Returns 0 + * Return: 0 */ -static int xuartps_suspend(struct device *device) +static int cdns_uart_suspend(struct device *device) { struct uart_port *port = dev_get_drvdata(device); struct tty_struct *tty; @@ -1242,23 +1194,24 @@ static int xuartps_suspend(struct device *device) * Call the API provided in serial_core.c file which handles * the suspend. */ - uart_suspend_port(&xuartps_uart_driver, port); + uart_suspend_port(&cdns_uart_uart_driver, port); if (console_suspend_enabled && !may_wake) { - struct xuartps *xuartps = port->private_data; + struct cdns_uart *cdns_uart = port->private_data; - clk_disable(xuartps->refclk); - clk_disable(xuartps->aperclk); + clk_disable(cdns_uart->uartclk); + clk_disable(cdns_uart->pclk); } else { unsigned long flags = 0; spin_lock_irqsave(&port->lock, flags); /* Empty the receive FIFO 1st before making changes */ - while (!(xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_RXEMPTY)) - xuartps_readl(XUARTPS_FIFO_OFFSET); + while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & + CDNS_UART_SR_RXEMPTY)) + cdns_uart_readl(CDNS_UART_FIFO_OFFSET); /* set RX trigger level to 1 */ - xuartps_writel(1, XUARTPS_RXWM_OFFSET); + cdns_uart_writel(1, CDNS_UART_RXWM_OFFSET); /* disable RX timeout interrups */ - xuartps_writel(XUARTPS_IXR_TOUT, XUARTPS_IDR_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_TOUT, CDNS_UART_IDR_OFFSET); spin_unlock_irqrestore(&port->lock, flags); } @@ -1266,12 +1219,12 @@ static int xuartps_suspend(struct device *device) } /** - * xuartps_resume - Resume after a previous suspend + * cdns_uart_resume - Resume after a previous suspend * @device: Pointer to the device structure * - * Returns 0 + * Return: 0 */ -static int xuartps_resume(struct device *device) +static int cdns_uart_resume(struct device *device) { struct uart_port *port = dev_get_drvdata(device); unsigned long flags = 0; @@ -1289,87 +1242,95 @@ static int xuartps_resume(struct device *device) } if (console_suspend_enabled && !may_wake) { - struct xuartps *xuartps = port->private_data; + struct cdns_uart *cdns_uart = port->private_data; - clk_enable(xuartps->aperclk); - clk_enable(xuartps->refclk); + clk_enable(cdns_uart->pclk); + clk_enable(cdns_uart->uartclk); spin_lock_irqsave(&port->lock, flags); /* Set TX/RX Reset */ - xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | - (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST), - XUARTPS_CR_OFFSET); - while (xuartps_readl(XUARTPS_CR_OFFSET) & - (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST)) + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); + while (cdns_uart_readl(CDNS_UART_CR_OFFSET) & + (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) cpu_relax(); /* restore rx timeout value */ - xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET); + cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); /* Enable Tx/Rx */ - ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); - xuartps_writel( - (ctrl_reg & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) | - (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN), - XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); + ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); spin_unlock_irqrestore(&port->lock, flags); } else { spin_lock_irqsave(&port->lock, flags); /* restore original rx trigger level */ - xuartps_writel(rx_trigger_level, XUARTPS_RXWM_OFFSET); + cdns_uart_writel(rx_trigger_level, CDNS_UART_RXWM_OFFSET); /* enable RX timeout interrupt */ - xuartps_writel(XUARTPS_IXR_TOUT, XUARTPS_IER_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_TOUT, CDNS_UART_IER_OFFSET); spin_unlock_irqrestore(&port->lock, flags); } - return uart_resume_port(&xuartps_uart_driver, port); + return uart_resume_port(&cdns_uart_uart_driver, port); } #endif /* ! CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(xuartps_dev_pm_ops, xuartps_suspend, xuartps_resume); +static SIMPLE_DEV_PM_OPS(cdns_uart_dev_pm_ops, cdns_uart_suspend, + cdns_uart_resume); -/* --------------------------------------------------------------------- - * Platform bus binding - */ /** - * xuartps_probe - Platform driver probe + * cdns_uart_probe - Platform driver probe * @pdev: Pointer to the platform device structure * - * Returns 0 on success, negative error otherwise - **/ -static int xuartps_probe(struct platform_device *pdev) + * Return: 0 on success, negative errno otherwise + */ +static int cdns_uart_probe(struct platform_device *pdev) { - int rc; + int rc, id; struct uart_port *port; struct resource *res, *res2; - struct xuartps *xuartps_data; + struct cdns_uart *cdns_uart_data; - xuartps_data = devm_kzalloc(&pdev->dev, sizeof(*xuartps_data), + cdns_uart_data = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_data), GFP_KERNEL); - if (!xuartps_data) + if (!cdns_uart_data) return -ENOMEM; - xuartps_data->aperclk = devm_clk_get(&pdev->dev, "aper_clk"); - if (IS_ERR(xuartps_data->aperclk)) { - dev_err(&pdev->dev, "aper_clk clock not found.\n"); - return PTR_ERR(xuartps_data->aperclk); + cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(cdns_uart_data->pclk)) { + cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "aper_clk"); + if (!IS_ERR(cdns_uart_data->pclk)) + dev_err(&pdev->dev, "clock name 'aper_clk' is deprecated.\n"); } - xuartps_data->refclk = devm_clk_get(&pdev->dev, "ref_clk"); - if (IS_ERR(xuartps_data->refclk)) { - dev_err(&pdev->dev, "ref_clk clock not found.\n"); - return PTR_ERR(xuartps_data->refclk); + if (IS_ERR(cdns_uart_data->pclk)) { + dev_err(&pdev->dev, "pclk clock not found.\n"); + return PTR_ERR(cdns_uart_data->pclk); } - rc = clk_prepare_enable(xuartps_data->aperclk); + cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "uart_clk"); + if (IS_ERR(cdns_uart_data->uartclk)) { + cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "ref_clk"); + if (!IS_ERR(cdns_uart_data->uartclk)) + dev_err(&pdev->dev, "clock name 'ref_clk' is deprecated.\n"); + } + if (IS_ERR(cdns_uart_data->uartclk)) { + dev_err(&pdev->dev, "uart_clk clock not found.\n"); + return PTR_ERR(cdns_uart_data->uartclk); + } + + rc = clk_prepare_enable(cdns_uart_data->pclk); if (rc) { - dev_err(&pdev->dev, "Unable to enable APER clock.\n"); + dev_err(&pdev->dev, "Unable to enable pclk clock.\n"); return rc; } - rc = clk_prepare_enable(xuartps_data->refclk); + rc = clk_prepare_enable(cdns_uart_data->uartclk); if (rc) { dev_err(&pdev->dev, "Unable to enable device clock.\n"); - goto err_out_clk_dis_aper; + goto err_out_clk_dis_pclk; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1385,15 +1346,19 @@ static int xuartps_probe(struct platform_device *pdev) } #ifdef CONFIG_COMMON_CLK - xuartps_data->clk_rate_change_nb.notifier_call = - xuartps_clk_notifier_cb; - if (clk_notifier_register(xuartps_data->refclk, - &xuartps_data->clk_rate_change_nb)) + cdns_uart_data->clk_rate_change_nb.notifier_call = + cdns_uart_clk_notifier_cb; + if (clk_notifier_register(cdns_uart_data->uartclk, + &cdns_uart_data->clk_rate_change_nb)) dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); #endif + /* Look for a serialN alias */ + id = of_alias_get_id(pdev->dev.of_node, "serial"); + if (id < 0) + id = 0; /* Initialize the port structure */ - port = xuartps_get_port(); + port = cdns_uart_get_port(id); if (!port) { dev_err(&pdev->dev, "Cannot get uart_port structure\n"); @@ -1407,11 +1372,11 @@ static int xuartps_probe(struct platform_device *pdev) port->mapbase = res->start; port->irq = res2->start; port->dev = &pdev->dev; - port->uartclk = clk_get_rate(xuartps_data->refclk); - port->private_data = xuartps_data; - xuartps_data->port = port; + 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(&xuartps_uart_driver, 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); @@ -1422,103 +1387,89 @@ static int xuartps_probe(struct platform_device *pdev) err_out_notif_unreg: #ifdef CONFIG_COMMON_CLK - clk_notifier_unregister(xuartps_data->refclk, - &xuartps_data->clk_rate_change_nb); + clk_notifier_unregister(cdns_uart_data->uartclk, + &cdns_uart_data->clk_rate_change_nb); #endif err_out_clk_disable: - clk_disable_unprepare(xuartps_data->refclk); -err_out_clk_dis_aper: - clk_disable_unprepare(xuartps_data->aperclk); + clk_disable_unprepare(cdns_uart_data->uartclk); +err_out_clk_dis_pclk: + clk_disable_unprepare(cdns_uart_data->pclk); return rc; } /** - * xuartps_remove - called when the platform driver is unregistered + * cdns_uart_remove - called when the platform driver is unregistered * @pdev: Pointer to the platform device structure * - * Returns 0 on success, negative error otherwise - **/ -static int xuartps_remove(struct platform_device *pdev) + * Return: 0 on success, negative errno otherwise + */ +static int cdns_uart_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); - struct xuartps *xuartps_data = port->private_data; + struct cdns_uart *cdns_uart_data = port->private_data; int rc; - /* Remove the xuartps port from the serial core */ + /* Remove the cdns_uart port from the serial core */ #ifdef CONFIG_COMMON_CLK - clk_notifier_unregister(xuartps_data->refclk, - &xuartps_data->clk_rate_change_nb); + clk_notifier_unregister(cdns_uart_data->uartclk, + &cdns_uart_data->clk_rate_change_nb); #endif - rc = uart_remove_one_port(&xuartps_uart_driver, port); + rc = uart_remove_one_port(&cdns_uart_uart_driver, port); port->mapbase = 0; - clk_disable_unprepare(xuartps_data->refclk); - clk_disable_unprepare(xuartps_data->aperclk); + clk_disable_unprepare(cdns_uart_data->uartclk); + clk_disable_unprepare(cdns_uart_data->pclk); return rc; } /* Match table for of_platform binding */ -static struct of_device_id xuartps_of_match[] = { +static struct of_device_id cdns_uart_of_match[] = { { .compatible = "xlnx,xuartps", }, + { .compatible = "cdns,uart-r1p8", }, {} }; -MODULE_DEVICE_TABLE(of, xuartps_of_match); +MODULE_DEVICE_TABLE(of, cdns_uart_of_match); -static struct platform_driver xuartps_platform_driver = { - .probe = xuartps_probe, /* Probe method */ - .remove = xuartps_remove, /* Detach method */ +static struct platform_driver cdns_uart_platform_driver = { + .probe = cdns_uart_probe, + .remove = cdns_uart_remove, .driver = { .owner = THIS_MODULE, - .name = XUARTPS_NAME, /* Driver name */ - .of_match_table = xuartps_of_match, - .pm = &xuartps_dev_pm_ops, + .name = CDNS_UART_NAME, + .of_match_table = cdns_uart_of_match, + .pm = &cdns_uart_dev_pm_ops, }, }; -/* --------------------------------------------------------------------- - * Module Init and Exit - */ -/** - * xuartps_init - Initial driver registration call - * - * Returns whether the registration was successful or not - **/ -static int __init xuartps_init(void) +static int __init cdns_uart_init(void) { int retval = 0; - /* Register the xuartps driver with the serial core */ - retval = uart_register_driver(&xuartps_uart_driver); + /* Register the cdns_uart driver with the serial core */ + retval = uart_register_driver(&cdns_uart_uart_driver); if (retval) return retval; /* Register the platform driver */ - retval = platform_driver_register(&xuartps_platform_driver); + retval = platform_driver_register(&cdns_uart_platform_driver); if (retval) - uart_unregister_driver(&xuartps_uart_driver); + uart_unregister_driver(&cdns_uart_uart_driver); return retval; } -/** - * xuartps_exit - Driver unregistration call - **/ -static void __exit xuartps_exit(void) +static void __exit cdns_uart_exit(void) { - /* The order of unregistration is important. Unregister the - * UART driver before the platform driver crashes the system. - */ - /* Unregister the platform driver */ - platform_driver_unregister(&xuartps_platform_driver); + platform_driver_unregister(&cdns_uart_platform_driver); - /* Unregister the xuartps driver */ - uart_unregister_driver(&xuartps_uart_driver); + /* Unregister the cdns_uart driver */ + uart_unregister_driver(&cdns_uart_uart_driver); } -module_init(xuartps_init); -module_exit(xuartps_exit); +module_init(cdns_uart_init); +module_exit(cdns_uart_exit); -MODULE_DESCRIPTION("Driver for PS UART"); +MODULE_DESCRIPTION("Driver for Cadence UART"); MODULE_AUTHOR("Xilinx Inc."); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index ce396ecdf412..454b65898e2c 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -46,6 +46,7 @@ #include <linux/jiffies.h> #include <linux/syscalls.h> #include <linux/of.h> +#include <linux/rcupdate.h> #include <asm/ptrace.h> #include <asm/irq_regs.h> @@ -88,7 +89,7 @@ static void sysrq_handle_loglevel(int key) int i; i = key - '0'; - console_loglevel = 7; + console_loglevel = CONSOLE_LOGLEVEL_DEFAULT; printk("Loglevel set to %d\n", i); console_loglevel = i; } @@ -343,7 +344,7 @@ static void send_sig_all(int sig) static void sysrq_handle_term(int key) { send_sig_all(SIGTERM); - console_loglevel = 8; + console_loglevel = CONSOLE_LOGLEVEL_DEBUG; } static struct sysrq_key_op sysrq_term_op = { .handler = sysrq_handle_term, @@ -387,7 +388,7 @@ static struct sysrq_key_op sysrq_thaw_op = { static void sysrq_handle_kill(int key) { send_sig_all(SIGKILL); - console_loglevel = 8; + console_loglevel = CONSOLE_LOGLEVEL_DEBUG; } static struct sysrq_key_op sysrq_kill_op = { .handler = sysrq_handle_kill, @@ -510,9 +511,9 @@ void __handle_sysrq(int key, bool check_mask) struct sysrq_key_op *op_p; int orig_log_level; int i; - unsigned long flags; - spin_lock_irqsave(&sysrq_key_table_lock, flags); + rcu_sysrq_start(); + rcu_read_lock(); /* * Raise the apparent loglevel to maximum so that the sysrq header * is shown to provide the user with positive feedback. We do not @@ -520,7 +521,7 @@ void __handle_sysrq(int key, bool check_mask) * routing in the consumers of /proc/kmsg. */ orig_log_level = console_loglevel; - console_loglevel = 7; + console_loglevel = CONSOLE_LOGLEVEL_DEFAULT; printk(KERN_INFO "SysRq : "); op_p = __sysrq_get_key_op(key); @@ -554,7 +555,8 @@ void __handle_sysrq(int key, bool check_mask) printk("\n"); console_loglevel = orig_log_level; } - spin_unlock_irqrestore(&sysrq_key_table_lock, flags); + rcu_read_unlock(); + rcu_sysrq_end(); } void handle_sysrq(int key) @@ -1043,16 +1045,23 @@ static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p, struct sysrq_key_op *remove_op_p) { int retval; - unsigned long flags; - spin_lock_irqsave(&sysrq_key_table_lock, flags); + spin_lock(&sysrq_key_table_lock); if (__sysrq_get_key_op(key) == remove_op_p) { __sysrq_put_key_op(key, insert_op_p); retval = 0; } else { retval = -1; } - spin_unlock_irqrestore(&sysrq_key_table_lock, flags); + spin_unlock(&sysrq_key_table_lock); + + /* + * A concurrent __handle_sysrq either got the old op or the new op. + * Wait for it to go away before returning, so the code for an old + * op is not freed (eg. on module unload) while it is in use. + */ + synchronize_rcu(); + return retval; } diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 2978ca596a7f..610b720d3b91 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -179,7 +179,6 @@ struct uni_pagedir { unsigned long sum; unsigned char *inverse_translations[4]; u16 *inverse_trans_unicode; - int readonly; }; static struct uni_pagedir *dflt; @@ -262,7 +261,7 @@ u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode) int m; if (glyph < 0 || glyph >= MAX_GLYPH) return 0; - else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc)) + else if (!(p = *conp->vc_uni_pagedir_loc)) return glyph; else if (use_unicode) { if (!p->inverse_trans_unicode) @@ -287,7 +286,7 @@ static void update_user_maps(void) for (i = 0; i < MAX_NR_CONSOLES; i++) { if (!vc_cons_allocated(i)) continue; - p = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc; + p = *vc_cons[i].d->vc_uni_pagedir_loc; if (p && p != q) { set_inverse_transl(vc_cons[i].d, p, USER_MAP); set_inverse_trans_unicode(vc_cons[i].d, p); @@ -418,10 +417,10 @@ void con_free_unimap(struct vc_data *vc) { struct uni_pagedir *p; - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; + p = *vc->vc_uni_pagedir_loc; if (!p) return; - *vc->vc_uni_pagedir_loc = 0; + *vc->vc_uni_pagedir_loc = NULL; if (--p->refcount) return; con_release_unimap(p); @@ -436,7 +435,7 @@ static int con_unify_unimap(struct vc_data *conp, struct uni_pagedir *p) for (i = 0; i < MAX_NR_CONSOLES; i++) { if (!vc_cons_allocated(i)) continue; - q = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc; + q = *vc_cons[i].d->vc_uni_pagedir_loc; if (!q || q == p || q->sum != p->sum) continue; for (j = 0; j < 32; j++) { @@ -459,7 +458,7 @@ static int con_unify_unimap(struct vc_data *conp, struct uni_pagedir *p) } if (j == 32) { q->refcount++; - *conp->vc_uni_pagedir_loc = (unsigned long)q; + *conp->vc_uni_pagedir_loc = q; con_release_unimap(p); kfree(p); return 1; @@ -500,10 +499,7 @@ static int con_do_clear_unimap(struct vc_data *vc, struct unimapinit *ui) { struct uni_pagedir *p, *q; - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - if (p && p->readonly) - return -EIO; - + p = *vc->vc_uni_pagedir_loc; if (!p || --p->refcount) { q = kzalloc(sizeof(*p), GFP_KERNEL); if (!q) { @@ -512,7 +508,7 @@ static int con_do_clear_unimap(struct vc_data *vc, struct unimapinit *ui) return -ENOMEM; } q->refcount=1; - *vc->vc_uni_pagedir_loc = (unsigned long)q; + *vc->vc_uni_pagedir_loc = q; } else { if (p == dflt) dflt = NULL; p->refcount++; @@ -536,19 +532,13 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) int err = 0, err1, i; struct uni_pagedir *p, *q; + if (!ct) + return 0; + console_lock(); /* Save original vc_unipagdir_loc in case we allocate a new one */ - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - if (p->readonly) { - console_unlock(); - return -EIO; - } - - if (!ct) { - console_unlock(); - return 0; - } + p = *vc->vc_uni_pagedir_loc; if (p->refcount > 1) { int j, k; @@ -564,7 +554,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) * Since refcount was > 1, con_clear_unimap() allocated a * a new uni_pagedir for this vc. Re: p != q */ - q = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; + q = *vc->vc_uni_pagedir_loc; /* * uni_pgdir is a 32*32*64 table with rows allocated @@ -586,7 +576,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) err1 = con_insert_unipair(q, l, p2[k]); if (err1) { p->refcount++; - *vc->vc_uni_pagedir_loc = (unsigned long)p; + *vc->vc_uni_pagedir_loc = p; con_release_unimap(q); kfree(q); console_unlock(); @@ -655,12 +645,12 @@ int con_set_default_unimap(struct vc_data *vc) struct uni_pagedir *p; if (dflt) { - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; + p = *vc->vc_uni_pagedir_loc; if (p == dflt) return 0; dflt->refcount++; - *vc->vc_uni_pagedir_loc = (unsigned long)dflt; + *vc->vc_uni_pagedir_loc = dflt; if (p && !--p->refcount) { con_release_unimap(p); kfree(p); @@ -674,7 +664,7 @@ int con_set_default_unimap(struct vc_data *vc) if (err) return err; - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; + p = *vc->vc_uni_pagedir_loc; q = dfont_unitable; for (i = 0; i < 256; i++) @@ -685,7 +675,7 @@ int con_set_default_unimap(struct vc_data *vc) } if (con_unify_unimap(vc, p)) { - dflt = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; + dflt = *vc->vc_uni_pagedir_loc; return err; } @@ -713,9 +703,9 @@ int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc) if (*dst_vc->vc_uni_pagedir_loc == *src_vc->vc_uni_pagedir_loc) return 0; con_free_unimap(dst_vc); - q = (struct uni_pagedir *)*src_vc->vc_uni_pagedir_loc; + q = *src_vc->vc_uni_pagedir_loc; q->refcount++; - *dst_vc->vc_uni_pagedir_loc = (long)q; + *dst_vc->vc_uni_pagedir_loc = q; return 0; } EXPORT_SYMBOL(con_copy_unimap); @@ -737,7 +727,7 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni ect = 0; if (*vc->vc_uni_pagedir_loc) { - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; + p = *vc->vc_uni_pagedir_loc; for (i = 0; i < 32; i++) if ((p1 = p->uni_pgdir[i])) for (j = 0; j < 32; j++) @@ -810,7 +800,7 @@ conv_uni_to_pc(struct vc_data *conp, long ucs) if (!*conp->vc_uni_pagedir_loc) return -3; - p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc; + p = *conp->vc_uni_pagedir_loc; if ((p1 = p->uni_pgdir[ucs >> 11]) && (p2 = p1[(ucs >> 6) & 0x1f]) && (h = p2[ucs & 0x3f]) < MAX_GLYPH) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 3ad0b61e35b4..5e0f6ff2e2f5 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -735,7 +735,7 @@ static void visual_init(struct vc_data *vc, int num, int init) vc->vc_num = num; vc->vc_display_fg = &master_display_fg; vc->vc_uni_pagedir_loc = &vc->vc_uni_pagedir; - vc->vc_uni_pagedir = 0; + vc->vc_uni_pagedir = NULL; vc->vc_hi_font_mask = 0; vc->vc_complement_mask = 0; vc->vc_can_do_color = 0; @@ -1231,6 +1231,52 @@ static void default_attr(struct vc_data *vc) vc->vc_color = vc->vc_def_color; } +struct rgb { u8 r; u8 g; u8 b; }; + +struct rgb rgb_from_256(int i) +{ + struct rgb c; + if (i < 8) { /* Standard colours. */ + c.r = i&1 ? 0xaa : 0x00; + c.g = i&2 ? 0xaa : 0x00; + c.b = i&4 ? 0xaa : 0x00; + } else if (i < 16) { + c.r = i&1 ? 0xff : 0x55; + c.g = i&2 ? 0xff : 0x55; + c.b = i&4 ? 0xff : 0x55; + } else if (i < 232) { /* 6x6x6 colour cube. */ + c.r = (i - 16) / 36 * 85 / 2; + c.g = (i - 16) / 6 % 6 * 85 / 2; + c.b = (i - 16) % 6 * 85 / 2; + } else /* Grayscale ramp. */ + c.r = c.g = c.b = i * 10 - 2312; + return c; +} + +static void rgb_foreground(struct vc_data *vc, struct rgb c) +{ + u8 hue, max = c.r; + if (c.g > max) + max = c.g; + if (c.b > max) + max = c.b; + hue = (c.r > max/2 ? 4 : 0) + | (c.g > max/2 ? 2 : 0) + | (c.b > max/2 ? 1 : 0); + if (hue == 7 && max <= 0x55) + hue = 0, vc->vc_intensity = 2; + else + vc->vc_intensity = (max > 0xaa) + 1; + vc->vc_color = (vc->vc_color & 0xf0) | hue; +} + +static void rgb_background(struct vc_data *vc, struct rgb c) +{ + /* For backgrounds, err on the dark side. */ + vc->vc_color = (vc->vc_color & 0x0f) + | (c.r&0x80) >> 1 | (c.g&0x80) >> 2 | (c.b&0x80) >> 3; +} + /* console_lock is held */ static void csi_m(struct vc_data *vc) { @@ -1302,8 +1348,7 @@ static void csi_m(struct vc_data *vc) case 27: vc->vc_reverse = 0; break; - case 38: - case 48: /* ITU T.416 + case 38: /* ITU T.416 * Higher colour modes. * They break the usual properties of SGR codes * and thus need to be detected and ignored by @@ -1315,15 +1360,41 @@ static void csi_m(struct vc_data *vc) i++; if (i > vc->vc_npar) break; - if (vc->vc_par[i] == 5) /* 256 colours */ - i++; /* ubiquitous */ - else if (vc->vc_par[i] == 2) /* 24 bit colours */ - i += 3; /* extremely rare */ + if (vc->vc_par[i] == 5 && /* 256 colours */ + i < vc->vc_npar) { /* ubiquitous */ + i++; + rgb_foreground(vc, + rgb_from_256(vc->vc_par[i])); + } else if (vc->vc_par[i] == 2 && /* 24 bit */ + i <= vc->vc_npar + 3) {/* extremely rare */ + struct rgb c = {r:vc->vc_par[i+1], + g:vc->vc_par[i+2], + b:vc->vc_par[i+3]}; + rgb_foreground(vc, c); + i += 3; + } /* Subcommands 3 (CMY) and 4 (CMYK) are so insane - * that detecting them is not worth the few extra - * bytes of kernel's size. + * there's no point in supporting them. */ break; + case 48: + i++; + if (i > vc->vc_npar) + break; + if (vc->vc_par[i] == 5 && /* 256 colours */ + i < vc->vc_npar) { + i++; + rgb_background(vc, + rgb_from_256(vc->vc_par[i])); + } else if (vc->vc_par[i] == 2 && /* 24 bit */ + i <= vc->vc_npar + 3) { + struct rgb c = {r:vc->vc_par[i+1], + g:vc->vc_par[i+2], + b:vc->vc_par[i+3]}; + rgb_background(vc, c); + i += 3; + } + break; case 39: vc->vc_color = (vc->vc_def_color & 0x0f) | (vc->vc_color & 0xf0); break; |