xref: /linux/drivers/input/serio/ambakmi.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/drivers/input/serio/ambakmi.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 2000-2003 Deep Blue Solutions Ltd.
61da177e4SLinus Torvalds  *  Copyright (C) 2002 Russell King.
71da177e4SLinus Torvalds  */
81da177e4SLinus Torvalds #include <linux/module.h>
91da177e4SLinus Torvalds #include <linux/serio.h>
101da177e4SLinus Torvalds #include <linux/errno.h>
111da177e4SLinus Torvalds #include <linux/interrupt.h>
121da177e4SLinus Torvalds #include <linux/ioport.h>
131da177e4SLinus Torvalds #include <linux/device.h>
141da177e4SLinus Torvalds #include <linux/delay.h>
151da177e4SLinus Torvalds #include <linux/slab.h>
161da177e4SLinus Torvalds #include <linux/err.h>
17a62c80e5SRussell King #include <linux/amba/bus.h>
18a62c80e5SRussell King #include <linux/amba/kmi.h>
19f8ce2547SRussell King #include <linux/clk.h>
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds #include <asm/io.h>
221da177e4SLinus Torvalds #include <asm/irq.h>
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds #define KMI_BASE	(kmi->base)
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds struct amba_kmi_port {
271da177e4SLinus Torvalds 	struct serio		*io;
281da177e4SLinus Torvalds 	struct clk		*clk;
291da177e4SLinus Torvalds 	void __iomem		*base;
301da177e4SLinus Torvalds 	unsigned int		irq;
311da177e4SLinus Torvalds 	unsigned int		divisor;
321da177e4SLinus Torvalds 	unsigned int		open;
331da177e4SLinus Torvalds };
341da177e4SLinus Torvalds 
amba_kmi_int(int irq,void * dev_id)357d12e780SDavid Howells static irqreturn_t amba_kmi_int(int irq, void *dev_id)
361da177e4SLinus Torvalds {
371da177e4SLinus Torvalds 	struct amba_kmi_port *kmi = dev_id;
381da177e4SLinus Torvalds 	unsigned int status = readb(KMIIR);
391da177e4SLinus Torvalds 	int handled = IRQ_NONE;
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds 	while (status & KMIIR_RXINTR) {
427d12e780SDavid Howells 		serio_interrupt(kmi->io, readb(KMIDATA), 0);
431da177e4SLinus Torvalds 		status = readb(KMIIR);
441da177e4SLinus Torvalds 		handled = IRQ_HANDLED;
451da177e4SLinus Torvalds 	}
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds 	return handled;
481da177e4SLinus Torvalds }
491da177e4SLinus Torvalds 
amba_kmi_write(struct serio * io,unsigned char val)501da177e4SLinus Torvalds static int amba_kmi_write(struct serio *io, unsigned char val)
511da177e4SLinus Torvalds {
521da177e4SLinus Torvalds 	struct amba_kmi_port *kmi = io->port_data;
531da177e4SLinus Torvalds 	unsigned int timeleft = 10000; /* timeout in 100ms */
541da177e4SLinus Torvalds 
554ab73761SRoel Kluin 	while ((readb(KMISTAT) & KMISTAT_TXEMPTY) == 0 && --timeleft)
561da177e4SLinus Torvalds 		udelay(10);
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 	if (timeleft)
591da177e4SLinus Torvalds 		writeb(val, KMIDATA);
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds 	return timeleft ? 0 : SERIO_TIMEOUT;
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds 
amba_kmi_open(struct serio * io)641da177e4SLinus Torvalds static int amba_kmi_open(struct serio *io)
651da177e4SLinus Torvalds {
661da177e4SLinus Torvalds 	struct amba_kmi_port *kmi = io->port_data;
671da177e4SLinus Torvalds 	unsigned int divisor;
681da177e4SLinus Torvalds 	int ret;
691da177e4SLinus Torvalds 
7059d1f5c4SPawel Moll 	ret = clk_prepare_enable(kmi->clk);
711da177e4SLinus Torvalds 	if (ret)
72a8d3584aSRussell King 		goto out;
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds 	divisor = clk_get_rate(kmi->clk) / 8000000 - 1;
751da177e4SLinus Torvalds 	writeb(divisor, KMICLKDIV);
761da177e4SLinus Torvalds 	writeb(KMICR_EN, KMICR);
771da177e4SLinus Torvalds 
785d61b54fSLiviu Dudau 	ret = request_irq(kmi->irq, amba_kmi_int, IRQF_SHARED, "kmi-pl050",
795d61b54fSLiviu Dudau 			  kmi);
801da177e4SLinus Torvalds 	if (ret) {
811da177e4SLinus Torvalds 		printk(KERN_ERR "kmi: failed to claim IRQ%d\n", kmi->irq);
821da177e4SLinus Torvalds 		writeb(0, KMICR);
831da177e4SLinus Torvalds 		goto clk_disable;
841da177e4SLinus Torvalds 	}
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	writeb(KMICR_EN | KMICR_RXINTREN, KMICR);
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 	return 0;
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds  clk_disable:
9159d1f5c4SPawel Moll 	clk_disable_unprepare(kmi->clk);
921da177e4SLinus Torvalds  out:
931da177e4SLinus Torvalds 	return ret;
941da177e4SLinus Torvalds }
951da177e4SLinus Torvalds 
amba_kmi_close(struct serio * io)961da177e4SLinus Torvalds static void amba_kmi_close(struct serio *io)
971da177e4SLinus Torvalds {
981da177e4SLinus Torvalds 	struct amba_kmi_port *kmi = io->port_data;
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds 	writeb(0, KMICR);
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds 	free_irq(kmi->irq, kmi);
10359d1f5c4SPawel Moll 	clk_disable_unprepare(kmi->clk);
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds 
amba_kmi_probe(struct amba_device * dev,const struct amba_id * id)1065298cc4cSBill Pemberton static int amba_kmi_probe(struct amba_device *dev,
107aa25afadSRussell King 	const struct amba_id *id)
1081da177e4SLinus Torvalds {
1091da177e4SLinus Torvalds 	struct amba_kmi_port *kmi;
1101da177e4SLinus Torvalds 	struct serio *io;
1111da177e4SLinus Torvalds 	int ret;
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds 	ret = amba_request_regions(dev, NULL);
1141da177e4SLinus Torvalds 	if (ret)
1151da177e4SLinus Torvalds 		return ret;
1161da177e4SLinus Torvalds 
117*06b449d7SErick Archer 	kmi = kzalloc(sizeof(*kmi), GFP_KERNEL);
118*06b449d7SErick Archer 	io = kzalloc(sizeof(*io), GFP_KERNEL);
1191da177e4SLinus Torvalds 	if (!kmi || !io) {
1201da177e4SLinus Torvalds 		ret = -ENOMEM;
1211da177e4SLinus Torvalds 		goto out;
1221da177e4SLinus Torvalds 	}
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds 	io->id.type	= SERIO_8042;
1261da177e4SLinus Torvalds 	io->write	= amba_kmi_write;
1271da177e4SLinus Torvalds 	io->open	= amba_kmi_open;
1281da177e4SLinus Torvalds 	io->close	= amba_kmi_close;
129a9f08ad7SWolfram Sang 	strscpy(io->name, dev_name(&dev->dev), sizeof(io->name));
130a9f08ad7SWolfram Sang 	strscpy(io->phys, dev_name(&dev->dev), sizeof(io->phys));
1311da177e4SLinus Torvalds 	io->port_data	= kmi;
1321da177e4SLinus Torvalds 	io->dev.parent	= &dev->dev;
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds 	kmi->io		= io;
135dc890c2dSLinus Walleij 	kmi->base	= ioremap(dev->res.start, resource_size(&dev->res));
1361da177e4SLinus Torvalds 	if (!kmi->base) {
1371da177e4SLinus Torvalds 		ret = -ENOMEM;
1381da177e4SLinus Torvalds 		goto out;
1391da177e4SLinus Torvalds 	}
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds 	kmi->clk = clk_get(&dev->dev, "KMIREFCLK");
1421da177e4SLinus Torvalds 	if (IS_ERR(kmi->clk)) {
1431da177e4SLinus Torvalds 		ret = PTR_ERR(kmi->clk);
1441da177e4SLinus Torvalds 		goto unmap;
1451da177e4SLinus Torvalds 	}
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds 	kmi->irq = dev->irq[0];
1481da177e4SLinus Torvalds 	amba_set_drvdata(dev, kmi);
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds 	serio_register_port(kmi->io);
1511da177e4SLinus Torvalds 	return 0;
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds  unmap:
1541da177e4SLinus Torvalds 	iounmap(kmi->base);
1551da177e4SLinus Torvalds  out:
1561da177e4SLinus Torvalds 	kfree(kmi);
1571da177e4SLinus Torvalds 	kfree(io);
1581da177e4SLinus Torvalds 	amba_release_regions(dev);
1591da177e4SLinus Torvalds 	return ret;
1601da177e4SLinus Torvalds }
1611da177e4SLinus Torvalds 
amba_kmi_remove(struct amba_device * dev)1623fd269e7SUwe Kleine-König static void amba_kmi_remove(struct amba_device *dev)
1631da177e4SLinus Torvalds {
1641da177e4SLinus Torvalds 	struct amba_kmi_port *kmi = amba_get_drvdata(dev);
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	serio_unregister_port(kmi->io);
1671da177e4SLinus Torvalds 	clk_put(kmi->clk);
1681da177e4SLinus Torvalds 	iounmap(kmi->base);
1691da177e4SLinus Torvalds 	kfree(kmi);
1701da177e4SLinus Torvalds 	amba_release_regions(dev);
1711da177e4SLinus Torvalds }
1721da177e4SLinus Torvalds 
amba_kmi_resume(struct device * dev)1739c19131fSJonathan Cameron static int amba_kmi_resume(struct device *dev)
1741da177e4SLinus Torvalds {
175cee3d8ccSUlf Hansson 	struct amba_kmi_port *kmi = dev_get_drvdata(dev);
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	/* kick the serio layer to rescan this port */
1781da177e4SLinus Torvalds 	serio_reconnect(kmi->io);
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds 	return 0;
1811da177e4SLinus Torvalds }
1821da177e4SLinus Torvalds 
1839c19131fSJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(amba_kmi_dev_pm_ops, NULL, amba_kmi_resume);
184cee3d8ccSUlf Hansson 
185b54bf2fdSArvind Yadav static const struct amba_id amba_kmi_idtable[] = {
1861da177e4SLinus Torvalds 	{
1871da177e4SLinus Torvalds 		.id	= 0x00041050,
1881da177e4SLinus Torvalds 		.mask	= 0x000fffff,
1891da177e4SLinus Torvalds 	},
1901da177e4SLinus Torvalds 	{ 0, 0 }
1911da177e4SLinus Torvalds };
1921da177e4SLinus Torvalds 
1932dfff235SDave Martin MODULE_DEVICE_TABLE(amba, amba_kmi_idtable);
1942dfff235SDave Martin 
1951da177e4SLinus Torvalds static struct amba_driver ambakmi_driver = {
1961da177e4SLinus Torvalds 	.drv		= {
1971da177e4SLinus Torvalds 		.name	= "kmi-pl050",
1989c19131fSJonathan Cameron 		.pm	= pm_sleep_ptr(&amba_kmi_dev_pm_ops),
1991da177e4SLinus Torvalds 	},
2001da177e4SLinus Torvalds 	.id_table	= amba_kmi_idtable,
2011da177e4SLinus Torvalds 	.probe		= amba_kmi_probe,
2021cb0aa88SBill Pemberton 	.remove		= amba_kmi_remove,
2031da177e4SLinus Torvalds };
2041da177e4SLinus Torvalds 
2059e5ed094Sviresh kumar module_amba_driver(ambakmi_driver);
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
2081da177e4SLinus Torvalds MODULE_DESCRIPTION("AMBA KMI controller driver");
2091da177e4SLinus Torvalds MODULE_LICENSE("GPL");
210