162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2007 Google, Inc. 462306a36Sopenharmony_ci * Copyright (C) 2012 Intel, Inc. 562306a36Sopenharmony_ci * Copyright (C) 2017 Imagination Technologies Ltd. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/console.h> 962306a36Sopenharmony_ci#include <linux/interrupt.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/tty.h> 1262306a36Sopenharmony_ci#include <linux/tty_flip.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1762306a36Sopenharmony_ci#include <linux/goldfish.h> 1862306a36Sopenharmony_ci#include <linux/mm.h> 1962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2062306a36Sopenharmony_ci#include <linux/serial_core.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Goldfish tty register's offsets */ 2362306a36Sopenharmony_ci#define GOLDFISH_TTY_REG_BYTES_READY 0x04 2462306a36Sopenharmony_ci#define GOLDFISH_TTY_REG_CMD 0x08 2562306a36Sopenharmony_ci#define GOLDFISH_TTY_REG_DATA_PTR 0x10 2662306a36Sopenharmony_ci#define GOLDFISH_TTY_REG_DATA_LEN 0x14 2762306a36Sopenharmony_ci#define GOLDFISH_TTY_REG_DATA_PTR_HIGH 0x18 2862306a36Sopenharmony_ci#define GOLDFISH_TTY_REG_VERSION 0x20 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* Goldfish tty commands */ 3162306a36Sopenharmony_ci#define GOLDFISH_TTY_CMD_INT_DISABLE 0 3262306a36Sopenharmony_ci#define GOLDFISH_TTY_CMD_INT_ENABLE 1 3362306a36Sopenharmony_ci#define GOLDFISH_TTY_CMD_WRITE_BUFFER 2 3462306a36Sopenharmony_ci#define GOLDFISH_TTY_CMD_READ_BUFFER 3 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct goldfish_tty { 3762306a36Sopenharmony_ci struct tty_port port; 3862306a36Sopenharmony_ci spinlock_t lock; 3962306a36Sopenharmony_ci void __iomem *base; 4062306a36Sopenharmony_ci u32 irq; 4162306a36Sopenharmony_ci int opencount; 4262306a36Sopenharmony_ci struct console console; 4362306a36Sopenharmony_ci u32 version; 4462306a36Sopenharmony_ci struct device *dev; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic DEFINE_MUTEX(goldfish_tty_lock); 4862306a36Sopenharmony_cistatic struct tty_driver *goldfish_tty_driver; 4962306a36Sopenharmony_cistatic u32 goldfish_tty_line_count = 8; 5062306a36Sopenharmony_cistatic u32 goldfish_tty_current_line_count; 5162306a36Sopenharmony_cistatic struct goldfish_tty *goldfish_ttys; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void do_rw_io(struct goldfish_tty *qtty, 5462306a36Sopenharmony_ci unsigned long address, 5562306a36Sopenharmony_ci unsigned int count, 5662306a36Sopenharmony_ci int is_write) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci unsigned long irq_flags; 5962306a36Sopenharmony_ci void __iomem *base = qtty->base; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci spin_lock_irqsave(&qtty->lock, irq_flags); 6262306a36Sopenharmony_ci gf_write_ptr((void *)address, base + GOLDFISH_TTY_REG_DATA_PTR, 6362306a36Sopenharmony_ci base + GOLDFISH_TTY_REG_DATA_PTR_HIGH); 6462306a36Sopenharmony_ci gf_iowrite32(count, base + GOLDFISH_TTY_REG_DATA_LEN); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (is_write) 6762306a36Sopenharmony_ci gf_iowrite32(GOLDFISH_TTY_CMD_WRITE_BUFFER, 6862306a36Sopenharmony_ci base + GOLDFISH_TTY_REG_CMD); 6962306a36Sopenharmony_ci else 7062306a36Sopenharmony_ci gf_iowrite32(GOLDFISH_TTY_CMD_READ_BUFFER, 7162306a36Sopenharmony_ci base + GOLDFISH_TTY_REG_CMD); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci spin_unlock_irqrestore(&qtty->lock, irq_flags); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void goldfish_tty_rw(struct goldfish_tty *qtty, 7762306a36Sopenharmony_ci unsigned long addr, 7862306a36Sopenharmony_ci unsigned int count, 7962306a36Sopenharmony_ci int is_write) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci dma_addr_t dma_handle; 8262306a36Sopenharmony_ci enum dma_data_direction dma_dir; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci dma_dir = (is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); 8562306a36Sopenharmony_ci if (qtty->version > 0) { 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * Goldfish TTY for Ranchu platform uses 8862306a36Sopenharmony_ci * physical addresses and DMA for read/write operations 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci unsigned long addr_end = addr + count; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci while (addr < addr_end) { 9362306a36Sopenharmony_ci unsigned long pg_end = (addr & PAGE_MASK) + PAGE_SIZE; 9462306a36Sopenharmony_ci unsigned long next = 9562306a36Sopenharmony_ci pg_end < addr_end ? pg_end : addr_end; 9662306a36Sopenharmony_ci unsigned long avail = next - addr; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* 9962306a36Sopenharmony_ci * Map the buffer's virtual address to the DMA address 10062306a36Sopenharmony_ci * so the buffer can be accessed by the device. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci dma_handle = dma_map_single(qtty->dev, (void *)addr, 10362306a36Sopenharmony_ci avail, dma_dir); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (dma_mapping_error(qtty->dev, dma_handle)) { 10662306a36Sopenharmony_ci dev_err(qtty->dev, "tty: DMA mapping error.\n"); 10762306a36Sopenharmony_ci return; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci do_rw_io(qtty, dma_handle, avail, is_write); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* 11262306a36Sopenharmony_ci * Unmap the previously mapped region after 11362306a36Sopenharmony_ci * the completion of the read/write operation. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci dma_unmap_single(qtty->dev, dma_handle, avail, dma_dir); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci addr += avail; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci } else { 12062306a36Sopenharmony_ci /* 12162306a36Sopenharmony_ci * Old style Goldfish TTY used on the Goldfish platform 12262306a36Sopenharmony_ci * uses virtual addresses. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci do_rw_io(qtty, addr, count, is_write); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void goldfish_tty_do_write(int line, const u8 *buf, unsigned int count) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct goldfish_tty *qtty = &goldfish_ttys[line]; 13162306a36Sopenharmony_ci unsigned long address = (unsigned long)(void *)buf; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci goldfish_tty_rw(qtty, address, count, 1); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct goldfish_tty *qtty = dev_id; 13962306a36Sopenharmony_ci void __iomem *base = qtty->base; 14062306a36Sopenharmony_ci unsigned long address; 14162306a36Sopenharmony_ci unsigned char *buf; 14262306a36Sopenharmony_ci u32 count; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci count = gf_ioread32(base + GOLDFISH_TTY_REG_BYTES_READY); 14562306a36Sopenharmony_ci if (count == 0) 14662306a36Sopenharmony_ci return IRQ_NONE; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci count = tty_prepare_flip_string(&qtty->port, &buf, count); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci address = (unsigned long)(void *)buf; 15162306a36Sopenharmony_ci goldfish_tty_rw(qtty, address, count, 0); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci tty_flip_buffer_push(&qtty->port); 15462306a36Sopenharmony_ci return IRQ_HANDLED; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int goldfish_tty_activate(struct tty_port *port, struct tty_struct *tty) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, 16062306a36Sopenharmony_ci port); 16162306a36Sopenharmony_ci gf_iowrite32(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_REG_CMD); 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void goldfish_tty_shutdown(struct tty_port *port) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, 16862306a36Sopenharmony_ci port); 16962306a36Sopenharmony_ci gf_iowrite32(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_REG_CMD); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int goldfish_tty_open(struct tty_struct *tty, struct file *filp) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct goldfish_tty *qtty = &goldfish_ttys[tty->index]; 17562306a36Sopenharmony_ci return tty_port_open(&qtty->port, tty, filp); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void goldfish_tty_close(struct tty_struct *tty, struct file *filp) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci tty_port_close(tty->port, tty, filp); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void goldfish_tty_hangup(struct tty_struct *tty) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci tty_port_hangup(tty->port); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic ssize_t goldfish_tty_write(struct tty_struct *tty, const u8 *buf, 18962306a36Sopenharmony_ci size_t count) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci goldfish_tty_do_write(tty->index, buf, count); 19262306a36Sopenharmony_ci return count; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic unsigned int goldfish_tty_write_room(struct tty_struct *tty) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci return 0x10000; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic unsigned int goldfish_tty_chars_in_buffer(struct tty_struct *tty) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct goldfish_tty *qtty = &goldfish_ttys[tty->index]; 20362306a36Sopenharmony_ci void __iomem *base = qtty->base; 20462306a36Sopenharmony_ci return gf_ioread32(base + GOLDFISH_TTY_REG_BYTES_READY); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic void goldfish_tty_console_write(struct console *co, const char *b, 20862306a36Sopenharmony_ci unsigned count) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci goldfish_tty_do_write(co->index, b, count); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic struct tty_driver *goldfish_tty_console_device(struct console *c, 21462306a36Sopenharmony_ci int *index) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci *index = c->index; 21762306a36Sopenharmony_ci return goldfish_tty_driver; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int goldfish_tty_console_setup(struct console *co, char *options) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci if ((unsigned)co->index >= goldfish_tty_line_count) 22362306a36Sopenharmony_ci return -ENODEV; 22462306a36Sopenharmony_ci if (!goldfish_ttys[co->index].base) 22562306a36Sopenharmony_ci return -ENODEV; 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic const struct tty_port_operations goldfish_port_ops = { 23062306a36Sopenharmony_ci .activate = goldfish_tty_activate, 23162306a36Sopenharmony_ci .shutdown = goldfish_tty_shutdown 23262306a36Sopenharmony_ci}; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic const struct tty_operations goldfish_tty_ops = { 23562306a36Sopenharmony_ci .open = goldfish_tty_open, 23662306a36Sopenharmony_ci .close = goldfish_tty_close, 23762306a36Sopenharmony_ci .hangup = goldfish_tty_hangup, 23862306a36Sopenharmony_ci .write = goldfish_tty_write, 23962306a36Sopenharmony_ci .write_room = goldfish_tty_write_room, 24062306a36Sopenharmony_ci .chars_in_buffer = goldfish_tty_chars_in_buffer, 24162306a36Sopenharmony_ci}; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int goldfish_tty_create_driver(void) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci int ret; 24662306a36Sopenharmony_ci struct tty_driver *tty; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci goldfish_ttys = kcalloc(goldfish_tty_line_count, 24962306a36Sopenharmony_ci sizeof(*goldfish_ttys), 25062306a36Sopenharmony_ci GFP_KERNEL); 25162306a36Sopenharmony_ci if (goldfish_ttys == NULL) { 25262306a36Sopenharmony_ci ret = -ENOMEM; 25362306a36Sopenharmony_ci goto err_alloc_goldfish_ttys_failed; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci tty = tty_alloc_driver(goldfish_tty_line_count, 25662306a36Sopenharmony_ci TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | 25762306a36Sopenharmony_ci TTY_DRIVER_DYNAMIC_DEV); 25862306a36Sopenharmony_ci if (IS_ERR(tty)) { 25962306a36Sopenharmony_ci ret = PTR_ERR(tty); 26062306a36Sopenharmony_ci goto err_tty_alloc_driver_failed; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci tty->driver_name = "goldfish"; 26362306a36Sopenharmony_ci tty->name = "ttyGF"; 26462306a36Sopenharmony_ci tty->type = TTY_DRIVER_TYPE_SERIAL; 26562306a36Sopenharmony_ci tty->subtype = SERIAL_TYPE_NORMAL; 26662306a36Sopenharmony_ci tty->init_termios = tty_std_termios; 26762306a36Sopenharmony_ci tty_set_operations(tty, &goldfish_tty_ops); 26862306a36Sopenharmony_ci ret = tty_register_driver(tty); 26962306a36Sopenharmony_ci if (ret) 27062306a36Sopenharmony_ci goto err_tty_register_driver_failed; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci goldfish_tty_driver = tty; 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cierr_tty_register_driver_failed: 27662306a36Sopenharmony_ci tty_driver_kref_put(tty); 27762306a36Sopenharmony_cierr_tty_alloc_driver_failed: 27862306a36Sopenharmony_ci kfree(goldfish_ttys); 27962306a36Sopenharmony_ci goldfish_ttys = NULL; 28062306a36Sopenharmony_cierr_alloc_goldfish_ttys_failed: 28162306a36Sopenharmony_ci return ret; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void goldfish_tty_delete_driver(void) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci tty_unregister_driver(goldfish_tty_driver); 28762306a36Sopenharmony_ci tty_driver_kref_put(goldfish_tty_driver); 28862306a36Sopenharmony_ci goldfish_tty_driver = NULL; 28962306a36Sopenharmony_ci kfree(goldfish_ttys); 29062306a36Sopenharmony_ci goldfish_ttys = NULL; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int goldfish_tty_probe(struct platform_device *pdev) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct goldfish_tty *qtty; 29662306a36Sopenharmony_ci int ret = -ENODEV; 29762306a36Sopenharmony_ci struct resource *r; 29862306a36Sopenharmony_ci struct device *ttydev; 29962306a36Sopenharmony_ci void __iomem *base; 30062306a36Sopenharmony_ci int irq; 30162306a36Sopenharmony_ci unsigned int line; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 30462306a36Sopenharmony_ci if (!r) { 30562306a36Sopenharmony_ci pr_err("goldfish_tty: No MEM resource available!\n"); 30662306a36Sopenharmony_ci return -ENOMEM; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci base = ioremap(r->start, 0x1000); 31062306a36Sopenharmony_ci if (!base) { 31162306a36Sopenharmony_ci pr_err("goldfish_tty: Unable to ioremap base!\n"); 31262306a36Sopenharmony_ci return -ENOMEM; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 31662306a36Sopenharmony_ci if (irq < 0) { 31762306a36Sopenharmony_ci ret = irq; 31862306a36Sopenharmony_ci goto err_unmap; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci mutex_lock(&goldfish_tty_lock); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (pdev->id == PLATFORM_DEVID_NONE) 32462306a36Sopenharmony_ci line = goldfish_tty_current_line_count; 32562306a36Sopenharmony_ci else 32662306a36Sopenharmony_ci line = pdev->id; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (line >= goldfish_tty_line_count) { 32962306a36Sopenharmony_ci pr_err("goldfish_tty: Reached maximum tty number of %d.\n", 33062306a36Sopenharmony_ci goldfish_tty_current_line_count); 33162306a36Sopenharmony_ci ret = -ENOMEM; 33262306a36Sopenharmony_ci goto err_unlock; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (goldfish_tty_current_line_count == 0) { 33662306a36Sopenharmony_ci ret = goldfish_tty_create_driver(); 33762306a36Sopenharmony_ci if (ret) 33862306a36Sopenharmony_ci goto err_unlock; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci goldfish_tty_current_line_count++; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci qtty = &goldfish_ttys[line]; 34362306a36Sopenharmony_ci spin_lock_init(&qtty->lock); 34462306a36Sopenharmony_ci tty_port_init(&qtty->port); 34562306a36Sopenharmony_ci qtty->port.ops = &goldfish_port_ops; 34662306a36Sopenharmony_ci qtty->base = base; 34762306a36Sopenharmony_ci qtty->irq = irq; 34862306a36Sopenharmony_ci qtty->dev = &pdev->dev; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* 35162306a36Sopenharmony_ci * Goldfish TTY device used by the Goldfish emulator 35262306a36Sopenharmony_ci * should identify itself with 0, forcing the driver 35362306a36Sopenharmony_ci * to use virtual addresses. Goldfish TTY device 35462306a36Sopenharmony_ci * on Ranchu emulator (qemu2) returns 1 here and 35562306a36Sopenharmony_ci * driver will use physical addresses. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_ci qtty->version = gf_ioread32(base + GOLDFISH_TTY_REG_VERSION); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* 36062306a36Sopenharmony_ci * Goldfish TTY device on Ranchu emulator (qemu2) 36162306a36Sopenharmony_ci * will use DMA for read/write IO operations. 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_ci if (qtty->version > 0) { 36462306a36Sopenharmony_ci /* 36562306a36Sopenharmony_ci * Initialize dma_mask to 32-bits. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci if (!pdev->dev.dma_mask) 36862306a36Sopenharmony_ci pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 36962306a36Sopenharmony_ci ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); 37062306a36Sopenharmony_ci if (ret) { 37162306a36Sopenharmony_ci dev_err(&pdev->dev, "No suitable DMA available.\n"); 37262306a36Sopenharmony_ci goto err_dec_line_count; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci gf_iowrite32(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_REG_CMD); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED, 37962306a36Sopenharmony_ci "goldfish_tty", qtty); 38062306a36Sopenharmony_ci if (ret) { 38162306a36Sopenharmony_ci pr_err("goldfish_tty: No IRQ available!\n"); 38262306a36Sopenharmony_ci goto err_dec_line_count; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver, 38662306a36Sopenharmony_ci line, &pdev->dev); 38762306a36Sopenharmony_ci if (IS_ERR(ttydev)) { 38862306a36Sopenharmony_ci ret = PTR_ERR(ttydev); 38962306a36Sopenharmony_ci goto err_tty_register_device_failed; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci strcpy(qtty->console.name, "ttyGF"); 39362306a36Sopenharmony_ci qtty->console.write = goldfish_tty_console_write; 39462306a36Sopenharmony_ci qtty->console.device = goldfish_tty_console_device; 39562306a36Sopenharmony_ci qtty->console.setup = goldfish_tty_console_setup; 39662306a36Sopenharmony_ci qtty->console.flags = CON_PRINTBUFFER; 39762306a36Sopenharmony_ci qtty->console.index = line; 39862306a36Sopenharmony_ci register_console(&qtty->console); 39962306a36Sopenharmony_ci platform_set_drvdata(pdev, qtty); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci mutex_unlock(&goldfish_tty_lock); 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cierr_tty_register_device_failed: 40562306a36Sopenharmony_ci free_irq(irq, qtty); 40662306a36Sopenharmony_cierr_dec_line_count: 40762306a36Sopenharmony_ci tty_port_destroy(&qtty->port); 40862306a36Sopenharmony_ci goldfish_tty_current_line_count--; 40962306a36Sopenharmony_ci if (goldfish_tty_current_line_count == 0) 41062306a36Sopenharmony_ci goldfish_tty_delete_driver(); 41162306a36Sopenharmony_cierr_unlock: 41262306a36Sopenharmony_ci mutex_unlock(&goldfish_tty_lock); 41362306a36Sopenharmony_cierr_unmap: 41462306a36Sopenharmony_ci iounmap(base); 41562306a36Sopenharmony_ci return ret; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int goldfish_tty_remove(struct platform_device *pdev) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct goldfish_tty *qtty = platform_get_drvdata(pdev); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci mutex_lock(&goldfish_tty_lock); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci unregister_console(&qtty->console); 42562306a36Sopenharmony_ci tty_unregister_device(goldfish_tty_driver, qtty->console.index); 42662306a36Sopenharmony_ci iounmap(qtty->base); 42762306a36Sopenharmony_ci qtty->base = NULL; 42862306a36Sopenharmony_ci free_irq(qtty->irq, qtty); 42962306a36Sopenharmony_ci tty_port_destroy(&qtty->port); 43062306a36Sopenharmony_ci goldfish_tty_current_line_count--; 43162306a36Sopenharmony_ci if (goldfish_tty_current_line_count == 0) 43262306a36Sopenharmony_ci goldfish_tty_delete_driver(); 43362306a36Sopenharmony_ci mutex_unlock(&goldfish_tty_lock); 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci#ifdef CONFIG_GOLDFISH_TTY_EARLY_CONSOLE 43862306a36Sopenharmony_cistatic void gf_early_console_putchar(struct uart_port *port, unsigned char ch) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci gf_iowrite32(ch, port->membase); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic void gf_early_write(struct console *con, const char *s, unsigned int n) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct earlycon_device *dev = con->data; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci uart_console_write(&dev->port, s, n, gf_early_console_putchar); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int __init gf_earlycon_setup(struct earlycon_device *device, 45162306a36Sopenharmony_ci const char *opt) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci if (!device->port.membase) 45462306a36Sopenharmony_ci return -ENODEV; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci device->con->write = gf_early_write; 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ciOF_EARLYCON_DECLARE(early_gf_tty, "google,goldfish-tty", gf_earlycon_setup); 46162306a36Sopenharmony_ci#endif 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic const struct of_device_id goldfish_tty_of_match[] = { 46462306a36Sopenharmony_ci { .compatible = "google,goldfish-tty", }, 46562306a36Sopenharmony_ci {}, 46662306a36Sopenharmony_ci}; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, goldfish_tty_of_match); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic struct platform_driver goldfish_tty_platform_driver = { 47162306a36Sopenharmony_ci .probe = goldfish_tty_probe, 47262306a36Sopenharmony_ci .remove = goldfish_tty_remove, 47362306a36Sopenharmony_ci .driver = { 47462306a36Sopenharmony_ci .name = "goldfish_tty", 47562306a36Sopenharmony_ci .of_match_table = goldfish_tty_of_match, 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci}; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cimodule_platform_driver(goldfish_tty_platform_driver); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 482