1 // SPDX-License-Identifier: GPL-2.0-only
3 * linux/drivers/input/serio/pcips2.c
5 * Copyright (C) 2003 Russell King, All Rights Reserved.
7 * I'm not sure if this is a generic PS/2 PCI interface or specific to
8 * the Mobility Electronics docking station.
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>
21 #define PS2_STATUS (1)
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)
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)
43 static int pcips2_write(struct serio
*io
, unsigned char val
)
45 struct pcips2_data
*ps2if
= io
->port_data
;
49 stat
= inb(ps2if
->base
+ PS2_STATUS
);
51 } while (!(stat
& PS2_STAT_TXEMPTY
));
53 outb(val
, ps2if
->base
+ PS2_DATA
);
58 static irqreturn_t
pcips2_interrupt(int irq
, void *devid
)
60 struct pcips2_data
*ps2if
= devid
;
61 unsigned char status
, scancode
;
67 status
= inb(ps2if
->base
+ PS2_STATUS
);
68 if (!(status
& PS2_STAT_RXFULL
))
71 scancode
= inb(ps2if
->base
+ PS2_DATA
);
72 if (status
== 0xff && scancode
== 0xff)
75 flag
= (status
& PS2_STAT_PARITY
) ? 0 : SERIO_PARITY
;
77 if (hweight8(scancode
) & 1)
80 serio_interrupt(ps2if
->io
, scancode
, flag
);
82 return IRQ_RETVAL(handled
);
85 static void pcips2_flush_input(struct pcips2_data
*ps2if
)
87 unsigned char status
, scancode
;
90 status
= inb(ps2if
->base
+ PS2_STATUS
);
91 if (!(status
& PS2_STAT_RXFULL
))
93 scancode
= inb(ps2if
->base
+ PS2_DATA
);
94 if (status
== 0xff && scancode
== 0xff)
99 static int pcips2_open(struct serio
*io
)
101 struct pcips2_data
*ps2if
= io
->port_data
;
104 outb(PS2_CTRL_ENABLE
, ps2if
->base
);
105 pcips2_flush_input(ps2if
);
107 ret
= request_irq(ps2if
->dev
->irq
, pcips2_interrupt
, IRQF_SHARED
,
110 val
= PS2_CTRL_ENABLE
| PS2_CTRL_RXIRQ
;
112 outb(val
, ps2if
->base
);
117 static void pcips2_close(struct serio
*io
)
119 struct pcips2_data
*ps2if
= io
->port_data
;
121 outb(0, ps2if
->base
);
123 free_irq(ps2if
->dev
->irq
, ps2if
);
126 static int pcips2_probe(struct pci_dev
*dev
, const struct pci_device_id
*id
)
128 struct pcips2_data
*ps2if
;
132 ret
= pci_enable_device(dev
);
136 ret
= pci_request_regions(dev
, "pcips2");
140 ps2if
= kzalloc(sizeof(struct pcips2_data
), GFP_KERNEL
);
141 serio
= kzalloc(sizeof(struct serio
), GFP_KERNEL
);
142 if (!ps2if
|| !serio
) {
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
;
158 ps2if
->base
= pci_resource_start(dev
, 0);
160 pci_set_drvdata(dev
, ps2if
);
162 serio_register_port(ps2if
->io
);
168 pci_release_regions(dev
);
170 pci_disable_device(dev
);
175 static void pcips2_remove(struct pci_dev
*dev
)
177 struct pcips2_data
*ps2if
= pci_get_drvdata(dev
);
179 serio_unregister_port(ps2if
->io
);
181 pci_release_regions(dev
);
182 pci_disable_device(dev
);
185 static const struct pci_device_id pcips2_ids
[] = {
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,
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,
204 MODULE_DEVICE_TABLE(pci
, pcips2_ids
);
206 static struct pci_driver pcips2_driver
= {
208 .id_table
= pcips2_ids
,
209 .probe
= pcips2_probe
,
210 .remove
= pcips2_remove
,
213 module_pci_driver(pcips2_driver
);
215 MODULE_LICENSE("GPL");
216 MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
217 MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver");