ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / input / serio / pcips2.c
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/init.h>
19 #include <linux/serio.h>
20 #include <linux/delay.h>
21 #include <asm/io.h>
22
23 #define PS2_CTRL                (0)
24 #define PS2_STATUS              (1)
25 #define PS2_DATA                (2)
26
27 #define PS2_CTRL_CLK            (1<<0)
28 #define PS2_CTRL_DAT            (1<<1)
29 #define PS2_CTRL_TXIRQ          (1<<2)
30 #define PS2_CTRL_ENABLE         (1<<3)
31 #define PS2_CTRL_RXIRQ          (1<<4)
32
33 #define PS2_STAT_CLK            (1<<0)
34 #define PS2_STAT_DAT            (1<<1)
35 #define PS2_STAT_PARITY         (1<<2)
36 #define PS2_STAT_RXFULL         (1<<5)
37 #define PS2_STAT_TXBUSY         (1<<6)
38 #define PS2_STAT_TXEMPTY        (1<<7)
39
40 struct pcips2_data {
41         struct serio    io;
42         unsigned int    base;
43         struct pci_dev  *dev;
44 };
45
46 static int pcips2_write(struct serio *io, unsigned char val)
47 {
48         struct pcips2_data *ps2if = io->driver;
49         unsigned int stat;
50
51         do {
52                 stat = inb(ps2if->base + PS2_STATUS);
53                 cpu_relax();
54         } while (!(stat & PS2_STAT_TXEMPTY));
55
56         outb(val, ps2if->base + PS2_DATA);
57
58         return 0;
59 }
60
61 static irqreturn_t pcips2_interrupt(int irq, void *devid, struct pt_regs *regs)
62 {
63         struct pcips2_data *ps2if = devid;
64         unsigned char status, scancode;
65         int handled = 0;
66
67         do {
68                 unsigned int flag;
69
70                 status = inb(ps2if->base + PS2_STATUS);
71                 if (!(status & PS2_STAT_RXFULL))
72                         break;
73                 handled = 1;
74                 scancode = inb(ps2if->base + PS2_DATA);
75                 if (status == 0xff && scancode == 0xff)
76                         break;
77
78                 flag = (status & PS2_STAT_PARITY) ? 0 : SERIO_PARITY;
79
80                 if (hweight8(scancode) & 1)
81                         flag ^= SERIO_PARITY;
82
83                 serio_interrupt(&ps2if->io, scancode, flag, regs);
84         } while (1);
85         return IRQ_RETVAL(handled);
86 }
87
88 static void pcips2_flush_input(struct pcips2_data *ps2if)
89 {
90         unsigned char status, scancode;
91
92         do {
93                 status = inb(ps2if->base + PS2_STATUS);
94                 if (!(status & PS2_STAT_RXFULL))
95                         break;
96                 scancode = inb(ps2if->base + PS2_DATA);
97                 if (status == 0xff && scancode == 0xff)
98                         break;
99         } while (1);
100 }
101
102 static int pcips2_open(struct serio *io)
103 {
104         struct pcips2_data *ps2if = io->driver;
105         int ret, val = 0;
106
107         outb(PS2_CTRL_ENABLE, ps2if->base);
108         pcips2_flush_input(ps2if);
109
110         ret = request_irq(ps2if->dev->irq, pcips2_interrupt, SA_SHIRQ,
111                           "pcips2", ps2if);
112         if (ret == 0)
113                 val = PS2_CTRL_ENABLE | PS2_CTRL_RXIRQ;
114
115         outb(val, ps2if->base);
116
117         return ret;
118 }
119
120 static void pcips2_close(struct serio *io)
121 {
122         struct pcips2_data *ps2if = io->driver;
123
124         outb(0, ps2if->base);
125
126         free_irq(ps2if->dev->irq, ps2if);
127 }
128
129 static int __devinit pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id)
130 {
131         struct pcips2_data *ps2if;
132         int ret;
133
134         ret = pci_enable_device(dev);
135         if (ret)
136                 return ret;
137
138         if (!request_region(pci_resource_start(dev, 0),
139                             pci_resource_len(dev, 0), "pcips2")) {
140                 ret = -EBUSY;
141                 goto disable;
142         }
143
144         ps2if = kmalloc(sizeof(struct pcips2_data), GFP_KERNEL);
145         if (!ps2if) {
146                 ret = -ENOMEM;
147                 goto release;
148         }
149
150         memset(ps2if, 0, sizeof(struct pcips2_data));
151
152         ps2if->io.type          = SERIO_8042;
153         ps2if->io.write         = pcips2_write;
154         ps2if->io.open          = pcips2_open;
155         ps2if->io.close         = pcips2_close;
156         ps2if->io.name          = pci_name(dev);
157         ps2if->io.phys          = dev->dev.bus_id;
158         ps2if->io.driver        = ps2if;
159         ps2if->dev              = dev;
160         ps2if->base             = pci_resource_start(dev, 0);
161
162         pci_set_drvdata(dev, ps2if);
163
164         serio_register_port(&ps2if->io);
165         return 0;
166
167  release:
168         release_region(pci_resource_start(dev, 0),
169                        pci_resource_len(dev, 0));
170  disable:
171         pci_disable_device(dev);
172         return ret;
173 }
174
175 static void __devexit pcips2_remove(struct pci_dev *dev)
176 {
177         struct pcips2_data *ps2if = pci_get_drvdata(dev);
178
179         serio_unregister_port(&ps2if->io);
180         release_region(pci_resource_start(dev, 0),
181                        pci_resource_len(dev, 0));
182         pci_set_drvdata(dev, NULL);
183         kfree(ps2if);
184         pci_disable_device(dev);
185 }
186
187 static struct pci_device_id pcips2_ids[] = {
188         {
189                 .vendor         = 0x14f2,       /* MOBILITY */
190                 .device         = 0x0123,       /* Keyboard */
191                 .subvendor      = PCI_ANY_ID,
192                 .subdevice      = PCI_ANY_ID,
193                 .class          = PCI_CLASS_INPUT_KEYBOARD << 8,
194                 .class_mask     = 0xffff00,
195         },
196         {
197                 .vendor         = 0x14f2,       /* MOBILITY */
198                 .device         = 0x0124,       /* Mouse */
199                 .subvendor      = PCI_ANY_ID,
200                 .subdevice      = PCI_ANY_ID,
201                 .class          = PCI_CLASS_INPUT_MOUSE << 8,
202                 .class_mask     = 0xffff00,
203         },
204         { 0, }
205 };
206
207 static struct pci_driver pcips2_driver = {
208         .name                   = "pcips2",
209         .id_table               = pcips2_ids,
210         .probe                  = pcips2_probe,
211         .remove                 = __devexit_p(pcips2_remove),
212 };
213
214 static int __init pcips2_init(void)
215 {
216         return pci_module_init(&pcips2_driver);
217 }
218
219 static void __exit pcips2_exit(void)
220 {
221         pci_unregister_driver(&pcips2_driver);
222 }
223
224 module_init(pcips2_init);
225 module_exit(pcips2_exit);
226
227 MODULE_LICENSE("GPL");
228 MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
229 MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver");
230 MODULE_DEVICE_TABLE(pci, pcips2_ids);