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#include <linux/of.h>
  23
  24#define DRV_NAME "altera_ps2"
  25
  26struct ps2if {
  27        struct serio *io;
  28        struct resource *iomem_res;
  29        void __iomem *base;
  30        unsigned irq;
  31};
  32
  33/*
  34 * Read all bytes waiting in the PS2 port.  There should be
  35 * at the most one, but we loop for safety.
  36 */
  37static irqreturn_t altera_ps2_rxint(int irq, void *dev_id)
  38{
  39        struct ps2if *ps2if = dev_id;
  40        unsigned int status;
  41        int handled = IRQ_NONE;
  42
  43        while ((status = readl(ps2if->base)) & 0xffff0000) {
  44                serio_interrupt(ps2if->io, status & 0xff, 0);
  45                handled = IRQ_HANDLED;
  46        }
  47
  48        return handled;
  49}
  50
  51/*
  52 * Write a byte to the PS2 port.
  53 */
  54static int altera_ps2_write(struct serio *io, unsigned char val)
  55{
  56        struct ps2if *ps2if = io->port_data;
  57
  58        writel(val, ps2if->base);
  59        return 0;
  60}
  61
  62static int altera_ps2_open(struct serio *io)
  63{
  64        struct ps2if *ps2if = io->port_data;
  65
  66        /* clear fifo */
  67        while (readl(ps2if->base) & 0xffff0000)
  68                /* empty */;
  69
  70        writel(1, ps2if->base + 4); /* enable rx irq */
  71        return 0;
  72}
  73
  74static void altera_ps2_close(struct serio *io)
  75{
  76        struct ps2if *ps2if = io->port_data;
  77
  78        writel(0, ps2if->base); /* disable rx irq */
  79}
  80
  81/*
  82 * Add one device to this driver.
  83 */
  84static int altera_ps2_probe(struct platform_device *pdev)
  85{
  86        struct ps2if *ps2if;
  87        struct serio *serio;
  88        int error, irq;
  89
  90        ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL);
  91        serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
  92        if (!ps2if || !serio) {
  93                error = -ENOMEM;
  94                goto err_free_mem;
  95        }
  96
  97        serio->id.type          = SERIO_8042;
  98        serio->write            = altera_ps2_write;
  99        serio->open             = altera_ps2_open;
 100        serio->close            = altera_ps2_close;
 101        strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name));
 102        strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys));
 103        serio->port_data        = ps2if;
 104        serio->dev.parent       = &pdev->dev;
 105        ps2if->io               = serio;
 106
 107        ps2if->iomem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 108        if (ps2if->iomem_res == NULL) {
 109                error = -ENOENT;
 110                goto err_free_mem;
 111        }
 112
 113
 114        irq = platform_get_irq(pdev, 0);
 115        if (irq < 0) {
 116                error = -ENXIO;
 117                goto err_free_mem;
 118        }
 119        ps2if->irq = irq;
 120
 121        if (!request_mem_region(ps2if->iomem_res->start,
 122                                resource_size(ps2if->iomem_res), pdev->name)) {
 123                error = -EBUSY;
 124                goto err_free_mem;
 125        }
 126
 127        ps2if->base = ioremap(ps2if->iomem_res->start,
 128                              resource_size(ps2if->iomem_res));
 129        if (!ps2if->base) {
 130                error = -ENOMEM;
 131                goto err_free_res;
 132        }
 133
 134        error = request_irq(ps2if->irq, altera_ps2_rxint, 0, pdev->name, ps2if);
 135        if (error) {
 136                dev_err(&pdev->dev, "could not allocate IRQ %d: %d\n",
 137                        ps2if->irq, error);
 138                goto err_unmap;
 139        }
 140
 141        dev_info(&pdev->dev, "base %p, irq %d\n", ps2if->base, ps2if->irq);
 142
 143        serio_register_port(ps2if->io);
 144        platform_set_drvdata(pdev, ps2if);
 145
 146        return 0;
 147
 148 err_unmap:
 149        iounmap(ps2if->base);
 150 err_free_res:
 151        release_mem_region(ps2if->iomem_res->start,
 152                           resource_size(ps2if->iomem_res));
 153 err_free_mem:
 154        kfree(ps2if);
 155        kfree(serio);
 156        return error;
 157}
 158
 159/*
 160 * Remove one device from this driver.
 161 */
 162static int altera_ps2_remove(struct platform_device *pdev)
 163{
 164        struct ps2if *ps2if = platform_get_drvdata(pdev);
 165
 166        platform_set_drvdata(pdev, NULL);
 167        serio_unregister_port(ps2if->io);
 168        free_irq(ps2if->irq, ps2if);
 169        iounmap(ps2if->base);
 170        release_mem_region(ps2if->iomem_res->start,
 171                           resource_size(ps2if->iomem_res));
 172        kfree(ps2if);
 173
 174        return 0;
 175}
 176
 177#ifdef CONFIG_OF
 178static const struct of_device_id altera_ps2_match[] = {
 179        { .compatible = "ALTR,ps2-1.0", },
 180        {},
 181};
 182MODULE_DEVICE_TABLE(of, altera_ps2_match);
 183#endif /* CONFIG_OF */
 184
 185/*
 186 * Our device driver structure
 187 */
 188static struct platform_driver altera_ps2_driver = {
 189        .probe          = altera_ps2_probe,
 190        .remove         = altera_ps2_remove,
 191        .driver = {
 192                .name   = DRV_NAME,
 193                .owner  = THIS_MODULE,
 194                .of_match_table = of_match_ptr(altera_ps2_match),
 195        },
 196};
 197module_platform_driver(altera_ps2_driver);
 198
 199MODULE_DESCRIPTION("Altera University Program PS2 controller driver");
 200MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
 201MODULE_LICENSE("GPL");
 202MODULE_ALIAS("platform:" DRV_NAME);
 203
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.