linux/drivers/input/serio/ambakmi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  linux/drivers/input/serio/ambakmi.c
   4 *
   5 *  Copyright (C) 2000-2003 Deep Blue Solutions Ltd.
   6 *  Copyright (C) 2002 Russell King.
   7 */
   8#include <linux/module.h>
   9#include <linux/serio.h>
  10#include <linux/errno.h>
  11#include <linux/interrupt.h>
  12#include <linux/ioport.h>
  13#include <linux/device.h>
  14#include <linux/delay.h>
  15#include <linux/slab.h>
  16#include <linux/err.h>
  17#include <linux/amba/bus.h>
  18#include <linux/amba/kmi.h>
  19#include <linux/clk.h>
  20
  21#include <asm/io.h>
  22#include <asm/irq.h>
  23
  24#define KMI_BASE        (kmi->base)
  25
  26struct amba_kmi_port {
  27        struct serio            *io;
  28        struct clk              *clk;
  29        void __iomem            *base;
  30        unsigned int            irq;
  31        unsigned int            divisor;
  32        unsigned int            open;
  33};
  34
  35static irqreturn_t amba_kmi_int(int irq, void *dev_id)
  36{
  37        struct amba_kmi_port *kmi = dev_id;
  38        unsigned int status = readb(KMIIR);
  39        int handled = IRQ_NONE;
  40
  41        while (status & KMIIR_RXINTR) {
  42                serio_interrupt(kmi->io, readb(KMIDATA), 0);
  43                status = readb(KMIIR);
  44                handled = IRQ_HANDLED;
  45        }
  46
  47        return handled;
  48}
  49
  50static int amba_kmi_write(struct serio *io, unsigned char val)
  51{
  52        struct amba_kmi_port *kmi = io->port_data;
  53        unsigned int timeleft = 10000; /* timeout in 100ms */
  54
  55        while ((readb(KMISTAT) & KMISTAT_TXEMPTY) == 0 && --timeleft)
  56                udelay(10);
  57
  58        if (timeleft)
  59                writeb(val, KMIDATA);
  60
  61        return timeleft ? 0 : SERIO_TIMEOUT;
  62}
  63
  64static int amba_kmi_open(struct serio *io)
  65{
  66        struct amba_kmi_port *kmi = io->port_data;
  67        unsigned int divisor;
  68        int ret;
  69
  70        ret = clk_prepare_enable(kmi->clk);
  71        if (ret)
  72                goto out;
  73
  74        divisor = clk_get_rate(kmi->clk) / 8000000 - 1;
  75        writeb(divisor, KMICLKDIV);
  76        writeb(KMICR_EN, KMICR);
  77
  78        ret = request_irq(kmi->irq, amba_kmi_int, IRQF_SHARED, "kmi-pl050",
  79                          kmi);
  80        if (ret) {
  81                printk(KERN_ERR "kmi: failed to claim IRQ%d\n", kmi->irq);
  82                writeb(0, KMICR);
  83                goto clk_disable;
  84        }
  85
  86        writeb(KMICR_EN | KMICR_RXINTREN, KMICR);
  87
  88        return 0;
  89
  90 clk_disable:
  91        clk_disable_unprepare(kmi->clk);
  92 out:
  93        return ret;
  94}
  95
  96static void amba_kmi_close(struct serio *io)
  97{
  98        struct amba_kmi_port *kmi = io->port_data;
  99
 100        writeb(0, KMICR);
 101
 102        free_irq(kmi->irq, kmi);
 103        clk_disable_unprepare(kmi->clk);
 104}
 105
 106static int amba_kmi_probe(struct amba_device *dev,
 107        const struct amba_id *id)
 108{
 109        struct amba_kmi_port *kmi;
 110        struct serio *io;
 111        int ret;
 112
 113        ret = amba_request_regions(dev, NULL);
 114        if (ret)
 115                return ret;
 116
 117        kmi = kzalloc(sizeof(struct amba_kmi_port), GFP_KERNEL);
 118        io = kzalloc(sizeof(struct serio), GFP_KERNEL);
 119        if (!kmi || !io) {
 120                ret = -ENOMEM;
 121                goto out;
 122        }
 123
 124
 125        io->id.type     = SERIO_8042;
 126        io->write       = amba_kmi_write;
 127        io->open        = amba_kmi_open;
 128        io->close       = amba_kmi_close;
 129        strlcpy(io->name, dev_name(&dev->dev), sizeof(io->name));
 130        strlcpy(io->phys, dev_name(&dev->dev), sizeof(io->phys));
 131        io->port_data   = kmi;
 132        io->dev.parent  = &dev->dev;
 133
 134        kmi->io         = io;
 135        kmi->base       = ioremap(dev->res.start, resource_size(&dev->res));
 136        if (!kmi->base) {
 137                ret = -ENOMEM;
 138                goto out;
 139        }
 140
 141        kmi->clk = clk_get(&dev->dev, "KMIREFCLK");
 142        if (IS_ERR(kmi->clk)) {
 143                ret = PTR_ERR(kmi->clk);
 144                goto unmap;
 145        }
 146
 147        kmi->irq = dev->irq[0];
 148        amba_set_drvdata(dev, kmi);
 149
 150        serio_register_port(kmi->io);
 151        return 0;
 152
 153 unmap:
 154        iounmap(kmi->base);
 155 out:
 156        kfree(kmi);
 157        kfree(io);
 158        amba_release_regions(dev);
 159        return ret;
 160}
 161
 162static void amba_kmi_remove(struct amba_device *dev)
 163{
 164        struct amba_kmi_port *kmi = amba_get_drvdata(dev);
 165
 166        serio_unregister_port(kmi->io);
 167        clk_put(kmi->clk);
 168        iounmap(kmi->base);
 169        kfree(kmi);
 170        amba_release_regions(dev);
 171}
 172
 173static int __maybe_unused amba_kmi_resume(struct device *dev)
 174{
 175        struct amba_kmi_port *kmi = dev_get_drvdata(dev);
 176
 177        /* kick the serio layer to rescan this port */
 178        serio_reconnect(kmi->io);
 179
 180        return 0;
 181}
 182
 183static SIMPLE_DEV_PM_OPS(amba_kmi_dev_pm_ops, NULL, amba_kmi_resume);
 184
 185static const struct amba_id amba_kmi_idtable[] = {
 186        {
 187                .id     = 0x00041050,
 188                .mask   = 0x000fffff,
 189        },
 190        { 0, 0 }
 191};
 192
 193MODULE_DEVICE_TABLE(amba, amba_kmi_idtable);
 194
 195static struct amba_driver ambakmi_driver = {
 196        .drv            = {
 197                .name   = "kmi-pl050",
 198                .owner  = THIS_MODULE,
 199                .pm     = &amba_kmi_dev_pm_ops,
 200        },
 201        .id_table       = amba_kmi_idtable,
 202        .probe          = amba_kmi_probe,
 203        .remove         = amba_kmi_remove,
 204};
 205
 206module_amba_driver(ambakmi_driver);
 207
 208MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
 209MODULE_DESCRIPTION("AMBA KMI controller driver");
 210MODULE_LICENSE("GPL");
 211