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