goldfish.c 12.0 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
A
Arve Hjønnevåg 已提交
2 3 4
/*
 * Copyright (C) 2007 Google, Inc.
 * Copyright (C) 2012 Intel, Inc.
5
 * Copyright (C) 2017 Imagination Technologies Ltd.
A
Arve Hjønnevåg 已提交
6 7 8 9 10 11 12 13 14 15
 */

#include <linux/console.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/module.h>
16
#include <linux/mod_devicetable.h>
A
Alan 已提交
17
#include <linux/goldfish.h>
18 19
#include <linux/mm.h>
#include <linux/dma-mapping.h>
20
#include <linux/serial_core.h>
A
Arve Hjønnevåg 已提交
21

22 23 24 25 26 27
/* 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
28
#define	GOLDFISH_TTY_REG_VERSION	0x20
29 30 31 32 33 34

/* 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
A
Arve Hjønnevåg 已提交
35 36 37 38 39 40 41 42

struct goldfish_tty {
	struct tty_port port;
	spinlock_t lock;
	void __iomem *base;
	u32 irq;
	int opencount;
	struct console console;
43 44
	u32 version;
	struct device *dev;
A
Arve Hjønnevåg 已提交
45 46 47 48 49 50 51 52
};

static DEFINE_MUTEX(goldfish_tty_lock);
static struct tty_driver *goldfish_tty_driver;
static u32 goldfish_tty_line_count = 8;
static u32 goldfish_tty_current_line_count;
static struct goldfish_tty *goldfish_ttys;

53 54 55 56
static void do_rw_io(struct goldfish_tty *qtty,
		     unsigned long address,
		     unsigned int count,
		     int is_write)
A
Arve Hjønnevåg 已提交
57 58 59
{
	unsigned long irq_flags;
	void __iomem *base = qtty->base;
60

A
Arve Hjønnevåg 已提交
61
	spin_lock_irqsave(&qtty->lock, irq_flags);
62
	gf_write_ptr((void *)address, base + GOLDFISH_TTY_REG_DATA_PTR,
63
		     base + GOLDFISH_TTY_REG_DATA_PTR_HIGH);
64
	__raw_writel(count, base + GOLDFISH_TTY_REG_DATA_LEN);
65 66

	if (is_write)
67
		__raw_writel(GOLDFISH_TTY_CMD_WRITE_BUFFER,
68 69
		       base + GOLDFISH_TTY_REG_CMD);
	else
70
		__raw_writel(GOLDFISH_TTY_CMD_READ_BUFFER,
71 72
		       base + GOLDFISH_TTY_REG_CMD);

A
Arve Hjønnevåg 已提交
73 74 75
	spin_unlock_irqrestore(&qtty->lock, irq_flags);
}

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
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);
}

A
Arve Hjønnevåg 已提交
137 138
static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)
{
139
	struct goldfish_tty *qtty = dev_id;
A
Arve Hjønnevåg 已提交
140
	void __iomem *base = qtty->base;
141
	unsigned long address;
A
Arve Hjønnevåg 已提交
142 143 144
	unsigned char *buf;
	u32 count;

145
	count = __raw_readl(base + GOLDFISH_TTY_REG_BYTES_READY);
146
	if (count == 0)
A
Arve Hjønnevåg 已提交
147 148
		return IRQ_NONE;

149
	count = tty_prepare_flip_string(&qtty->port, &buf, count);
150 151 152 153

	address = (unsigned long)(void *)buf;
	goldfish_tty_rw(qtty, address, count, 0);

154
	tty_schedule_flip(&qtty->port);
A
Arve Hjønnevåg 已提交
155 156 157 158 159
	return IRQ_HANDLED;
}

static int goldfish_tty_activate(struct tty_port *port, struct tty_struct *tty)
{
160 161
	struct goldfish_tty *qtty = container_of(port, struct goldfish_tty,
									port);
162
	__raw_writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_REG_CMD);
A
Arve Hjønnevåg 已提交
163 164 165 166 167
	return 0;
}

static void goldfish_tty_shutdown(struct tty_port *port)
{
168 169
	struct goldfish_tty *qtty = container_of(port, struct goldfish_tty,
									port);
170
	__raw_writel(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_REG_CMD);
A
Arve Hjønnevåg 已提交
171 172
}

173
static int goldfish_tty_open(struct tty_struct *tty, struct file *filp)
A
Arve Hjønnevåg 已提交
174 175 176 177 178
{
	struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
	return tty_port_open(&qtty->port, tty, filp);
}

179
static void goldfish_tty_close(struct tty_struct *tty, struct file *filp)
A
Arve Hjønnevåg 已提交
180 181 182 183 184 185 186 187 188
{
	tty_port_close(tty->port, tty, filp);
}

static void goldfish_tty_hangup(struct tty_struct *tty)
{
	tty_port_hangup(tty->port);
}

189 190
static int goldfish_tty_write(struct tty_struct *tty, const unsigned char *buf,
								int count)
A
Arve Hjønnevåg 已提交
191 192 193 194 195
{
	goldfish_tty_do_write(tty->index, buf, count);
	return count;
}

196
static unsigned int goldfish_tty_write_room(struct tty_struct *tty)
A
Arve Hjønnevåg 已提交
197 198 199 200
{
	return 0x10000;
}

201
static unsigned int goldfish_tty_chars_in_buffer(struct tty_struct *tty)
A
Arve Hjønnevåg 已提交
202 203 204
{
	struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
	void __iomem *base = qtty->base;
205
	return __raw_readl(base + GOLDFISH_TTY_REG_BYTES_READY);
A
Arve Hjønnevåg 已提交
206 207
}

208 209
static void goldfish_tty_console_write(struct console *co, const char *b,
								unsigned count)
A
Arve Hjønnevåg 已提交
210 211 212 213
{
	goldfish_tty_do_write(co->index, b, count);
}

214 215
static struct tty_driver *goldfish_tty_console_device(struct console *c,
								int *index)
A
Arve Hjønnevåg 已提交
216 217 218 219 220 221 222
{
	*index = c->index;
	return goldfish_tty_driver;
}

static int goldfish_tty_console_setup(struct console *co, char *options)
{
223
	if ((unsigned)co->index >= goldfish_tty_line_count)
A
Arve Hjønnevåg 已提交
224
		return -ENODEV;
F
Fabian Frederick 已提交
225
	if (!goldfish_ttys[co->index].base)
A
Arve Hjønnevåg 已提交
226 227 228 229
		return -ENODEV;
	return 0;
}

230
static const struct tty_port_operations goldfish_port_ops = {
A
Arve Hjønnevåg 已提交
231 232 233 234
	.activate = goldfish_tty_activate,
	.shutdown = goldfish_tty_shutdown
};

235
static const struct tty_operations goldfish_tty_ops = {
A
Arve Hjønnevåg 已提交
236 237 238 239 240 241 242 243 244 245 246 247 248
	.open = goldfish_tty_open,
	.close = goldfish_tty_close,
	.hangup = goldfish_tty_hangup,
	.write = goldfish_tty_write,
	.write_room = goldfish_tty_write_room,
	.chars_in_buffer = goldfish_tty_chars_in_buffer,
};

static int goldfish_tty_create_driver(void)
{
	int ret;
	struct tty_driver *tty;

K
Kees Cook 已提交
249 250 251
	goldfish_ttys = kcalloc(goldfish_tty_line_count,
				sizeof(*goldfish_ttys),
				GFP_KERNEL);
252
	if (goldfish_ttys == NULL) {
A
Arve Hjønnevåg 已提交
253 254 255
		ret = -ENOMEM;
		goto err_alloc_goldfish_ttys_failed;
	}
J
Jiri Slaby 已提交
256 257 258 259 260 261
	tty = tty_alloc_driver(goldfish_tty_line_count,
			TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
			TTY_DRIVER_DYNAMIC_DEV);
	if (IS_ERR(tty)) {
		ret = PTR_ERR(tty);
		goto err_tty_alloc_driver_failed;
A
Arve Hjønnevåg 已提交
262 263 264 265 266 267 268 269
	}
	tty->driver_name = "goldfish";
	tty->name = "ttyGF";
	tty->type = TTY_DRIVER_TYPE_SERIAL;
	tty->subtype = SERIAL_TYPE_NORMAL;
	tty->init_termios = tty_std_termios;
	tty_set_operations(tty, &goldfish_tty_ops);
	ret = tty_register_driver(tty);
270
	if (ret)
A
Arve Hjønnevåg 已提交
271 272 273 274 275 276 277
		goto err_tty_register_driver_failed;

	goldfish_tty_driver = tty;
	return 0;

err_tty_register_driver_failed:
	put_tty_driver(tty);
J
Jiri Slaby 已提交
278
err_tty_alloc_driver_failed:
A
Arve Hjønnevåg 已提交
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
	kfree(goldfish_ttys);
	goldfish_ttys = NULL;
err_alloc_goldfish_ttys_failed:
	return ret;
}

static void goldfish_tty_delete_driver(void)
{
	tty_unregister_driver(goldfish_tty_driver);
	put_tty_driver(goldfish_tty_driver);
	goldfish_tty_driver = NULL;
	kfree(goldfish_ttys);
	goldfish_ttys = NULL;
}

static int goldfish_tty_probe(struct platform_device *pdev)
{
	struct goldfish_tty *qtty;
297
	int ret = -ENODEV;
A
Arve Hjønnevåg 已提交
298 299 300 301
	struct resource *r;
	struct device *ttydev;
	void __iomem *base;
	u32 irq;
302
	unsigned int line;
A
Arve Hjønnevåg 已提交
303 304

	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
305 306 307 308
	if (!r) {
		pr_err("goldfish_tty: No MEM resource available!\n");
		return -ENOMEM;
	}
A
Arve Hjønnevåg 已提交
309 310

	base = ioremap(r->start, 0x1000);
311 312 313 314
	if (!base) {
		pr_err("goldfish_tty: Unable to ioremap base!\n");
		return -ENOMEM;
	}
A
Arve Hjønnevåg 已提交
315 316

	r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
317 318
	if (!r) {
		pr_err("goldfish_tty: No IRQ resource available!\n");
A
Arve Hjønnevåg 已提交
319
		goto err_unmap;
320
	}
A
Arve Hjønnevåg 已提交
321 322 323 324

	irq = r->start;

	mutex_lock(&goldfish_tty_lock);
325 326 327 328 329 330

	if (pdev->id == PLATFORM_DEVID_NONE)
		line = goldfish_tty_current_line_count;
	else
		line = pdev->id;

331 332 333 334 335 336
	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;
	}
337

338
	if (goldfish_tty_current_line_count == 0) {
A
Arve Hjønnevåg 已提交
339
		ret = goldfish_tty_create_driver();
340
		if (ret)
341
			goto err_unlock;
A
Arve Hjønnevåg 已提交
342 343 344
	}
	goldfish_tty_current_line_count++;

345
	qtty = &goldfish_ttys[line];
A
Arve Hjønnevåg 已提交
346 347 348 349 350
	spin_lock_init(&qtty->lock);
	tty_port_init(&qtty->port);
	qtty->port.ops = &goldfish_port_ops;
	qtty->base = base;
	qtty->irq = irq;
351 352 353 354 355 356 357 358 359
	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.
	 */
360
	qtty->version = __raw_readl(base + GOLDFISH_TTY_REG_VERSION);
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377

	/*
	 * 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;
		}
	}
A
Arve Hjønnevåg 已提交
378

379
	__raw_writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_REG_CMD);
A
Arve Hjønnevåg 已提交
380

381
	ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED,
382 383 384 385 386
			  "goldfish_tty", qtty);
	if (ret) {
		pr_err("goldfish_tty: No IRQ available!\n");
		goto err_dec_line_count;
	}
A
Arve Hjønnevåg 已提交
387 388

	ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver,
389
					  line, &pdev->dev);
390
	if (IS_ERR(ttydev)) {
A
Arve Hjønnevåg 已提交
391 392 393 394 395 396 397 398 399
		ret = PTR_ERR(ttydev);
		goto err_tty_register_device_failed;
	}

	strcpy(qtty->console.name, "ttyGF");
	qtty->console.write = goldfish_tty_console_write;
	qtty->console.device = goldfish_tty_console_device;
	qtty->console.setup = goldfish_tty_console_setup;
	qtty->console.flags = CON_PRINTBUFFER;
400
	qtty->console.index = line;
A
Arve Hjønnevåg 已提交
401
	register_console(&qtty->console);
402
	platform_set_drvdata(pdev, qtty);
A
Arve Hjønnevåg 已提交
403 404 405 406 407

	mutex_unlock(&goldfish_tty_lock);
	return 0;

err_tty_register_device_failed:
408
	free_irq(irq, qtty);
409
err_dec_line_count:
A
Arve Hjønnevåg 已提交
410
	goldfish_tty_current_line_count--;
411
	if (goldfish_tty_current_line_count == 0)
A
Arve Hjønnevåg 已提交
412
		goldfish_tty_delete_driver();
413
err_unlock:
A
Arve Hjønnevåg 已提交
414 415 416 417 418 419 420 421
	mutex_unlock(&goldfish_tty_lock);
err_unmap:
	iounmap(base);
	return ret;
}

static int goldfish_tty_remove(struct platform_device *pdev)
{
422
	struct goldfish_tty *qtty = platform_get_drvdata(pdev);
A
Arve Hjønnevåg 已提交
423 424 425 426

	mutex_lock(&goldfish_tty_lock);

	unregister_console(&qtty->console);
427
	tty_unregister_device(goldfish_tty_driver, qtty->console.index);
A
Arve Hjønnevåg 已提交
428
	iounmap(qtty->base);
F
Fabian Frederick 已提交
429
	qtty->base = NULL;
A
Arve Hjønnevåg 已提交
430 431
	free_irq(qtty->irq, pdev);
	goldfish_tty_current_line_count--;
432
	if (goldfish_tty_current_line_count == 0)
A
Arve Hjønnevåg 已提交
433 434 435 436 437
		goldfish_tty_delete_driver();
	mutex_unlock(&goldfish_tty_lock);
	return 0;
}

438
#ifdef CONFIG_GOLDFISH_TTY_EARLY_CONSOLE
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
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);
462
#endif
463

464 465 466 467 468 469 470
static const struct of_device_id goldfish_tty_of_match[] = {
	{ .compatible = "google,goldfish-tty", },
	{},
};

MODULE_DEVICE_TABLE(of, goldfish_tty_of_match);

A
Arve Hjønnevåg 已提交
471 472 473 474
static struct platform_driver goldfish_tty_platform_driver = {
	.probe = goldfish_tty_probe,
	.remove = goldfish_tty_remove,
	.driver = {
475 476
		.name = "goldfish_tty",
		.of_match_table = goldfish_tty_of_match,
A
Arve Hjønnevåg 已提交
477 478 479 480 481 482
	}
};

module_platform_driver(goldfish_tty_platform_driver);

MODULE_LICENSE("GPL v2");