linux/drivers/input/serio/altera_ps2.c
<<
>>
Prefs
   1/*
   2 * Altera University Program PS2 controller driver
   3 *
   4 * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
   5 *
   6 * Based on sa1111ps2.c, which is:
   7 * Copyright (C) 2002 Russell King
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License version 2 as
  11 * published by the Free Software Foundation.
  12 */
  13
  14#include <linux/module.h>
  15#include <linux/init.h>
  16#include <linux/input.h>
  17#include <linux/serio.h>
  18#include <linux/interrupt.h>
  19#include <linux/platform_device.h>
  20#include <linux/io.h>
  21#include <linux/slab.h>
  22
  23#define DRV_NAME "altera_ps2"
  24
  25struct ps2if {
  26        struct serio *io;
  27        struct resource *iomem_res;
  28        void __iomem *base;
  29        unsigned irq;
  30};
  31
  32/*
  33 * Read all bytes waiting in the PS2 port.  There should be
  34 * at the most one, but we loop for safety.
  35 */
  36static irqreturn_t altera_ps2_rxint(int irq, void *dev_id)
  37{
  38        struct ps2if *ps2if = dev_id;
  39        unsigned int status;
  40        int handled = IRQ_NONE;
  41
  42        while ((status = readl(ps2if->base)) & 0xffff0000) {
  43                serio_interrupt(ps2if->io, status & 0xff, 0);
  44                handled = IRQ_HANDLED;
  45        }
  46
  47        return handled;
  48}
  49
  50/*
  51 * Write a byte to the PS2 port.
  52 */
  53static int altera_ps2_write(struct serio *io, unsigned char val)
  54{
  55        struct ps2if *ps2if = io->port_data;
  56
  57        writel(val, ps2if->base);
  58        return 0;
  59}
  60
  61static int altera_ps2_open(struct serio *io)
  62{
  63        struct ps2if *ps2if = io->port_data;
  64
  65        /* clear fifo */
  66        while (readl(ps2if->base) & 0xffff0000)
  67                /* empty */;
  68
  69        writel(1, ps2if->base + 4); /* enable rx irq */
  70        return 0;
  71}
  72
  73static void altera_ps2_close(struct serio *io)
  74{
  75        struct ps2if *ps2if = io->port_data;
  76
  77        writel(0, ps2if->base); /* disable rx irq */
  78}
  79
  80/*
  81 * Add one device to this driver.
  82 */
  83static int __devinit altera_ps2_probe(struct platform_device *pdev)
  84{
  85        struct ps2if *ps2if;
  86        struct serio *serio;
  87        int error, irq;
  88
  89        ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL);
  90        serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
  91        if (!ps2if || !serio) {
  92                error = -ENOMEM;
  93                goto err_free_mem;
  94        }
  95
  96        serio->id.type          = SERIO_8042;
  97        serio->write            = altera_ps2_write;
  98        serio->open             = altera_ps2_open;
  99        serio->close            = altera_ps2_close;
 100        strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name));
 101        strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys));
 102        serio->port_data        = ps2if;
 103        serio->dev.parent       = &pdev->dev;
 104        ps2if->io               = serio;
 105
 106        ps2if->iomem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 107        if (ps2if->iomem_res == NULL) {
 108                error = -ENOENT;
 109                goto err_free_mem;
 110        }
 111
 112
 113        irq = platform_get_irq(pdev, 0);
 114        if (irq < 0) {
 115                error = -ENXIO;
 116                goto err_free_mem;
 117        }
 118        ps2if->irq = irq;
 119
 120        if (!request_mem_region(ps2if->iomem_res->start,
 121                                resource_size(ps2if->iomem_res), pdev->name)) {
 122                error = -EBUSY;
 123                goto err_free_mem;
 124        }
 125
 126        ps2if->base = ioremap(ps2if->iomem_res->start,
 127                              resource_size(ps2if->iomem_res));
 128        if (!ps2if->base) {
 129                error = -ENOMEM;
 130                goto err_free_res;
 131        }
 132
 133        error = request_irq(ps2if->irq, altera_ps2_rxint, 0, pdev->name, ps2if);
 134        if (error) {
 135                dev_err(&pdev->dev, "could not allocate IRQ %d: %d\n",
 136                        ps2if->irq, error);
 137                goto err_unmap;
 138        }
 139
 140        dev_info(&pdev->dev, "base %p, irq %d\n", ps2if->base, ps2if->irq);
 141
 142        serio_register_port(ps2if->io);
 143        platform_set_drvdata(pdev, ps2if);
 144
 145        return 0;
 146
 147 err_unmap:
 148        iounmap(ps2if->base);
 149 err_free_res:
 150        release_mem_region(ps2if->iomem_res->start,
 151                           resource_size(ps2if->iomem_res));
 152 err_free_mem:
 153        kfree(ps2if);
 154        kfree(serio);
 155        return error;
 156}
 157
 158/*
 159 * Remove one device from this driver.
 160 */
 161static int __devexit altera_ps2_remove(struct platform_device *pdev)
 162{
 163        struct ps2if *ps2if = platform_get_drvdata(pdev);
 164
 165        platform_set_drvdata(pdev, NULL);
 166        serio_unregister_port(ps2if->io);
 167        free_irq(ps2if->irq, ps2if);
 168        iounmap(ps2if->base);
 169        release_mem_region(ps2if->iomem_res->start,
 170                           resource_size(ps2if->iomem_res));
 171        kfree(ps2if);
 172
 173        return 0;
 174}
 175
 176/*
 177 * Our device driver structure
 178 */
 179static struct platform_driver altera_ps2_driver = {
 180        .probe          = altera_ps2_probe,
 181        .remove         = __devexit_p(altera_ps2_remove),
 182        .driver = {
 183                .name   = DRV_NAME,
 184                .owner  = THIS_MODULE,
 185        },
 186};
 187
 188static int __init altera_ps2_init(void)
 189{
 190        return platform_driver_register(&altera_ps2_driver);
 191}
 192
 193static void __exit altera_ps2_exit(void)
 194{
 195        platform_driver_unregister(&altera_ps2_driver);
 196}
 197
 198module_init(altera_ps2_init);
 199module_exit(altera_ps2_exit);
 200
 201MODULE_DESCRIPTION("Altera University Program PS2 controller driver");
 202MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
 203MODULE_LICENSE("GPL");
 204MODULE_ALIAS("platform:" DRV_NAME);
 205