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