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