blob: c52e3e589f7283bef15c502c9e14fb3625ad2d43 [file] [log] [blame]
Mischa Jonkere4b29002012-10-22 22:48:08 -07001/*
2 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * Driver is originally developed by Pavel Sokolov <psokolov@synopsys.com>
9 */
10
Thierry Reding8d1cbc92013-01-21 11:09:05 +010011#include <linux/err.h>
Mischa Jonkere4b29002012-10-22 22:48:08 -070012#include <linux/module.h>
13#include <linux/interrupt.h>
14#include <linux/input.h>
15#include <linux/serio.h>
16#include <linux/platform_device.h>
17#include <linux/io.h>
18#include <linux/kernel.h>
19#include <linux/slab.h>
20
21#define ARC_PS2_PORTS 2
22
23#define ARC_ARC_PS2_ID 0x0001f609
24
25#define STAT_TIMEOUT 128
26
27#define PS2_STAT_RX_FRM_ERR (1)
28#define PS2_STAT_RX_BUF_OVER (1 << 1)
29#define PS2_STAT_RX_INT_EN (1 << 2)
30#define PS2_STAT_RX_VAL (1 << 3)
31#define PS2_STAT_TX_ISNOT_FUL (1 << 4)
32#define PS2_STAT_TX_INT_EN (1 << 5)
33
34struct arc_ps2_port {
35 void __iomem *data_addr;
36 void __iomem *status_addr;
37 struct serio *io;
38};
39
40struct arc_ps2_data {
41 struct arc_ps2_port port[ARC_PS2_PORTS];
Mischa Jonkere4b29002012-10-22 22:48:08 -070042 void __iomem *addr;
43 unsigned int frame_error;
44 unsigned int buf_overflow;
45 unsigned int total_int;
46};
47
48static void arc_ps2_check_rx(struct arc_ps2_data *arc_ps2,
49 struct arc_ps2_port *port)
50{
51 unsigned int timeout = 1000;
52 unsigned int flag, status;
53 unsigned char data;
54
55 do {
56 status = ioread32(port->status_addr);
57 if (!(status & PS2_STAT_RX_VAL))
58 return;
59
60 data = ioread32(port->data_addr) & 0xff;
61
62 flag = 0;
63 arc_ps2->total_int++;
64 if (status & PS2_STAT_RX_FRM_ERR) {
65 arc_ps2->frame_error++;
66 flag |= SERIO_PARITY;
67 } else if (status & PS2_STAT_RX_BUF_OVER) {
68 arc_ps2->buf_overflow++;
69 flag |= SERIO_FRAME;
70 }
71
72 serio_interrupt(port->io, data, flag);
73 } while (--timeout);
74
75 dev_err(&port->io->dev, "PS/2 hardware stuck\n");
76}
77
78static irqreturn_t arc_ps2_interrupt(int irq, void *dev)
79{
80 struct arc_ps2_data *arc_ps2 = dev;
81 int i;
82
83 for (i = 0; i < ARC_PS2_PORTS; i++)
84 arc_ps2_check_rx(arc_ps2, &arc_ps2->port[i]);
85
86 return IRQ_HANDLED;
87}
88
89static int arc_ps2_write(struct serio *io, unsigned char val)
90{
91 unsigned status;
92 struct arc_ps2_port *port = io->port_data;
93 int timeout = STAT_TIMEOUT;
94
95 do {
96 status = ioread32(port->status_addr);
97 cpu_relax();
98
99 if (status & PS2_STAT_TX_ISNOT_FUL) {
100 iowrite32(val & 0xff, port->data_addr);
101 return 0;
102 }
103
104 } while (--timeout);
105
106 dev_err(&io->dev, "write timeout\n");
107 return -ETIMEDOUT;
108}
109
110static int arc_ps2_open(struct serio *io)
111{
112 struct arc_ps2_port *port = io->port_data;
113
114 iowrite32(PS2_STAT_RX_INT_EN, port->status_addr);
115
116 return 0;
117}
118
119static void arc_ps2_close(struct serio *io)
120{
121 struct arc_ps2_port *port = io->port_data;
122
123 iowrite32(ioread32(port->status_addr) & ~PS2_STAT_RX_INT_EN,
124 port->status_addr);
125}
126
Bill Pemberton5298cc42012-11-23 21:38:25 -0800127static void __iomem *arc_ps2_calc_addr(struct arc_ps2_data *arc_ps2,
Dmitry Torokhova4d69832012-10-24 23:53:01 -0700128 int index, bool status)
129{
130 void __iomem *addr;
131
132 addr = arc_ps2->addr + 4 + 4 * index;
133 if (status)
134 addr += ARC_PS2_PORTS * 4;
135
136 return addr;
137}
138
Bill Pemberton5298cc42012-11-23 21:38:25 -0800139static void arc_ps2_inhibit_ports(struct arc_ps2_data *arc_ps2)
Dmitry Torokhova4d69832012-10-24 23:53:01 -0700140{
141 void __iomem *addr;
142 u32 val;
143 int i;
144
145 for (i = 0; i < ARC_PS2_PORTS; i++) {
146 addr = arc_ps2_calc_addr(arc_ps2, i, true);
147 val = ioread32(addr);
148 val &= ~(PS2_STAT_RX_INT_EN | PS2_STAT_TX_INT_EN);
149 iowrite32(val, addr);
150 }
151}
152
Bill Pemberton5298cc42012-11-23 21:38:25 -0800153static int arc_ps2_create_port(struct platform_device *pdev,
Mischa Jonkere4b29002012-10-22 22:48:08 -0700154 struct arc_ps2_data *arc_ps2,
155 int index)
156{
157 struct arc_ps2_port *port = &arc_ps2->port[index];
158 struct serio *io;
159
160 io = kzalloc(sizeof(struct serio), GFP_KERNEL);
161 if (!io)
162 return -ENOMEM;
163
164 io->id.type = SERIO_8042;
165 io->write = arc_ps2_write;
166 io->open = arc_ps2_open;
167 io->close = arc_ps2_close;
168 snprintf(io->name, sizeof(io->name), "ARC PS/2 port%d", index);
169 snprintf(io->phys, sizeof(io->phys), "arc/serio%d", index);
170 io->port_data = port;
171
172 port->io = io;
173
Dmitry Torokhova4d69832012-10-24 23:53:01 -0700174 port->data_addr = arc_ps2_calc_addr(arc_ps2, index, false);
175 port->status_addr = arc_ps2_calc_addr(arc_ps2, index, true);
Mischa Jonkere4b29002012-10-22 22:48:08 -0700176
177 dev_dbg(&pdev->dev, "port%d is allocated (data = 0x%p, status = 0x%p)\n",
178 index, port->data_addr, port->status_addr);
179
180 serio_register_port(port->io);
181 return 0;
182}
183
Bill Pemberton5298cc42012-11-23 21:38:25 -0800184static int arc_ps2_probe(struct platform_device *pdev)
Mischa Jonkere4b29002012-10-22 22:48:08 -0700185{
186 struct arc_ps2_data *arc_ps2;
Dmitry Torokhova4d69832012-10-24 23:53:01 -0700187 struct resource *res;
188 int irq;
Mischa Jonkere4b29002012-10-22 22:48:08 -0700189 int error, id, i;
190
Dmitry Torokhova4d69832012-10-24 23:53:01 -0700191 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
192 if (!res) {
193 dev_err(&pdev->dev, "no IO memory defined\n");
194 return -EINVAL;
195 }
196
197 irq = platform_get_irq_byname(pdev, "arc_ps2_irq");
198 if (irq < 0) {
199 dev_err(&pdev->dev, "no IRQ defined\n");
200 return -EINVAL;
201 }
202
203 arc_ps2 = devm_kzalloc(&pdev->dev, sizeof(struct arc_ps2_data),
204 GFP_KERNEL);
Mischa Jonkere4b29002012-10-22 22:48:08 -0700205 if (!arc_ps2) {
206 dev_err(&pdev->dev, "out of memory\n");
207 return -ENOMEM;
208 }
209
Thierry Reding8d1cbc92013-01-21 11:09:05 +0100210 arc_ps2->addr = devm_ioremap_resource(&pdev->dev, res);
211 if (IS_ERR(arc_ps2->addr))
212 return PTR_ERR(arc_ps2->addr);
Mischa Jonkere4b29002012-10-22 22:48:08 -0700213
214 dev_info(&pdev->dev, "irq = %d, address = 0x%p, ports = %i\n",
Dmitry Torokhova4d69832012-10-24 23:53:01 -0700215 irq, arc_ps2->addr, ARC_PS2_PORTS);
Mischa Jonkere4b29002012-10-22 22:48:08 -0700216
217 id = ioread32(arc_ps2->addr);
218 if (id != ARC_ARC_PS2_ID) {
219 dev_err(&pdev->dev, "device id does not match\n");
Dmitry Torokhova4d69832012-10-24 23:53:01 -0700220 return -ENXIO;
221 }
222
223 arc_ps2_inhibit_ports(arc_ps2);
224
225 error = devm_request_irq(&pdev->dev, irq, arc_ps2_interrupt,
226 0, "arc_ps2", arc_ps2);
227 if (error) {
228 dev_err(&pdev->dev, "Could not allocate IRQ\n");
229 return error;
Mischa Jonkere4b29002012-10-22 22:48:08 -0700230 }
231
232 for (i = 0; i < ARC_PS2_PORTS; i++) {
233 error = arc_ps2_create_port(pdev, arc_ps2, i);
Dmitry Torokhova4d69832012-10-24 23:53:01 -0700234 if (error) {
235 while (--i >= 0)
236 serio_unregister_port(arc_ps2->port[i].io);
237 return error;
238 }
Mischa Jonkere4b29002012-10-22 22:48:08 -0700239 }
240
241 platform_set_drvdata(pdev, arc_ps2);
242
243 return 0;
Mischa Jonkere4b29002012-10-22 22:48:08 -0700244}
245
Bill Pembertone2619cf2012-11-23 21:50:47 -0800246static int arc_ps2_remove(struct platform_device *pdev)
Mischa Jonkere4b29002012-10-22 22:48:08 -0700247{
248 struct arc_ps2_data *arc_ps2 = platform_get_drvdata(pdev);
249 int i;
250
251 for (i = 0; i < ARC_PS2_PORTS; i++)
252 serio_unregister_port(arc_ps2->port[i].io);
253
Mischa Jonkere4b29002012-10-22 22:48:08 -0700254 dev_dbg(&pdev->dev, "interrupt count = %i\n", arc_ps2->total_int);
255 dev_dbg(&pdev->dev, "frame error count = %i\n", arc_ps2->frame_error);
256 dev_dbg(&pdev->dev, "buffer overflow count = %i\n",
257 arc_ps2->buf_overflow);
258
Mischa Jonkere4b29002012-10-22 22:48:08 -0700259 return 0;
260}
261
262static struct platform_driver arc_ps2_driver = {
263 .driver = {
264 .name = "arc_ps2",
265 .owner = THIS_MODULE,
266 },
267 .probe = arc_ps2_probe,
Bill Pemberton1cb0aa82012-11-23 21:27:39 -0800268 .remove = arc_ps2_remove,
Mischa Jonkere4b29002012-10-22 22:48:08 -0700269};
270
271module_platform_driver(arc_ps2_driver);
272
273MODULE_LICENSE("GPL");
274MODULE_AUTHOR("Pavel Sokolov <psokolov@synopsys.com>");
275MODULE_DESCRIPTION("ARC PS/2 Driver");