18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2007 Google, Inc. 48c2ecf20Sopenharmony_ci * Copyright (C) 2012 Intel, Inc. 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Imagination Technologies Ltd. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/console.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 118c2ecf20Sopenharmony_ci#include <linux/tty.h> 128c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 178c2ecf20Sopenharmony_ci#include <linux/goldfish.h> 188c2ecf20Sopenharmony_ci#include <linux/mm.h> 198c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 208c2ecf20Sopenharmony_ci#include <linux/serial_core.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* Goldfish tty register's offsets */ 238c2ecf20Sopenharmony_ci#define GOLDFISH_TTY_REG_BYTES_READY 0x04 248c2ecf20Sopenharmony_ci#define GOLDFISH_TTY_REG_CMD 0x08 258c2ecf20Sopenharmony_ci#define GOLDFISH_TTY_REG_DATA_PTR 0x10 268c2ecf20Sopenharmony_ci#define GOLDFISH_TTY_REG_DATA_LEN 0x14 278c2ecf20Sopenharmony_ci#define GOLDFISH_TTY_REG_DATA_PTR_HIGH 0x18 288c2ecf20Sopenharmony_ci#define GOLDFISH_TTY_REG_VERSION 0x20 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* Goldfish tty commands */ 318c2ecf20Sopenharmony_ci#define GOLDFISH_TTY_CMD_INT_DISABLE 0 328c2ecf20Sopenharmony_ci#define GOLDFISH_TTY_CMD_INT_ENABLE 1 338c2ecf20Sopenharmony_ci#define GOLDFISH_TTY_CMD_WRITE_BUFFER 2 348c2ecf20Sopenharmony_ci#define GOLDFISH_TTY_CMD_READ_BUFFER 3 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct goldfish_tty { 378c2ecf20Sopenharmony_ci struct tty_port port; 388c2ecf20Sopenharmony_ci spinlock_t lock; 398c2ecf20Sopenharmony_ci void __iomem *base; 408c2ecf20Sopenharmony_ci u32 irq; 418c2ecf20Sopenharmony_ci int opencount; 428c2ecf20Sopenharmony_ci struct console console; 438c2ecf20Sopenharmony_ci u32 version; 448c2ecf20Sopenharmony_ci struct device *dev; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(goldfish_tty_lock); 488c2ecf20Sopenharmony_cistatic struct tty_driver *goldfish_tty_driver; 498c2ecf20Sopenharmony_cistatic u32 goldfish_tty_line_count = 8; 508c2ecf20Sopenharmony_cistatic u32 goldfish_tty_current_line_count; 518c2ecf20Sopenharmony_cistatic struct goldfish_tty *goldfish_ttys; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void do_rw_io(struct goldfish_tty *qtty, 548c2ecf20Sopenharmony_ci unsigned long address, 558c2ecf20Sopenharmony_ci unsigned int count, 568c2ecf20Sopenharmony_ci int is_write) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci unsigned long irq_flags; 598c2ecf20Sopenharmony_ci void __iomem *base = qtty->base; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci spin_lock_irqsave(&qtty->lock, irq_flags); 628c2ecf20Sopenharmony_ci gf_write_ptr((void *)address, base + GOLDFISH_TTY_REG_DATA_PTR, 638c2ecf20Sopenharmony_ci base + GOLDFISH_TTY_REG_DATA_PTR_HIGH); 648c2ecf20Sopenharmony_ci writel(count, base + GOLDFISH_TTY_REG_DATA_LEN); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (is_write) 678c2ecf20Sopenharmony_ci writel(GOLDFISH_TTY_CMD_WRITE_BUFFER, 688c2ecf20Sopenharmony_ci base + GOLDFISH_TTY_REG_CMD); 698c2ecf20Sopenharmony_ci else 708c2ecf20Sopenharmony_ci writel(GOLDFISH_TTY_CMD_READ_BUFFER, 718c2ecf20Sopenharmony_ci base + GOLDFISH_TTY_REG_CMD); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&qtty->lock, irq_flags); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void goldfish_tty_rw(struct goldfish_tty *qtty, 778c2ecf20Sopenharmony_ci unsigned long addr, 788c2ecf20Sopenharmony_ci unsigned int count, 798c2ecf20Sopenharmony_ci int is_write) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci dma_addr_t dma_handle; 828c2ecf20Sopenharmony_ci enum dma_data_direction dma_dir; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci dma_dir = (is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); 858c2ecf20Sopenharmony_ci if (qtty->version > 0) { 868c2ecf20Sopenharmony_ci /* 878c2ecf20Sopenharmony_ci * Goldfish TTY for Ranchu platform uses 888c2ecf20Sopenharmony_ci * physical addresses and DMA for read/write operations 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci unsigned long addr_end = addr + count; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci while (addr < addr_end) { 938c2ecf20Sopenharmony_ci unsigned long pg_end = (addr & PAGE_MASK) + PAGE_SIZE; 948c2ecf20Sopenharmony_ci unsigned long next = 958c2ecf20Sopenharmony_ci pg_end < addr_end ? pg_end : addr_end; 968c2ecf20Sopenharmony_ci unsigned long avail = next - addr; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* 998c2ecf20Sopenharmony_ci * Map the buffer's virtual address to the DMA address 1008c2ecf20Sopenharmony_ci * so the buffer can be accessed by the device. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci dma_handle = dma_map_single(qtty->dev, (void *)addr, 1038c2ecf20Sopenharmony_ci avail, dma_dir); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (dma_mapping_error(qtty->dev, dma_handle)) { 1068c2ecf20Sopenharmony_ci dev_err(qtty->dev, "tty: DMA mapping error.\n"); 1078c2ecf20Sopenharmony_ci return; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci do_rw_io(qtty, dma_handle, avail, is_write); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* 1128c2ecf20Sopenharmony_ci * Unmap the previously mapped region after 1138c2ecf20Sopenharmony_ci * the completion of the read/write operation. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci dma_unmap_single(qtty->dev, dma_handle, avail, dma_dir); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci addr += avail; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci } else { 1208c2ecf20Sopenharmony_ci /* 1218c2ecf20Sopenharmony_ci * Old style Goldfish TTY used on the Goldfish platform 1228c2ecf20Sopenharmony_ci * uses virtual addresses. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci do_rw_io(qtty, addr, count, is_write); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void goldfish_tty_do_write(int line, const char *buf, 1298c2ecf20Sopenharmony_ci unsigned int count) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct goldfish_tty *qtty = &goldfish_ttys[line]; 1328c2ecf20Sopenharmony_ci unsigned long address = (unsigned long)(void *)buf; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci goldfish_tty_rw(qtty, address, count, 1); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct goldfish_tty *qtty = dev_id; 1408c2ecf20Sopenharmony_ci void __iomem *base = qtty->base; 1418c2ecf20Sopenharmony_ci unsigned long address; 1428c2ecf20Sopenharmony_ci unsigned char *buf; 1438c2ecf20Sopenharmony_ci u32 count; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci count = readl(base + GOLDFISH_TTY_REG_BYTES_READY); 1468c2ecf20Sopenharmony_ci if (count == 0) 1478c2ecf20Sopenharmony_ci return IRQ_NONE; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci count = tty_prepare_flip_string(&qtty->port, &buf, count); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci address = (unsigned long)(void *)buf; 1528c2ecf20Sopenharmony_ci goldfish_tty_rw(qtty, address, count, 0); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci tty_flip_buffer_push(&qtty->port); 1558c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int goldfish_tty_activate(struct tty_port *port, struct tty_struct *tty) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, 1618c2ecf20Sopenharmony_ci port); 1628c2ecf20Sopenharmony_ci writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_REG_CMD); 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void goldfish_tty_shutdown(struct tty_port *port) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, 1698c2ecf20Sopenharmony_ci port); 1708c2ecf20Sopenharmony_ci writel(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_REG_CMD); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int goldfish_tty_open(struct tty_struct *tty, struct file *filp) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct goldfish_tty *qtty = &goldfish_ttys[tty->index]; 1768c2ecf20Sopenharmony_ci return tty_port_open(&qtty->port, tty, filp); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void goldfish_tty_close(struct tty_struct *tty, struct file *filp) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci tty_port_close(tty->port, tty, filp); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void goldfish_tty_hangup(struct tty_struct *tty) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci tty_port_hangup(tty->port); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int goldfish_tty_write(struct tty_struct *tty, const unsigned char *buf, 1908c2ecf20Sopenharmony_ci int count) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci goldfish_tty_do_write(tty->index, buf, count); 1938c2ecf20Sopenharmony_ci return count; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int goldfish_tty_write_room(struct tty_struct *tty) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci return 0x10000; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int goldfish_tty_chars_in_buffer(struct tty_struct *tty) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct goldfish_tty *qtty = &goldfish_ttys[tty->index]; 2048c2ecf20Sopenharmony_ci void __iomem *base = qtty->base; 2058c2ecf20Sopenharmony_ci return readl(base + GOLDFISH_TTY_REG_BYTES_READY); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic void goldfish_tty_console_write(struct console *co, const char *b, 2098c2ecf20Sopenharmony_ci unsigned count) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci goldfish_tty_do_write(co->index, b, count); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic struct tty_driver *goldfish_tty_console_device(struct console *c, 2158c2ecf20Sopenharmony_ci int *index) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci *index = c->index; 2188c2ecf20Sopenharmony_ci return goldfish_tty_driver; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic int goldfish_tty_console_setup(struct console *co, char *options) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci if ((unsigned)co->index >= goldfish_tty_line_count) 2248c2ecf20Sopenharmony_ci return -ENODEV; 2258c2ecf20Sopenharmony_ci if (!goldfish_ttys[co->index].base) 2268c2ecf20Sopenharmony_ci return -ENODEV; 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic const struct tty_port_operations goldfish_port_ops = { 2318c2ecf20Sopenharmony_ci .activate = goldfish_tty_activate, 2328c2ecf20Sopenharmony_ci .shutdown = goldfish_tty_shutdown 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic const struct tty_operations goldfish_tty_ops = { 2368c2ecf20Sopenharmony_ci .open = goldfish_tty_open, 2378c2ecf20Sopenharmony_ci .close = goldfish_tty_close, 2388c2ecf20Sopenharmony_ci .hangup = goldfish_tty_hangup, 2398c2ecf20Sopenharmony_ci .write = goldfish_tty_write, 2408c2ecf20Sopenharmony_ci .write_room = goldfish_tty_write_room, 2418c2ecf20Sopenharmony_ci .chars_in_buffer = goldfish_tty_chars_in_buffer, 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int goldfish_tty_create_driver(void) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci int ret; 2478c2ecf20Sopenharmony_ci struct tty_driver *tty; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci goldfish_ttys = kcalloc(goldfish_tty_line_count, 2508c2ecf20Sopenharmony_ci sizeof(*goldfish_ttys), 2518c2ecf20Sopenharmony_ci GFP_KERNEL); 2528c2ecf20Sopenharmony_ci if (goldfish_ttys == NULL) { 2538c2ecf20Sopenharmony_ci ret = -ENOMEM; 2548c2ecf20Sopenharmony_ci goto err_alloc_goldfish_ttys_failed; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci tty = alloc_tty_driver(goldfish_tty_line_count); 2578c2ecf20Sopenharmony_ci if (tty == NULL) { 2588c2ecf20Sopenharmony_ci ret = -ENOMEM; 2598c2ecf20Sopenharmony_ci goto err_alloc_tty_driver_failed; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci tty->driver_name = "goldfish"; 2628c2ecf20Sopenharmony_ci tty->name = "ttyGF"; 2638c2ecf20Sopenharmony_ci tty->type = TTY_DRIVER_TYPE_SERIAL; 2648c2ecf20Sopenharmony_ci tty->subtype = SERIAL_TYPE_NORMAL; 2658c2ecf20Sopenharmony_ci tty->init_termios = tty_std_termios; 2668c2ecf20Sopenharmony_ci tty->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | 2678c2ecf20Sopenharmony_ci TTY_DRIVER_DYNAMIC_DEV; 2688c2ecf20Sopenharmony_ci tty_set_operations(tty, &goldfish_tty_ops); 2698c2ecf20Sopenharmony_ci ret = tty_register_driver(tty); 2708c2ecf20Sopenharmony_ci if (ret) 2718c2ecf20Sopenharmony_ci goto err_tty_register_driver_failed; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci goldfish_tty_driver = tty; 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cierr_tty_register_driver_failed: 2778c2ecf20Sopenharmony_ci put_tty_driver(tty); 2788c2ecf20Sopenharmony_cierr_alloc_tty_driver_failed: 2798c2ecf20Sopenharmony_ci kfree(goldfish_ttys); 2808c2ecf20Sopenharmony_ci goldfish_ttys = NULL; 2818c2ecf20Sopenharmony_cierr_alloc_goldfish_ttys_failed: 2828c2ecf20Sopenharmony_ci return ret; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic void goldfish_tty_delete_driver(void) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci tty_unregister_driver(goldfish_tty_driver); 2888c2ecf20Sopenharmony_ci put_tty_driver(goldfish_tty_driver); 2898c2ecf20Sopenharmony_ci goldfish_tty_driver = NULL; 2908c2ecf20Sopenharmony_ci kfree(goldfish_ttys); 2918c2ecf20Sopenharmony_ci goldfish_ttys = NULL; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int goldfish_tty_probe(struct platform_device *pdev) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct goldfish_tty *qtty; 2978c2ecf20Sopenharmony_ci int ret = -ENODEV; 2988c2ecf20Sopenharmony_ci struct resource *r; 2998c2ecf20Sopenharmony_ci struct device *ttydev; 3008c2ecf20Sopenharmony_ci void __iomem *base; 3018c2ecf20Sopenharmony_ci u32 irq; 3028c2ecf20Sopenharmony_ci unsigned int line; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3058c2ecf20Sopenharmony_ci if (!r) { 3068c2ecf20Sopenharmony_ci pr_err("goldfish_tty: No MEM resource available!\n"); 3078c2ecf20Sopenharmony_ci return -ENOMEM; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci base = ioremap(r->start, 0x1000); 3118c2ecf20Sopenharmony_ci if (!base) { 3128c2ecf20Sopenharmony_ci pr_err("goldfish_tty: Unable to ioremap base!\n"); 3138c2ecf20Sopenharmony_ci return -ENOMEM; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 3178c2ecf20Sopenharmony_ci if (!r) { 3188c2ecf20Sopenharmony_ci pr_err("goldfish_tty: No IRQ resource available!\n"); 3198c2ecf20Sopenharmony_ci goto err_unmap; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci irq = r->start; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci mutex_lock(&goldfish_tty_lock); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (pdev->id == PLATFORM_DEVID_NONE) 3278c2ecf20Sopenharmony_ci line = goldfish_tty_current_line_count; 3288c2ecf20Sopenharmony_ci else 3298c2ecf20Sopenharmony_ci line = pdev->id; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (line >= goldfish_tty_line_count) { 3328c2ecf20Sopenharmony_ci pr_err("goldfish_tty: Reached maximum tty number of %d.\n", 3338c2ecf20Sopenharmony_ci goldfish_tty_current_line_count); 3348c2ecf20Sopenharmony_ci ret = -ENOMEM; 3358c2ecf20Sopenharmony_ci goto err_unlock; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (goldfish_tty_current_line_count == 0) { 3398c2ecf20Sopenharmony_ci ret = goldfish_tty_create_driver(); 3408c2ecf20Sopenharmony_ci if (ret) 3418c2ecf20Sopenharmony_ci goto err_unlock; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci goldfish_tty_current_line_count++; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci qtty = &goldfish_ttys[line]; 3468c2ecf20Sopenharmony_ci spin_lock_init(&qtty->lock); 3478c2ecf20Sopenharmony_ci tty_port_init(&qtty->port); 3488c2ecf20Sopenharmony_ci qtty->port.ops = &goldfish_port_ops; 3498c2ecf20Sopenharmony_ci qtty->base = base; 3508c2ecf20Sopenharmony_ci qtty->irq = irq; 3518c2ecf20Sopenharmony_ci qtty->dev = &pdev->dev; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* 3548c2ecf20Sopenharmony_ci * Goldfish TTY device used by the Goldfish emulator 3558c2ecf20Sopenharmony_ci * should identify itself with 0, forcing the driver 3568c2ecf20Sopenharmony_ci * to use virtual addresses. Goldfish TTY device 3578c2ecf20Sopenharmony_ci * on Ranchu emulator (qemu2) returns 1 here and 3588c2ecf20Sopenharmony_ci * driver will use physical addresses. 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_ci qtty->version = readl(base + GOLDFISH_TTY_REG_VERSION); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* 3638c2ecf20Sopenharmony_ci * Goldfish TTY device on Ranchu emulator (qemu2) 3648c2ecf20Sopenharmony_ci * will use DMA for read/write IO operations. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_ci if (qtty->version > 0) { 3678c2ecf20Sopenharmony_ci /* 3688c2ecf20Sopenharmony_ci * Initialize dma_mask to 32-bits. 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci if (!pdev->dev.dma_mask) 3718c2ecf20Sopenharmony_ci pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 3728c2ecf20Sopenharmony_ci ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); 3738c2ecf20Sopenharmony_ci if (ret) { 3748c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No suitable DMA available.\n"); 3758c2ecf20Sopenharmony_ci goto err_dec_line_count; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_REG_CMD); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED, 3828c2ecf20Sopenharmony_ci "goldfish_tty", qtty); 3838c2ecf20Sopenharmony_ci if (ret) { 3848c2ecf20Sopenharmony_ci pr_err("goldfish_tty: No IRQ available!\n"); 3858c2ecf20Sopenharmony_ci goto err_dec_line_count; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver, 3898c2ecf20Sopenharmony_ci line, &pdev->dev); 3908c2ecf20Sopenharmony_ci if (IS_ERR(ttydev)) { 3918c2ecf20Sopenharmony_ci ret = PTR_ERR(ttydev); 3928c2ecf20Sopenharmony_ci goto err_tty_register_device_failed; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci strcpy(qtty->console.name, "ttyGF"); 3968c2ecf20Sopenharmony_ci qtty->console.write = goldfish_tty_console_write; 3978c2ecf20Sopenharmony_ci qtty->console.device = goldfish_tty_console_device; 3988c2ecf20Sopenharmony_ci qtty->console.setup = goldfish_tty_console_setup; 3998c2ecf20Sopenharmony_ci qtty->console.flags = CON_PRINTBUFFER; 4008c2ecf20Sopenharmony_ci qtty->console.index = line; 4018c2ecf20Sopenharmony_ci register_console(&qtty->console); 4028c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, qtty); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci mutex_unlock(&goldfish_tty_lock); 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cierr_tty_register_device_failed: 4088c2ecf20Sopenharmony_ci free_irq(irq, qtty); 4098c2ecf20Sopenharmony_cierr_dec_line_count: 4108c2ecf20Sopenharmony_ci tty_port_destroy(&qtty->port); 4118c2ecf20Sopenharmony_ci goldfish_tty_current_line_count--; 4128c2ecf20Sopenharmony_ci if (goldfish_tty_current_line_count == 0) 4138c2ecf20Sopenharmony_ci goldfish_tty_delete_driver(); 4148c2ecf20Sopenharmony_cierr_unlock: 4158c2ecf20Sopenharmony_ci mutex_unlock(&goldfish_tty_lock); 4168c2ecf20Sopenharmony_cierr_unmap: 4178c2ecf20Sopenharmony_ci iounmap(base); 4188c2ecf20Sopenharmony_ci return ret; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic int goldfish_tty_remove(struct platform_device *pdev) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct goldfish_tty *qtty = platform_get_drvdata(pdev); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci mutex_lock(&goldfish_tty_lock); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci unregister_console(&qtty->console); 4288c2ecf20Sopenharmony_ci tty_unregister_device(goldfish_tty_driver, qtty->console.index); 4298c2ecf20Sopenharmony_ci iounmap(qtty->base); 4308c2ecf20Sopenharmony_ci qtty->base = NULL; 4318c2ecf20Sopenharmony_ci free_irq(qtty->irq, qtty); 4328c2ecf20Sopenharmony_ci tty_port_destroy(&qtty->port); 4338c2ecf20Sopenharmony_ci goldfish_tty_current_line_count--; 4348c2ecf20Sopenharmony_ci if (goldfish_tty_current_line_count == 0) 4358c2ecf20Sopenharmony_ci goldfish_tty_delete_driver(); 4368c2ecf20Sopenharmony_ci mutex_unlock(&goldfish_tty_lock); 4378c2ecf20Sopenharmony_ci return 0; 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci#ifdef CONFIG_GOLDFISH_TTY_EARLY_CONSOLE 4418c2ecf20Sopenharmony_cistatic void gf_early_console_putchar(struct uart_port *port, int ch) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci __raw_writel(ch, port->membase); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic void gf_early_write(struct console *con, const char *s, unsigned int n) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct earlycon_device *dev = con->data; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci uart_console_write(&dev->port, s, n, gf_early_console_putchar); 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic int __init gf_earlycon_setup(struct earlycon_device *device, 4548c2ecf20Sopenharmony_ci const char *opt) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci if (!device->port.membase) 4578c2ecf20Sopenharmony_ci return -ENODEV; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci device->con->write = gf_early_write; 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ciOF_EARLYCON_DECLARE(early_gf_tty, "google,goldfish-tty", gf_earlycon_setup); 4648c2ecf20Sopenharmony_ci#endif 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic const struct of_device_id goldfish_tty_of_match[] = { 4678c2ecf20Sopenharmony_ci { .compatible = "google,goldfish-tty", }, 4688c2ecf20Sopenharmony_ci {}, 4698c2ecf20Sopenharmony_ci}; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, goldfish_tty_of_match); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic struct platform_driver goldfish_tty_platform_driver = { 4748c2ecf20Sopenharmony_ci .probe = goldfish_tty_probe, 4758c2ecf20Sopenharmony_ci .remove = goldfish_tty_remove, 4768c2ecf20Sopenharmony_ci .driver = { 4778c2ecf20Sopenharmony_ci .name = "goldfish_tty", 4788c2ecf20Sopenharmony_ci .of_match_table = goldfish_tty_of_match, 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci}; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cimodule_platform_driver(goldfish_tty_platform_driver); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 485