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