brintos

brintos / linux-shallow public Read only

0
0
Text · 11.9 KiB · c60745f Raw
475 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2007 Google, Inc.4 * Copyright (C) 2012 Intel, Inc.5 * Copyright (C) 2017 Imagination Technologies Ltd.6 */7 8#include <linux/console.h>9#include <linux/interrupt.h>10#include <linux/platform_device.h>11#include <linux/tty.h>12#include <linux/tty_flip.h>13#include <linux/slab.h>14#include <linux/io.h>15#include <linux/module.h>16#include <linux/mod_devicetable.h>17#include <linux/goldfish.h>18#include <linux/mm.h>19#include <linux/dma-mapping.h>20#include <linux/serial_core.h>21 22/* Goldfish tty register's offsets */23#define	GOLDFISH_TTY_REG_BYTES_READY	0x0424#define	GOLDFISH_TTY_REG_CMD		0x0825#define	GOLDFISH_TTY_REG_DATA_PTR	0x1026#define	GOLDFISH_TTY_REG_DATA_LEN	0x1427#define	GOLDFISH_TTY_REG_DATA_PTR_HIGH	0x1828#define	GOLDFISH_TTY_REG_VERSION	0x2029 30/* Goldfish tty commands */31#define	GOLDFISH_TTY_CMD_INT_DISABLE	032#define	GOLDFISH_TTY_CMD_INT_ENABLE	133#define	GOLDFISH_TTY_CMD_WRITE_BUFFER	234#define	GOLDFISH_TTY_CMD_READ_BUFFER	335 36struct goldfish_tty {37	struct tty_port port;38	spinlock_t lock;39	void __iomem *base;40	u32 irq;41	int opencount;42	struct console console;43	u32 version;44	struct device *dev;45};46 47static DEFINE_MUTEX(goldfish_tty_lock);48static struct tty_driver *goldfish_tty_driver;49static u32 goldfish_tty_line_count = 8;50static u32 goldfish_tty_current_line_count;51static struct goldfish_tty *goldfish_ttys;52 53static void do_rw_io(struct goldfish_tty *qtty, unsigned long address,54		     size_t count, bool is_write)55{56	unsigned long irq_flags;57	void __iomem *base = qtty->base;58 59	spin_lock_irqsave(&qtty->lock, irq_flags);60	gf_write_ptr((void *)address, base + GOLDFISH_TTY_REG_DATA_PTR,61		     base + GOLDFISH_TTY_REG_DATA_PTR_HIGH);62	gf_iowrite32(count, base + GOLDFISH_TTY_REG_DATA_LEN);63 64	if (is_write)65		gf_iowrite32(GOLDFISH_TTY_CMD_WRITE_BUFFER,66		       base + GOLDFISH_TTY_REG_CMD);67	else68		gf_iowrite32(GOLDFISH_TTY_CMD_READ_BUFFER,69		       base + GOLDFISH_TTY_REG_CMD);70 71	spin_unlock_irqrestore(&qtty->lock, irq_flags);72}73 74static void goldfish_tty_rw(struct goldfish_tty *qtty, unsigned long addr,75			    size_t count, bool is_write)76{77	dma_addr_t dma_handle;78	enum dma_data_direction dma_dir;79 80	dma_dir = (is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);81	if (qtty->version > 0) {82		/*83		 * Goldfish TTY for Ranchu platform uses84		 * physical addresses and DMA for read/write operations85		 */86		unsigned long addr_end = addr + count;87 88		while (addr < addr_end) {89			unsigned long pg_end = (addr & PAGE_MASK) + PAGE_SIZE;90			unsigned long next =91					pg_end < addr_end ? pg_end : addr_end;92			unsigned long avail = next - addr;93 94			/*95			 * Map the buffer's virtual address to the DMA address96			 * so the buffer can be accessed by the device.97			 */98			dma_handle = dma_map_single(qtty->dev, (void *)addr,99						    avail, dma_dir);100 101			if (dma_mapping_error(qtty->dev, dma_handle)) {102				dev_err(qtty->dev, "tty: DMA mapping error.\n");103				return;104			}105			do_rw_io(qtty, dma_handle, avail, is_write);106 107			/*108			 * Unmap the previously mapped region after109			 * the completion of the read/write operation.110			 */111			dma_unmap_single(qtty->dev, dma_handle, avail, dma_dir);112 113			addr += avail;114		}115	} else {116		/*117		 * Old style Goldfish TTY used on the Goldfish platform118		 * uses virtual addresses.119		 */120		do_rw_io(qtty, addr, count, is_write);121	}122}123 124static void goldfish_tty_do_write(int line, const u8 *buf, size_t count)125{126	struct goldfish_tty *qtty = &goldfish_ttys[line];127 128	goldfish_tty_rw(qtty, (unsigned long)buf, count, true);129}130 131static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)132{133	struct goldfish_tty *qtty = dev_id;134	void __iomem *base = qtty->base;135	u8 *buf;136	u32 count;137 138	count = gf_ioread32(base + GOLDFISH_TTY_REG_BYTES_READY);139	if (count == 0)140		return IRQ_NONE;141 142	count = tty_prepare_flip_string(&qtty->port, &buf, count);143 144	goldfish_tty_rw(qtty, (unsigned long)buf, count, false);145 146	tty_flip_buffer_push(&qtty->port);147	return IRQ_HANDLED;148}149 150static int goldfish_tty_activate(struct tty_port *port, struct tty_struct *tty)151{152	struct goldfish_tty *qtty = container_of(port, struct goldfish_tty,153									port);154	gf_iowrite32(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_REG_CMD);155	return 0;156}157 158static void goldfish_tty_shutdown(struct tty_port *port)159{160	struct goldfish_tty *qtty = container_of(port, struct goldfish_tty,161									port);162	gf_iowrite32(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_REG_CMD);163}164 165static int goldfish_tty_open(struct tty_struct *tty, struct file *filp)166{167	struct goldfish_tty *qtty = &goldfish_ttys[tty->index];168	return tty_port_open(&qtty->port, tty, filp);169}170 171static void goldfish_tty_close(struct tty_struct *tty, struct file *filp)172{173	tty_port_close(tty->port, tty, filp);174}175 176static void goldfish_tty_hangup(struct tty_struct *tty)177{178	tty_port_hangup(tty->port);179}180 181static ssize_t goldfish_tty_write(struct tty_struct *tty, const u8 *buf,182				  size_t count)183{184	goldfish_tty_do_write(tty->index, buf, count);185	return count;186}187 188static unsigned int goldfish_tty_write_room(struct tty_struct *tty)189{190	return 0x10000;191}192 193static unsigned int goldfish_tty_chars_in_buffer(struct tty_struct *tty)194{195	struct goldfish_tty *qtty = &goldfish_ttys[tty->index];196	void __iomem *base = qtty->base;197	return gf_ioread32(base + GOLDFISH_TTY_REG_BYTES_READY);198}199 200static void goldfish_tty_console_write(struct console *co, const char *b,201								unsigned count)202{203	goldfish_tty_do_write(co->index, b, count);204}205 206static struct tty_driver *goldfish_tty_console_device(struct console *c,207								int *index)208{209	*index = c->index;210	return goldfish_tty_driver;211}212 213static int goldfish_tty_console_setup(struct console *co, char *options)214{215	if ((unsigned)co->index >= goldfish_tty_line_count)216		return -ENODEV;217	if (!goldfish_ttys[co->index].base)218		return -ENODEV;219	return 0;220}221 222static const struct tty_port_operations goldfish_port_ops = {223	.activate = goldfish_tty_activate,224	.shutdown = goldfish_tty_shutdown225};226 227static const struct tty_operations goldfish_tty_ops = {228	.open = goldfish_tty_open,229	.close = goldfish_tty_close,230	.hangup = goldfish_tty_hangup,231	.write = goldfish_tty_write,232	.write_room = goldfish_tty_write_room,233	.chars_in_buffer = goldfish_tty_chars_in_buffer,234};235 236static int goldfish_tty_create_driver(void)237{238	int ret;239	struct tty_driver *tty;240 241	goldfish_ttys = kcalloc(goldfish_tty_line_count,242				sizeof(*goldfish_ttys),243				GFP_KERNEL);244	if (goldfish_ttys == NULL) {245		ret = -ENOMEM;246		goto err_alloc_goldfish_ttys_failed;247	}248	tty = tty_alloc_driver(goldfish_tty_line_count,249			TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |250			TTY_DRIVER_DYNAMIC_DEV);251	if (IS_ERR(tty)) {252		ret = PTR_ERR(tty);253		goto err_tty_alloc_driver_failed;254	}255	tty->driver_name = "goldfish";256	tty->name = "ttyGF";257	tty->type = TTY_DRIVER_TYPE_SERIAL;258	tty->subtype = SERIAL_TYPE_NORMAL;259	tty->init_termios = tty_std_termios;260	tty_set_operations(tty, &goldfish_tty_ops);261	ret = tty_register_driver(tty);262	if (ret)263		goto err_tty_register_driver_failed;264 265	goldfish_tty_driver = tty;266	return 0;267 268err_tty_register_driver_failed:269	tty_driver_kref_put(tty);270err_tty_alloc_driver_failed:271	kfree(goldfish_ttys);272	goldfish_ttys = NULL;273err_alloc_goldfish_ttys_failed:274	return ret;275}276 277static void goldfish_tty_delete_driver(void)278{279	tty_unregister_driver(goldfish_tty_driver);280	tty_driver_kref_put(goldfish_tty_driver);281	goldfish_tty_driver = NULL;282	kfree(goldfish_ttys);283	goldfish_ttys = NULL;284}285 286static int goldfish_tty_probe(struct platform_device *pdev)287{288	struct goldfish_tty *qtty;289	int ret = -ENODEV;290	struct resource *r;291	struct device *ttydev;292	void __iomem *base;293	int irq;294	unsigned int line;295 296	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);297	if (!r) {298		pr_err("goldfish_tty: No MEM resource available!\n");299		return -ENOMEM;300	}301 302	base = ioremap(r->start, 0x1000);303	if (!base) {304		pr_err("goldfish_tty: Unable to ioremap base!\n");305		return -ENOMEM;306	}307 308	irq = platform_get_irq(pdev, 0);309	if (irq < 0) {310		ret = irq;311		goto err_unmap;312	}313 314	mutex_lock(&goldfish_tty_lock);315 316	if (pdev->id == PLATFORM_DEVID_NONE)317		line = goldfish_tty_current_line_count;318	else319		line = pdev->id;320 321	if (line >= goldfish_tty_line_count) {322		pr_err("goldfish_tty: Reached maximum tty number of %d.\n",323		       goldfish_tty_current_line_count);324		ret = -ENOMEM;325		goto err_unlock;326	}327 328	if (goldfish_tty_current_line_count == 0) {329		ret = goldfish_tty_create_driver();330		if (ret)331			goto err_unlock;332	}333	goldfish_tty_current_line_count++;334 335	qtty = &goldfish_ttys[line];336	spin_lock_init(&qtty->lock);337	tty_port_init(&qtty->port);338	qtty->port.ops = &goldfish_port_ops;339	qtty->base = base;340	qtty->irq = irq;341	qtty->dev = &pdev->dev;342 343	/*344	 * Goldfish TTY device used by the Goldfish emulator345	 * should identify itself with 0, forcing the driver346	 * to use virtual addresses. Goldfish TTY device347	 * on Ranchu emulator (qemu2) returns 1 here and348	 * driver will use physical addresses.349	 */350	qtty->version = gf_ioread32(base + GOLDFISH_TTY_REG_VERSION);351 352	/*353	 * Goldfish TTY device on Ranchu emulator (qemu2)354	 * will use DMA for read/write IO operations.355	 */356	if (qtty->version > 0) {357		/*358		 * Initialize dma_mask to 32-bits.359		 */360		if (!pdev->dev.dma_mask)361			pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;362		ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));363		if (ret) {364			dev_err(&pdev->dev, "No suitable DMA available.\n");365			goto err_dec_line_count;366		}367	}368 369	gf_iowrite32(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_REG_CMD);370 371	ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED,372			  "goldfish_tty", qtty);373	if (ret) {374		pr_err("goldfish_tty: No IRQ available!\n");375		goto err_dec_line_count;376	}377 378	ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver,379					  line, &pdev->dev);380	if (IS_ERR(ttydev)) {381		ret = PTR_ERR(ttydev);382		goto err_tty_register_device_failed;383	}384 385	strcpy(qtty->console.name, "ttyGF");386	qtty->console.write = goldfish_tty_console_write;387	qtty->console.device = goldfish_tty_console_device;388	qtty->console.setup = goldfish_tty_console_setup;389	qtty->console.flags = CON_PRINTBUFFER;390	qtty->console.index = line;391	register_console(&qtty->console);392	platform_set_drvdata(pdev, qtty);393 394	mutex_unlock(&goldfish_tty_lock);395	return 0;396 397err_tty_register_device_failed:398	free_irq(irq, qtty);399err_dec_line_count:400	tty_port_destroy(&qtty->port);401	goldfish_tty_current_line_count--;402	if (goldfish_tty_current_line_count == 0)403		goldfish_tty_delete_driver();404err_unlock:405	mutex_unlock(&goldfish_tty_lock);406err_unmap:407	iounmap(base);408	return ret;409}410 411static void goldfish_tty_remove(struct platform_device *pdev)412{413	struct goldfish_tty *qtty = platform_get_drvdata(pdev);414 415	mutex_lock(&goldfish_tty_lock);416 417	unregister_console(&qtty->console);418	tty_unregister_device(goldfish_tty_driver, qtty->console.index);419	iounmap(qtty->base);420	qtty->base = NULL;421	free_irq(qtty->irq, qtty);422	tty_port_destroy(&qtty->port);423	goldfish_tty_current_line_count--;424	if (goldfish_tty_current_line_count == 0)425		goldfish_tty_delete_driver();426	mutex_unlock(&goldfish_tty_lock);427}428 429#ifdef CONFIG_GOLDFISH_TTY_EARLY_CONSOLE430static void gf_early_console_putchar(struct uart_port *port, unsigned char ch)431{432	gf_iowrite32(ch, port->membase);433}434 435static void gf_early_write(struct console *con, const char *s, unsigned int n)436{437	struct earlycon_device *dev = con->data;438 439	uart_console_write(&dev->port, s, n, gf_early_console_putchar);440}441 442static int __init gf_earlycon_setup(struct earlycon_device *device,443				    const char *opt)444{445	if (!device->port.membase)446		return -ENODEV;447 448	device->con->write = gf_early_write;449	return 0;450}451 452OF_EARLYCON_DECLARE(early_gf_tty, "google,goldfish-tty", gf_earlycon_setup);453#endif454 455static const struct of_device_id goldfish_tty_of_match[] = {456	{ .compatible = "google,goldfish-tty", },457	{},458};459 460MODULE_DEVICE_TABLE(of, goldfish_tty_of_match);461 462static struct platform_driver goldfish_tty_platform_driver = {463	.probe = goldfish_tty_probe,464	.remove_new = goldfish_tty_remove,465	.driver = {466		.name = "goldfish_tty",467		.of_match_table = goldfish_tty_of_match,468	}469};470 471module_platform_driver(goldfish_tty_platform_driver);472 473MODULE_DESCRIPTION("Goldfish TTY Driver");474MODULE_LICENSE("GPL v2");475