linux/drivers/input/serio/pcips2.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/input/serio/pcips2.c
   3 *
   4 *  Copyright (C) 2003 Russell King, All Rights Reserved.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License.
   9 *
  10 *  I'm not sure if this is a generic PS/2 PCI interface or specific to
  11 *  the Mobility Electronics docking station.
  12 */
  13#include <linux/module.h>
  14#include <linux/interrupt.h>
  15#include <linux/ioport.h>
  16#include <linux/input.h>
  17#include <linux/pci.h>
  18#include <linux/slab.h>
  19#include <linux/init.h>
  20#include <linux/serio.h>
  21#include <linux/delay.h>
  22#include <asm/io.h>
  23
  24#define PS2_CTRL                (0)
  25#define PS2_STATUS              (1)
  26#define PS2_DATA                (2)
  27
  28#define PS2_CTRL_CLK            (1<<0)
  29#define PS2_CTRL_DAT            (1<<1)
  30#define PS2_CTRL_TXIRQ          (1<<2)
  31#define PS2_CTRL_ENABLE         (1<<3)
  32#define PS2_CTRL_RXIRQ          (1<<4)
  33
  34#define PS2_STAT_CLK            (1<<0)
  35#define PS2_STAT_DAT            (1<<1)
  36#define PS2_STAT_PARITY         (1<<2)
  37#define PS2_STAT_RXFULL         (1<<5)
  38#define PS2_STAT_TXBUSY         (1<<6)
  39#define PS2_STAT_TXEMPTY        (1<<7)
  40
  41struct pcips2_data {
  42        struct serio    *io;
  43        unsigned int    base;
  44        struct pci_dev  *dev;
  45};
  46
  47static int pcips2_write(struct serio *io, unsigned char val)
  48{
  49        struct pcips2_data *ps2if = io->port_data;
  50        unsigned int stat;
  51
  52        do {
  53                stat = inb(ps2if->base + PS2_STATUS);
  54                cpu_relax();
  55        } while (!(stat & PS2_STAT_TXEMPTY));
  56
  57        outb(val, ps2if->base + PS2_DATA);
  58
  59        return 0;
  60}
  61
  62static irqreturn_t pcips2_interrupt(int irq, void *devid)
  63{
  64        struct pcips2_data *ps2if = devid;
  65        unsigned char status, scancode;
  66        int handled = 0;
  67
  68        do {
  69                unsigned int flag;
  70
  71                status = inb(ps2if->base + PS2_STATUS);
  72                if (!(status & PS2_STAT_RXFULL))
  73                        break;
  74                handled = 1;
  75                scancode = inb(ps2if->base + PS2_DATA);
  76                if (status == 0xff && scancode == 0xff)
  77                        break;
  78
  79                flag = (status & PS2_STAT_PARITY) ? 0 : SERIO_PARITY;
  80
  81                if (hweight8(scancode) & 1)
  82                        flag ^= SERIO_PARITY;
  83
  84                serio_interrupt(ps2if->io, scancode, flag);
  85        } while (1);
  86        return IRQ_RETVAL(handled);
  87}
  88
  89static void pcips2_flush_input(struct pcips2_data *ps2if)
  90{
  91        unsigned char status, scancode;
  92
  93        do {
  94                status = inb(ps2if->base + PS2_STATUS);
  95                if (!(status & PS2_STAT_RXFULL))
  96                        break;
  97                scancode = inb(ps2if->base + PS2_DATA);
  98                if (status == 0xff && scancode == 0xff)
  99                        break;
 100        } while (1);
 101}
 102
 103static int pcips2_open(struct serio *io)
 104{
 105        struct pcips2_data *ps2if = io->port_data;
 106        int ret, val = 0;
 107
 108        outb(PS2_CTRL_ENABLE, ps2if->base);
 109        pcips2_flush_input(ps2if);
 110
 111        ret = request_irq(ps2if->dev->irq, pcips2_interrupt, IRQF_SHARED,
 112                          "pcips2", ps2if);
 113        if (ret == 0)
 114                val = PS2_CTRL_ENABLE | PS2_CTRL_RXIRQ;
 115
 116        outb(val, ps2if->base);
 117
 118        return ret;
 119}
 120
 121static void pcips2_close(struct serio *io)
 122{
 123        struct pcips2_data *ps2if = io->port_data;
 124
 125        outb(0, ps2if->base);
 126
 127        free_irq(ps2if->dev->irq, ps2if);
 128}
 129
 130static int __devinit pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id)
 131{
 132        struct pcips2_data *ps2if;
 133        struct serio *serio;
 134        int ret;
 135
 136        ret = pci_enable_device(dev);
 137        if (ret)
 138                goto out;
 139
 140        ret = pci_request_regions(dev, "pcips2");
 141        if (ret)
 142                goto disable;
 143
 144        ps2if = kzalloc(sizeof(struct pcips2_data), GFP_KERNEL);
 145        serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
 146        if (!ps2if || !serio) {
 147                ret = -ENOMEM;
 148                goto release;
 149        }
 150
 151
 152        serio->id.type          = SERIO_8042;
 153        serio->write            = pcips2_write;
 154        serio->open             = pcips2_open;
 155        serio->close            = pcips2_close;
 156        strlcpy(serio->name, pci_name(dev), sizeof(serio->name));
 157        strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys));
 158        serio->port_data        = ps2if;
 159        serio->dev.parent       = &dev->dev;
 160        ps2if->io               = serio;
 161        ps2if->dev              = dev;
 162        ps2if->base             = pci_resource_start(dev, 0);
 163
 164        pci_set_drvdata(dev, ps2if);
 165
 166        serio_register_port(ps2if->io);
 167        return 0;
 168
 169 release:
 170        kfree(ps2if);
 171        kfree(serio);
 172        pci_release_regions(dev);
 173 disable:
 174        pci_disable_device(dev);
 175 out:
 176        return ret;
 177}
 178
 179static void __devexit pcips2_remove(struct pci_dev *dev)
 180{
 181        struct pcips2_data *ps2if = pci_get_drvdata(dev);
 182
 183        serio_unregister_port(ps2if->io);
 184        pci_set_drvdata(dev, NULL);
 185        kfree(ps2if);
 186        pci_release_regions(dev);
 187        pci_disable_device(dev);
 188}
 189
 190static const struct pci_device_id pcips2_ids[] = {
 191        {
 192                .vendor         = 0x14f2,       /* MOBILITY */
 193                .device         = 0x0123,       /* Keyboard */
 194                .subvendor      = PCI_ANY_ID,
 195                .subdevice      = PCI_ANY_ID,
 196                .class          = PCI_CLASS_INPUT_KEYBOARD << 8,
 197                .class_mask     = 0xffff00,
 198        },
 199        {
 200                .vendor         = 0x14f2,       /* MOBILITY */
 201                .device         = 0x0124,       /* Mouse */
 202                .subvendor      = PCI_ANY_ID,
 203                .subdevice      = PCI_ANY_ID,
 204                .class          = PCI_CLASS_INPUT_MOUSE << 8,
 205                .class_mask     = 0xffff00,
 206        },
 207        { 0, }
 208};
 209
 210static struct pci_driver pcips2_driver = {
 211        .name                   = "pcips2",
 212        .id_table               = pcips2_ids,
 213        .probe                  = pcips2_probe,
 214        .remove                 = __devexit_p(pcips2_remove),
 215};
 216
 217static int __init pcips2_init(void)
 218{
 219        return pci_register_driver(&pcips2_driver);
 220}
 221
 222static void __exit pcips2_exit(void)
 223{
 224        pci_unregister_driver(&pcips2_driver);
 225}
 226
 227module_init(pcips2_init);
 228module_exit(pcips2_exit);
 229
 230MODULE_LICENSE("GPL");
 231MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
 232MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver");
 233MODULE_DEVICE_TABLE(pci, pcips2_ids);
 234
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.