Merge tag 'wireless-drivers-2020-02-08' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / drivers / input / serio / pcips2.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * linux/drivers/input/serio/pcips2.c
4  *
5  *  Copyright (C) 2003 Russell King, All Rights Reserved.
6  *
7  *  I'm not sure if this is a generic PS/2 PCI interface or specific to
8  *  the Mobility Electronics docking station.
9  */
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>
18 #include <asm/io.h>
19
20 #define PS2_CTRL                (0)
21 #define PS2_STATUS              (1)
22 #define PS2_DATA                (2)
23
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)
29
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)
36
37 struct pcips2_data {
38         struct serio    *io;
39         unsigned int    base;
40         struct pci_dev  *dev;
41 };
42
43 static int pcips2_write(struct serio *io, unsigned char val)
44 {
45         struct pcips2_data *ps2if = io->port_data;
46         unsigned int stat;
47
48         do {
49                 stat = inb(ps2if->base + PS2_STATUS);
50                 cpu_relax();
51         } while (!(stat & PS2_STAT_TXEMPTY));
52
53         outb(val, ps2if->base + PS2_DATA);
54
55         return 0;
56 }
57
58 static irqreturn_t pcips2_interrupt(int irq, void *devid)
59 {
60         struct pcips2_data *ps2if = devid;
61         unsigned char status, scancode;
62         int handled = 0;
63
64         do {
65                 unsigned int flag;
66
67                 status = inb(ps2if->base + PS2_STATUS);
68                 if (!(status & PS2_STAT_RXFULL))
69                         break;
70                 handled = 1;
71                 scancode = inb(ps2if->base + PS2_DATA);
72                 if (status == 0xff && scancode == 0xff)
73                         break;
74
75                 flag = (status & PS2_STAT_PARITY) ? 0 : SERIO_PARITY;
76
77                 if (hweight8(scancode) & 1)
78                         flag ^= SERIO_PARITY;
79
80                 serio_interrupt(ps2if->io, scancode, flag);
81         } while (1);
82         return IRQ_RETVAL(handled);
83 }
84
85 static void pcips2_flush_input(struct pcips2_data *ps2if)
86 {
87         unsigned char status, scancode;
88
89         do {
90                 status = inb(ps2if->base + PS2_STATUS);
91                 if (!(status & PS2_STAT_RXFULL))
92                         break;
93                 scancode = inb(ps2if->base + PS2_DATA);
94                 if (status == 0xff && scancode == 0xff)
95                         break;
96         } while (1);
97 }
98
99 static int pcips2_open(struct serio *io)
100 {
101         struct pcips2_data *ps2if = io->port_data;
102         int ret, val = 0;
103
104         outb(PS2_CTRL_ENABLE, ps2if->base);
105         pcips2_flush_input(ps2if);
106
107         ret = request_irq(ps2if->dev->irq, pcips2_interrupt, IRQF_SHARED,
108                           "pcips2", ps2if);
109         if (ret == 0)
110                 val = PS2_CTRL_ENABLE | PS2_CTRL_RXIRQ;
111
112         outb(val, ps2if->base);
113
114         return ret;
115 }
116
117 static void pcips2_close(struct serio *io)
118 {
119         struct pcips2_data *ps2if = io->port_data;
120
121         outb(0, ps2if->base);
122
123         free_irq(ps2if->dev->irq, ps2if);
124 }
125
126 static int pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id)
127 {
128         struct pcips2_data *ps2if;
129         struct serio *serio;
130         int ret;
131
132         ret = pci_enable_device(dev);
133         if (ret)
134                 goto out;
135
136         ret = pci_request_regions(dev, "pcips2");
137         if (ret)
138                 goto disable;
139
140         ps2if = kzalloc(sizeof(struct pcips2_data), GFP_KERNEL);
141         serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
142         if (!ps2if || !serio) {
143                 ret = -ENOMEM;
144                 goto release;
145         }
146
147
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;
156         ps2if->io               = serio;
157         ps2if->dev              = dev;
158         ps2if->base             = pci_resource_start(dev, 0);
159
160         pci_set_drvdata(dev, ps2if);
161
162         serio_register_port(ps2if->io);
163         return 0;
164
165  release:
166         kfree(ps2if);
167         kfree(serio);
168         pci_release_regions(dev);
169  disable:
170         pci_disable_device(dev);
171  out:
172         return ret;
173 }
174
175 static void pcips2_remove(struct pci_dev *dev)
176 {
177         struct pcips2_data *ps2if = pci_get_drvdata(dev);
178
179         serio_unregister_port(ps2if->io);
180         kfree(ps2if);
181         pci_release_regions(dev);
182         pci_disable_device(dev);
183 }
184
185 static const struct pci_device_id pcips2_ids[] = {
186         {
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,
193         },
194         {
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,
201         },
202         { 0, }
203 };
204 MODULE_DEVICE_TABLE(pci, pcips2_ids);
205
206 static struct pci_driver pcips2_driver = {
207         .name                   = "pcips2",
208         .id_table               = pcips2_ids,
209         .probe                  = pcips2_probe,
210         .remove                 = pcips2_remove,
211 };
212
213 module_pci_driver(pcips2_driver);
214
215 MODULE_LICENSE("GPL");
216 MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
217 MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver");