Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * linux/drivers/input/serio/pcips2.c
0004  *
0005  *  Copyright (C) 2003 Russell King, All Rights Reserved.
0006  *
0007  *  I'm not sure if this is a generic PS/2 PCI interface or specific to
0008  *  the Mobility Electronics docking station.
0009  */
0010 #include <linux/module.h>
0011 #include <linux/interrupt.h>
0012 #include <linux/ioport.h>
0013 #include <linux/input.h>
0014 #include <linux/pci.h>
0015 #include <linux/slab.h>
0016 #include <linux/serio.h>
0017 #include <linux/delay.h>
0018 #include <asm/io.h>
0019 
0020 #define PS2_CTRL        (0)
0021 #define PS2_STATUS      (1)
0022 #define PS2_DATA        (2)
0023 
0024 #define PS2_CTRL_CLK        (1<<0)
0025 #define PS2_CTRL_DAT        (1<<1)
0026 #define PS2_CTRL_TXIRQ      (1<<2)
0027 #define PS2_CTRL_ENABLE     (1<<3)
0028 #define PS2_CTRL_RXIRQ      (1<<4)
0029 
0030 #define PS2_STAT_CLK        (1<<0)
0031 #define PS2_STAT_DAT        (1<<1)
0032 #define PS2_STAT_PARITY     (1<<2)
0033 #define PS2_STAT_RXFULL     (1<<5)
0034 #define PS2_STAT_TXBUSY     (1<<6)
0035 #define PS2_STAT_TXEMPTY    (1<<7)
0036 
0037 struct pcips2_data {
0038     struct serio    *io;
0039     unsigned int    base;
0040     struct pci_dev  *dev;
0041 };
0042 
0043 static int pcips2_write(struct serio *io, unsigned char val)
0044 {
0045     struct pcips2_data *ps2if = io->port_data;
0046     unsigned int stat;
0047 
0048     do {
0049         stat = inb(ps2if->base + PS2_STATUS);
0050         cpu_relax();
0051     } while (!(stat & PS2_STAT_TXEMPTY));
0052 
0053     outb(val, ps2if->base + PS2_DATA);
0054 
0055     return 0;
0056 }
0057 
0058 static irqreturn_t pcips2_interrupt(int irq, void *devid)
0059 {
0060     struct pcips2_data *ps2if = devid;
0061     unsigned char status, scancode;
0062     int handled = 0;
0063 
0064     do {
0065         unsigned int flag;
0066 
0067         status = inb(ps2if->base + PS2_STATUS);
0068         if (!(status & PS2_STAT_RXFULL))
0069             break;
0070         handled = 1;
0071         scancode = inb(ps2if->base + PS2_DATA);
0072         if (status == 0xff && scancode == 0xff)
0073             break;
0074 
0075         flag = (status & PS2_STAT_PARITY) ? 0 : SERIO_PARITY;
0076 
0077         if (hweight8(scancode) & 1)
0078             flag ^= SERIO_PARITY;
0079 
0080         serio_interrupt(ps2if->io, scancode, flag);
0081     } while (1);
0082     return IRQ_RETVAL(handled);
0083 }
0084 
0085 static void pcips2_flush_input(struct pcips2_data *ps2if)
0086 {
0087     unsigned char status, scancode;
0088 
0089     do {
0090         status = inb(ps2if->base + PS2_STATUS);
0091         if (!(status & PS2_STAT_RXFULL))
0092             break;
0093         scancode = inb(ps2if->base + PS2_DATA);
0094         if (status == 0xff && scancode == 0xff)
0095             break;
0096     } while (1);
0097 }
0098 
0099 static int pcips2_open(struct serio *io)
0100 {
0101     struct pcips2_data *ps2if = io->port_data;
0102     int ret, val = 0;
0103 
0104     outb(PS2_CTRL_ENABLE, ps2if->base);
0105     pcips2_flush_input(ps2if);
0106 
0107     ret = request_irq(ps2if->dev->irq, pcips2_interrupt, IRQF_SHARED,
0108               "pcips2", ps2if);
0109     if (ret == 0)
0110         val = PS2_CTRL_ENABLE | PS2_CTRL_RXIRQ;
0111 
0112     outb(val, ps2if->base);
0113 
0114     return ret;
0115 }
0116 
0117 static void pcips2_close(struct serio *io)
0118 {
0119     struct pcips2_data *ps2if = io->port_data;
0120 
0121     outb(0, ps2if->base);
0122 
0123     free_irq(ps2if->dev->irq, ps2if);
0124 }
0125 
0126 static int pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id)
0127 {
0128     struct pcips2_data *ps2if;
0129     struct serio *serio;
0130     int ret;
0131 
0132     ret = pci_enable_device(dev);
0133     if (ret)
0134         goto out;
0135 
0136     ret = pci_request_regions(dev, "pcips2");
0137     if (ret)
0138         goto disable;
0139 
0140     ps2if = kzalloc(sizeof(struct pcips2_data), GFP_KERNEL);
0141     serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
0142     if (!ps2if || !serio) {
0143         ret = -ENOMEM;
0144         goto release;
0145     }
0146 
0147 
0148     serio->id.type      = SERIO_8042;
0149     serio->write        = pcips2_write;
0150     serio->open     = pcips2_open;
0151     serio->close        = pcips2_close;
0152     strlcpy(serio->name, pci_name(dev), sizeof(serio->name));
0153     strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys));
0154     serio->port_data    = ps2if;
0155     serio->dev.parent   = &dev->dev;
0156     ps2if->io       = serio;
0157     ps2if->dev      = dev;
0158     ps2if->base     = pci_resource_start(dev, 0);
0159 
0160     pci_set_drvdata(dev, ps2if);
0161 
0162     serio_register_port(ps2if->io);
0163     return 0;
0164 
0165  release:
0166     kfree(ps2if);
0167     kfree(serio);
0168     pci_release_regions(dev);
0169  disable:
0170     pci_disable_device(dev);
0171  out:
0172     return ret;
0173 }
0174 
0175 static void pcips2_remove(struct pci_dev *dev)
0176 {
0177     struct pcips2_data *ps2if = pci_get_drvdata(dev);
0178 
0179     serio_unregister_port(ps2if->io);
0180     kfree(ps2if);
0181     pci_release_regions(dev);
0182     pci_disable_device(dev);
0183 }
0184 
0185 static const struct pci_device_id pcips2_ids[] = {
0186     {
0187         .vendor     = 0x14f2,   /* MOBILITY */
0188         .device     = 0x0123,   /* Keyboard */
0189         .subvendor  = PCI_ANY_ID,
0190         .subdevice  = PCI_ANY_ID,
0191         .class      = PCI_CLASS_INPUT_KEYBOARD << 8,
0192         .class_mask = 0xffff00,
0193     },
0194     {
0195         .vendor     = 0x14f2,   /* MOBILITY */
0196         .device     = 0x0124,   /* Mouse */
0197         .subvendor  = PCI_ANY_ID,
0198         .subdevice  = PCI_ANY_ID,
0199         .class      = PCI_CLASS_INPUT_MOUSE << 8,
0200         .class_mask = 0xffff00,
0201     },
0202     { 0, }
0203 };
0204 MODULE_DEVICE_TABLE(pci, pcips2_ids);
0205 
0206 static struct pci_driver pcips2_driver = {
0207     .name           = "pcips2",
0208     .id_table       = pcips2_ids,
0209     .probe          = pcips2_probe,
0210     .remove         = pcips2_remove,
0211 };
0212 
0213 module_pci_driver(pcips2_driver);
0214 
0215 MODULE_LICENSE("GPL");
0216 MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
0217 MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver");