Merge ../linux-2.6-watchdog-mm
[linux-drm-fsl-dcu.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->port_data;
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)
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);
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->port_data;
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, IRQF_SHARED,
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->port_data;
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         struct serio *serio;
133         int ret;
134
135         ret = pci_enable_device(dev);
136         if (ret)
137                 goto out;
138
139         ret = pci_request_regions(dev, "pcips2");
140         if (ret)
141                 goto disable;
142
143         ps2if = kmalloc(sizeof(struct pcips2_data), GFP_KERNEL);
144         serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
145         if (!ps2if || !serio) {
146                 ret = -ENOMEM;
147                 goto release;
148         }
149
150         memset(ps2if, 0, sizeof(struct pcips2_data));
151         memset(serio, 0, sizeof(struct serio));
152
153         serio->id.type          = SERIO_8042;
154         serio->write            = pcips2_write;
155         serio->open             = pcips2_open;
156         serio->close            = pcips2_close;
157         strlcpy(serio->name, pci_name(dev), sizeof(serio->name));
158         strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys));
159         serio->port_data        = ps2if;
160         serio->dev.parent       = &dev->dev;
161         ps2if->io               = serio;
162         ps2if->dev              = dev;
163         ps2if->base             = pci_resource_start(dev, 0);
164
165         pci_set_drvdata(dev, ps2if);
166
167         serio_register_port(ps2if->io);
168         return 0;
169
170  release:
171         kfree(ps2if);
172         kfree(serio);
173         pci_release_regions(dev);
174  disable:
175         pci_disable_device(dev);
176  out:
177         return ret;
178 }
179
180 static void __devexit pcips2_remove(struct pci_dev *dev)
181 {
182         struct pcips2_data *ps2if = pci_get_drvdata(dev);
183
184         serio_unregister_port(ps2if->io);
185         pci_set_drvdata(dev, NULL);
186         kfree(ps2if);
187         pci_release_regions(dev);
188         pci_disable_device(dev);
189 }
190
191 static struct pci_device_id pcips2_ids[] = {
192         {
193                 .vendor         = 0x14f2,       /* MOBILITY */
194                 .device         = 0x0123,       /* Keyboard */
195                 .subvendor      = PCI_ANY_ID,
196                 .subdevice      = PCI_ANY_ID,
197                 .class          = PCI_CLASS_INPUT_KEYBOARD << 8,
198                 .class_mask     = 0xffff00,
199         },
200         {
201                 .vendor         = 0x14f2,       /* MOBILITY */
202                 .device         = 0x0124,       /* Mouse */
203                 .subvendor      = PCI_ANY_ID,
204                 .subdevice      = PCI_ANY_ID,
205                 .class          = PCI_CLASS_INPUT_MOUSE << 8,
206                 .class_mask     = 0xffff00,
207         },
208         { 0, }
209 };
210
211 static struct pci_driver pcips2_driver = {
212         .name                   = "pcips2",
213         .id_table               = pcips2_ids,
214         .probe                  = pcips2_probe,
215         .remove                 = __devexit_p(pcips2_remove),
216 };
217
218 static int __init pcips2_init(void)
219 {
220         return pci_register_driver(&pcips2_driver);
221 }
222
223 static void __exit pcips2_exit(void)
224 {
225         pci_unregister_driver(&pcips2_driver);
226 }
227
228 module_init(pcips2_init);
229 module_exit(pcips2_exit);
230
231 MODULE_LICENSE("GPL");
232 MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
233 MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver");
234 MODULE_DEVICE_TABLE(pci, pcips2_ids);