Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/input/serio/pcips2.c
15111 views
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
41
struct pcips2_data {
42
struct serio *io;
43
unsigned int base;
44
struct pci_dev *dev;
45
};
46
47
static 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
62
static 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
89
static 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
103
static 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
121
static 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
130
static 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
179
static 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
190
static 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
210
static 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
217
static int __init pcips2_init(void)
218
{
219
return pci_register_driver(&pcips2_driver);
220
}
221
222
static void __exit pcips2_exit(void)
223
{
224
pci_unregister_driver(&pcips2_driver);
225
}
226
227
module_init(pcips2_init);
228
module_exit(pcips2_exit);
229
230
MODULE_LICENSE("GPL");
231
MODULE_AUTHOR("Russell King <[email protected]>");
232
MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver");
233
MODULE_DEVICE_TABLE(pci, pcips2_ids);
234
235