diff options
Diffstat (limited to 'drivers/tty')
65 files changed, 1744 insertions, 368 deletions
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 95103054c0e4..873e0ba89737 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -392,6 +392,9 @@ config PPC_EARLY_DEBUG_EHV_BC_HANDLE config GOLDFISH_TTY tristate "Goldfish TTY Driver" depends on GOLDFISH + select SERIAL_CORE + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON help Console and system TTY driver for the Goldfish virtual platform. diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index 61fe8d6fd24e..a1c7125cb968 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -122,7 +122,7 @@ static int find_console_handle(void) stdout_irq = irq_of_parse_and_map(np, 0); if (stdout_irq == NO_IRQ) { - pr_err("ehv-bc: no 'interrupts' property in %s node\n", np->full_name); + pr_err("ehv-bc: no 'interrupts' property in %pOF node\n", np); return 0; } diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index 996bd473dd03..381e981dee06 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2007 Google, Inc. * Copyright (C) 2012 Intel, Inc. + * Copyright (C) 2017 Imagination Technologies Ltd. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -22,21 +23,23 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/goldfish.h> - -enum { - GOLDFISH_TTY_PUT_CHAR = 0x00, - GOLDFISH_TTY_BYTES_READY = 0x04, - GOLDFISH_TTY_CMD = 0x08, - - 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, - GOLDFISH_TTY_CMD_WRITE_BUFFER = 2, - GOLDFISH_TTY_CMD_READ_BUFFER = 3, -}; +#include <linux/mm.h> +#include <linux/dma-mapping.h> +#include <linux/serial_core.h> + +/* Goldfish tty register's offsets */ +#define GOLDFISH_TTY_REG_BYTES_READY 0x04 +#define GOLDFISH_TTY_REG_CMD 0x08 +#define GOLDFISH_TTY_REG_DATA_PTR 0x10 +#define GOLDFISH_TTY_REG_DATA_LEN 0x14 +#define GOLDFISH_TTY_REG_DATA_PTR_HIGH 0x18 +#define GOLDFISH_TTY_REG_VERSION 0x20 + +/* Goldfish tty commands */ +#define GOLDFISH_TTY_CMD_INT_DISABLE 0 +#define GOLDFISH_TTY_CMD_INT_ENABLE 1 +#define GOLDFISH_TTY_CMD_WRITE_BUFFER 2 +#define GOLDFISH_TTY_CMD_READ_BUFFER 3 struct goldfish_tty { struct tty_port port; @@ -45,6 +48,8 @@ struct goldfish_tty { u32 irq; int opencount; struct console console; + u32 version; + struct device *dev; }; static DEFINE_MUTEX(goldfish_tty_lock); @@ -53,38 +58,107 @@ static u32 goldfish_tty_line_count = 8; static u32 goldfish_tty_current_line_count; static struct goldfish_tty *goldfish_ttys; -static void goldfish_tty_do_write(int line, const char *buf, unsigned count) +static void do_rw_io(struct goldfish_tty *qtty, + unsigned long address, + unsigned int count, + int is_write) { unsigned long irq_flags; - struct goldfish_tty *qtty = &goldfish_ttys[line]; void __iomem *base = qtty->base; + spin_lock_irqsave(&qtty->lock, irq_flags); - gf_write_ptr(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); + gf_write_ptr((void *)address, base + GOLDFISH_TTY_REG_DATA_PTR, + base + GOLDFISH_TTY_REG_DATA_PTR_HIGH); + writel(count, base + GOLDFISH_TTY_REG_DATA_LEN); + + if (is_write) + writel(GOLDFISH_TTY_CMD_WRITE_BUFFER, + base + GOLDFISH_TTY_REG_CMD); + else + writel(GOLDFISH_TTY_CMD_READ_BUFFER, + base + GOLDFISH_TTY_REG_CMD); + spin_unlock_irqrestore(&qtty->lock, irq_flags); } +static void goldfish_tty_rw(struct goldfish_tty *qtty, + unsigned long addr, + unsigned int count, + int is_write) +{ + dma_addr_t dma_handle; + enum dma_data_direction dma_dir; + + dma_dir = (is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (qtty->version > 0) { + /* + * Goldfish TTY for Ranchu platform uses + * physical addresses and DMA for read/write operations + */ + unsigned long addr_end = addr + count; + + while (addr < addr_end) { + unsigned long pg_end = (addr & PAGE_MASK) + PAGE_SIZE; + unsigned long next = + pg_end < addr_end ? pg_end : addr_end; + unsigned long avail = next - addr; + + /* + * Map the buffer's virtual address to the DMA address + * so the buffer can be accessed by the device. + */ + dma_handle = dma_map_single(qtty->dev, (void *)addr, + avail, dma_dir); + + if (dma_mapping_error(qtty->dev, dma_handle)) { + dev_err(qtty->dev, "tty: DMA mapping error.\n"); + return; + } + do_rw_io(qtty, dma_handle, avail, is_write); + + /* + * Unmap the previously mapped region after + * the completion of the read/write operation. + */ + dma_unmap_single(qtty->dev, dma_handle, avail, dma_dir); + + addr += avail; + } + } else { + /* + * Old style Goldfish TTY used on the Goldfish platform + * uses virtual addresses. + */ + do_rw_io(qtty, addr, count, is_write); + } +} + +static void goldfish_tty_do_write(int line, const char *buf, + unsigned int count) +{ + struct goldfish_tty *qtty = &goldfish_ttys[line]; + unsigned long address = (unsigned long)(void *)buf; + + goldfish_tty_rw(qtty, address, count, 1); +} + static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id) { struct goldfish_tty *qtty = dev_id; void __iomem *base = qtty->base; - unsigned long irq_flags; + unsigned long address; unsigned char *buf; u32 count; - count = readl(base + GOLDFISH_TTY_BYTES_READY); + count = readl(base + GOLDFISH_TTY_REG_BYTES_READY); if (count == 0) return IRQ_NONE; count = tty_prepare_flip_string(&qtty->port, &buf, count); - spin_lock_irqsave(&qtty->lock, irq_flags); - gf_write_ptr(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); + + address = (unsigned long)(void *)buf; + goldfish_tty_rw(qtty, address, count, 0); + tty_schedule_flip(&qtty->port); return IRQ_HANDLED; } @@ -93,7 +167,7 @@ static int goldfish_tty_activate(struct tty_port *port, struct tty_struct *tty) { struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, port); - writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_CMD); + writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_REG_CMD); return 0; } @@ -101,7 +175,7 @@ static void goldfish_tty_shutdown(struct tty_port *port) { struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, port); - writel(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_CMD); + writel(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_REG_CMD); } static int goldfish_tty_open(struct tty_struct *tty, struct file *filp) @@ -136,7 +210,7 @@ static int goldfish_tty_chars_in_buffer(struct tty_struct *tty) { struct goldfish_tty *qtty = &goldfish_ttys[tty->index]; void __iomem *base = qtty->base; - return readl(base + GOLDFISH_TTY_BYTES_READY); + return readl(base + GOLDFISH_TTY_REG_BYTES_READY); } static void goldfish_tty_console_write(struct console *co, const char *b, @@ -227,7 +301,7 @@ static void goldfish_tty_delete_driver(void) static int goldfish_tty_probe(struct platform_device *pdev) { struct goldfish_tty *qtty; - int ret = -EINVAL; + int ret = -ENODEV; struct resource *r; struct device *ttydev; void __iomem *base; @@ -235,16 +309,22 @@ static int goldfish_tty_probe(struct platform_device *pdev) unsigned int line; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) - return -EINVAL; + if (!r) { + pr_err("goldfish_tty: No MEM resource available!\n"); + return -ENOMEM; + } base = ioremap(r->start, 0x1000); - if (base == NULL) - pr_err("goldfish_tty: unable to remap base\n"); + if (!base) { + pr_err("goldfish_tty: Unable to ioremap base!\n"); + return -ENOMEM; + } r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (r == NULL) + if (!r) { + pr_err("goldfish_tty: No IRQ resource available!\n"); goto err_unmap; + } irq = r->start; @@ -255,13 +335,17 @@ static int goldfish_tty_probe(struct platform_device *pdev) else line = pdev->id; - if (line >= goldfish_tty_line_count) - goto err_create_driver_failed; + if (line >= goldfish_tty_line_count) { + pr_err("goldfish_tty: Reached maximum tty number of %d.\n", + goldfish_tty_current_line_count); + ret = -ENOMEM; + goto err_unlock; + } if (goldfish_tty_current_line_count == 0) { ret = goldfish_tty_create_driver(); if (ret) - goto err_create_driver_failed; + goto err_unlock; } goldfish_tty_current_line_count++; @@ -271,17 +355,45 @@ static int goldfish_tty_probe(struct platform_device *pdev) qtty->port.ops = &goldfish_port_ops; qtty->base = base; qtty->irq = irq; + qtty->dev = &pdev->dev; + + /* + * Goldfish TTY device used by the Goldfish emulator + * should identify itself with 0, forcing the driver + * to use virtual addresses. Goldfish TTY device + * on Ranchu emulator (qemu2) returns 1 here and + * driver will use physical addresses. + */ + qtty->version = readl(base + GOLDFISH_TTY_REG_VERSION); + + /* + * Goldfish TTY device on Ranchu emulator (qemu2) + * will use DMA for read/write IO operations. + */ + if (qtty->version > 0) { + /* + * Initialize dma_mask to 32-bits. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "No suitable DMA available.\n"); + goto err_dec_line_count; + } + } - writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_CMD); + writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_REG_CMD); ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED, - "goldfish_tty", qtty); - if (ret) - goto err_request_irq_failed; - + "goldfish_tty", qtty); + if (ret) { + pr_err("goldfish_tty: No IRQ available!\n"); + goto err_dec_line_count; + } ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver, - line, &pdev->dev); + line, &pdev->dev); if (IS_ERR(ttydev)) { ret = PTR_ERR(ttydev); goto err_tty_register_device_failed; @@ -301,11 +413,11 @@ static int goldfish_tty_probe(struct platform_device *pdev) err_tty_register_device_failed: free_irq(irq, qtty); -err_request_irq_failed: +err_dec_line_count: goldfish_tty_current_line_count--; if (goldfish_tty_current_line_count == 0) goldfish_tty_delete_driver(); -err_create_driver_failed: +err_unlock: mutex_unlock(&goldfish_tty_lock); err_unmap: iounmap(base); @@ -330,6 +442,30 @@ static int goldfish_tty_remove(struct platform_device *pdev) return 0; } +static void gf_early_console_putchar(struct uart_port *port, int ch) +{ + __raw_writel(ch, port->membase); +} + +static void gf_early_write(struct console *con, const char *s, unsigned int n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, gf_early_console_putchar); +} + +static int __init gf_earlycon_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = gf_early_write; + return 0; +} + +OF_EARLYCON_DECLARE(early_gf_tty, "google,goldfish-tty", gf_earlycon_setup); + static const struct of_device_id goldfish_tty_of_match[] = { { .compatible = "google,goldfish-tty", }, {}, diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index b8d5ea0ae26b..fec457edad14 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -4,7 +4,7 @@ config HVC_DRIVER bool help Generic "hypervisor virtual console" infrastructure for various - hypervisors (pSeries, iSeries, Xen, lguest). + hypervisors (pSeries, iSeries, Xen). It will automatically be selected if one of the back-end console drivers is selected. diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c index 510799311099..16331a90c1e8 100644 --- a/drivers/tty/hvc/hvc_opal.c +++ b/drivers/tty/hvc/hvc_opal.c @@ -179,8 +179,8 @@ static int hvc_opal_probe(struct platform_device *dev) proto = HV_PROTOCOL_HVSI; ops = &hvc_opal_hvsi_ops; } else { - pr_err("hvc_opal: Unknown protocol for %s\n", - dev->dev.of_node->full_name); + pr_err("hvc_opal: Unknown protocol for %pOF\n", + dev->dev.of_node); return -ENXIO; } @@ -204,14 +204,14 @@ static int hvc_opal_probe(struct platform_device *dev) /* Instanciate now to establish a mapping index==vtermno */ hvc_instantiate(termno, termno, ops); } else { - pr_err("hvc_opal: Device %s has duplicate terminal number #%d\n", - dev->dev.of_node->full_name, termno); + pr_err("hvc_opal: Device %pOF has duplicate terminal number #%d\n", + dev->dev.of_node, termno); return -ENXIO; } - pr_info("hvc%d: %s protocol on %s%s\n", termno, + pr_info("hvc%d: %s protocol on %pOF%s\n", termno, proto == HV_PROTOCOL_RAW ? "raw" : "hvsi", - dev->dev.of_node->full_name, + dev->dev.of_node, boot ? " (boot console)" : ""); irq = irq_of_parse_and_map(dev->dev.of_node, 0); @@ -222,8 +222,8 @@ static int hvc_opal_probe(struct platform_device *dev) } if (!irq) { - pr_err("hvc_opal: Unable to map interrupt for device %s\n", - dev->dev.of_node->full_name); + pr_err("hvc_opal: Unable to map interrupt for device %pOF\n", + dev->dev.of_node); return irq; } diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index 6ffbdd8d50c5..a1d272ac82bb 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c @@ -53,7 +53,7 @@ static const char hvc_driver_name[] = "hvc_console"; -static struct vio_device_id hvc_driver_table[] = { +static const struct vio_device_id hvc_driver_table[] = { {"serial", "hvterm1"}, #ifndef HVC_OLD_HVSI {"serial", "hvterm-protocol"}, @@ -312,12 +312,12 @@ static int hvc_vio_probe(struct vio_dev *vdev, proto = HV_PROTOCOL_HVSI; ops = &hvterm_hvsi_ops; } else { - pr_err("hvc_vio: Unknown protocol for %s\n", vdev->dev.of_node->full_name); + pr_err("hvc_vio: Unknown protocol for %pOF\n", vdev->dev.of_node); return -ENXIO; } - pr_devel("hvc_vio_probe() device %s, using %s protocol\n", - vdev->dev.of_node->full_name, + pr_devel("hvc_vio_probe() device %pOF, using %s protocol\n", + vdev->dev.of_node, proto == HV_PROTOCOL_RAW ? "raw" : "hvsi"); /* Is it our boot one ? */ diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 79cc5beea2da..63c29fe9d21f 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -189,7 +189,7 @@ MODULE_VERSION(HVCS_DRIVER_VERSION); * that will cause echoing or we'll go into recursive loop echoing chars back * and forth with the console drivers. */ -static struct ktermios hvcs_tty_termios = { +static const struct ktermios hvcs_tty_termios = { .c_iflag = IGNBRK | IGNPAR, .c_oflag = OPOST, .c_cflag = B38400 | CS8 | CREAD | HUPCL, @@ -675,7 +675,7 @@ static int khvcsd(void *unused) return 0; } -static struct vio_device_id hvcs_driver_table[] = { +static const struct vio_device_id hvcs_driver_table[] = { {"serial-server", "hvterm2"}, { "", "" } }; diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index b70187b46d9d..61ecdd6b2fc2 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -150,7 +150,7 @@ static int isicom_probe(struct pci_dev *, const struct pci_device_id *); static void isicom_remove(struct pci_dev *); -static struct pci_device_id isicom_pci_tbl[] = { +static const struct pci_device_id isicom_pci_tbl[] = { { PCI_DEVICE(VENDOR_ID, 0x2028) }, { PCI_DEVICE(VENDOR_ID, 0x2051) }, { PCI_DEVICE(VENDOR_ID, 0x2052) }, diff --git a/drivers/tty/mips_ejtag_fdc.c b/drivers/tty/mips_ejtag_fdc.c index 234123b0c642..a2dab3fb8751 100644 --- a/drivers/tty/mips_ejtag_fdc.c +++ b/drivers/tty/mips_ejtag_fdc.c @@ -1110,7 +1110,7 @@ out: return ret; } -static struct mips_cdmm_device_id mips_ejtag_fdc_tty_ids[] = { +static const struct mips_cdmm_device_id mips_ejtag_fdc_tty_ids[] = { { .type = 0xfd }, { } }; diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 3b251f4e5df0..7f3d4cb0341b 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -88,7 +88,7 @@ static char *moxa_brdname[] = }; #ifdef CONFIG_PCI -static struct pci_device_id moxa_pcibrds[] = { +static const s |