1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) |
4 | * |
5 | * Driver is originally developed by Pavel Sokolov <psokolov@synopsys.com> |
6 | */ |
7 | |
8 | #include <linux/err.h> |
9 | #include <linux/module.h> |
10 | #include <linux/interrupt.h> |
11 | #include <linux/input.h> |
12 | #include <linux/serio.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/of.h> |
15 | #include <linux/io.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/slab.h> |
18 | |
19 | #define ARC_PS2_PORTS 2 |
20 | |
21 | #define ARC_ARC_PS2_ID 0x0001f609 |
22 | |
23 | #define STAT_TIMEOUT 128 |
24 | |
25 | #define PS2_STAT_RX_FRM_ERR (1) |
26 | #define PS2_STAT_RX_BUF_OVER (1 << 1) |
27 | #define PS2_STAT_RX_INT_EN (1 << 2) |
28 | #define PS2_STAT_RX_VAL (1 << 3) |
29 | #define PS2_STAT_TX_ISNOT_FUL (1 << 4) |
30 | #define PS2_STAT_TX_INT_EN (1 << 5) |
31 | |
32 | struct arc_ps2_port { |
33 | void __iomem *data_addr; |
34 | void __iomem *status_addr; |
35 | struct serio *io; |
36 | }; |
37 | |
38 | struct arc_ps2_data { |
39 | struct arc_ps2_port port[ARC_PS2_PORTS]; |
40 | void __iomem *addr; |
41 | unsigned int frame_error; |
42 | unsigned int buf_overflow; |
43 | unsigned int total_int; |
44 | }; |
45 | |
46 | static void arc_ps2_check_rx(struct arc_ps2_data *arc_ps2, |
47 | struct arc_ps2_port *port) |
48 | { |
49 | unsigned int timeout = 1000; |
50 | unsigned int flag, status; |
51 | unsigned char data; |
52 | |
53 | do { |
54 | status = ioread32(port->status_addr); |
55 | if (!(status & PS2_STAT_RX_VAL)) |
56 | return; |
57 | |
58 | data = ioread32(port->data_addr) & 0xff; |
59 | |
60 | flag = 0; |
61 | arc_ps2->total_int++; |
62 | if (status & PS2_STAT_RX_FRM_ERR) { |
63 | arc_ps2->frame_error++; |
64 | flag |= SERIO_PARITY; |
65 | } else if (status & PS2_STAT_RX_BUF_OVER) { |
66 | arc_ps2->buf_overflow++; |
67 | flag |= SERIO_FRAME; |
68 | } |
69 | |
70 | serio_interrupt(serio: port->io, data, flags: flag); |
71 | } while (--timeout); |
72 | |
73 | dev_err(&port->io->dev, "PS/2 hardware stuck\n" ); |
74 | } |
75 | |
76 | static irqreturn_t arc_ps2_interrupt(int irq, void *dev) |
77 | { |
78 | struct arc_ps2_data *arc_ps2 = dev; |
79 | int i; |
80 | |
81 | for (i = 0; i < ARC_PS2_PORTS; i++) |
82 | arc_ps2_check_rx(arc_ps2, port: &arc_ps2->port[i]); |
83 | |
84 | return IRQ_HANDLED; |
85 | } |
86 | |
87 | static int arc_ps2_write(struct serio *io, unsigned char val) |
88 | { |
89 | unsigned status; |
90 | struct arc_ps2_port *port = io->port_data; |
91 | int timeout = STAT_TIMEOUT; |
92 | |
93 | do { |
94 | status = ioread32(port->status_addr); |
95 | cpu_relax(); |
96 | |
97 | if (status & PS2_STAT_TX_ISNOT_FUL) { |
98 | iowrite32(val & 0xff, port->data_addr); |
99 | return 0; |
100 | } |
101 | |
102 | } while (--timeout); |
103 | |
104 | dev_err(&io->dev, "write timeout\n" ); |
105 | return -ETIMEDOUT; |
106 | } |
107 | |
108 | static int arc_ps2_open(struct serio *io) |
109 | { |
110 | struct arc_ps2_port *port = io->port_data; |
111 | |
112 | iowrite32(PS2_STAT_RX_INT_EN, port->status_addr); |
113 | |
114 | return 0; |
115 | } |
116 | |
117 | static void arc_ps2_close(struct serio *io) |
118 | { |
119 | struct arc_ps2_port *port = io->port_data; |
120 | |
121 | iowrite32(ioread32(port->status_addr) & ~PS2_STAT_RX_INT_EN, |
122 | port->status_addr); |
123 | } |
124 | |
125 | static void __iomem *arc_ps2_calc_addr(struct arc_ps2_data *arc_ps2, |
126 | int index, bool status) |
127 | { |
128 | void __iomem *addr; |
129 | |
130 | addr = arc_ps2->addr + 4 + 4 * index; |
131 | if (status) |
132 | addr += ARC_PS2_PORTS * 4; |
133 | |
134 | return addr; |
135 | } |
136 | |
137 | static void arc_ps2_inhibit_ports(struct arc_ps2_data *arc_ps2) |
138 | { |
139 | void __iomem *addr; |
140 | u32 val; |
141 | int i; |
142 | |
143 | for (i = 0; i < ARC_PS2_PORTS; i++) { |
144 | addr = arc_ps2_calc_addr(arc_ps2, index: i, status: true); |
145 | val = ioread32(addr); |
146 | val &= ~(PS2_STAT_RX_INT_EN | PS2_STAT_TX_INT_EN); |
147 | iowrite32(val, addr); |
148 | } |
149 | } |
150 | |
151 | static int arc_ps2_create_port(struct platform_device *pdev, |
152 | struct arc_ps2_data *arc_ps2, |
153 | int index) |
154 | { |
155 | struct arc_ps2_port *port = &arc_ps2->port[index]; |
156 | struct serio *io; |
157 | |
158 | io = kzalloc(size: sizeof(struct serio), GFP_KERNEL); |
159 | if (!io) |
160 | return -ENOMEM; |
161 | |
162 | io->id.type = SERIO_8042; |
163 | io->write = arc_ps2_write; |
164 | io->open = arc_ps2_open; |
165 | io->close = arc_ps2_close; |
166 | snprintf(buf: io->name, size: sizeof(io->name), fmt: "ARC PS/2 port%d" , index); |
167 | snprintf(buf: io->phys, size: sizeof(io->phys), fmt: "arc/serio%d" , index); |
168 | io->port_data = port; |
169 | |
170 | port->io = io; |
171 | |
172 | port->data_addr = arc_ps2_calc_addr(arc_ps2, index, status: false); |
173 | port->status_addr = arc_ps2_calc_addr(arc_ps2, index, status: true); |
174 | |
175 | dev_dbg(&pdev->dev, "port%d is allocated (data = 0x%p, status = 0x%p)\n" , |
176 | index, port->data_addr, port->status_addr); |
177 | |
178 | serio_register_port(port->io); |
179 | return 0; |
180 | } |
181 | |
182 | static int arc_ps2_probe(struct platform_device *pdev) |
183 | { |
184 | struct arc_ps2_data *arc_ps2; |
185 | int irq; |
186 | int error, id, i; |
187 | |
188 | irq = platform_get_irq_byname(pdev, "arc_ps2_irq" ); |
189 | if (irq < 0) |
190 | return -EINVAL; |
191 | |
192 | arc_ps2 = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct arc_ps2_data), |
193 | GFP_KERNEL); |
194 | if (!arc_ps2) { |
195 | dev_err(&pdev->dev, "out of memory\n" ); |
196 | return -ENOMEM; |
197 | } |
198 | |
199 | arc_ps2->addr = devm_platform_get_and_ioremap_resource(pdev, index: 0, NULL); |
200 | if (IS_ERR(ptr: arc_ps2->addr)) |
201 | return PTR_ERR(ptr: arc_ps2->addr); |
202 | |
203 | dev_info(&pdev->dev, "irq = %d, address = 0x%p, ports = %i\n" , |
204 | irq, arc_ps2->addr, ARC_PS2_PORTS); |
205 | |
206 | id = ioread32(arc_ps2->addr); |
207 | if (id != ARC_ARC_PS2_ID) { |
208 | dev_err(&pdev->dev, "device id does not match\n" ); |
209 | return -ENXIO; |
210 | } |
211 | |
212 | arc_ps2_inhibit_ports(arc_ps2); |
213 | |
214 | error = devm_request_irq(dev: &pdev->dev, irq, handler: arc_ps2_interrupt, |
215 | irqflags: 0, devname: "arc_ps2" , dev_id: arc_ps2); |
216 | if (error) { |
217 | dev_err(&pdev->dev, "Could not allocate IRQ\n" ); |
218 | return error; |
219 | } |
220 | |
221 | for (i = 0; i < ARC_PS2_PORTS; i++) { |
222 | error = arc_ps2_create_port(pdev, arc_ps2, index: i); |
223 | if (error) { |
224 | while (--i >= 0) |
225 | serio_unregister_port(serio: arc_ps2->port[i].io); |
226 | return error; |
227 | } |
228 | } |
229 | |
230 | platform_set_drvdata(pdev, data: arc_ps2); |
231 | |
232 | return 0; |
233 | } |
234 | |
235 | static int arc_ps2_remove(struct platform_device *pdev) |
236 | { |
237 | struct arc_ps2_data *arc_ps2 = platform_get_drvdata(pdev); |
238 | int i; |
239 | |
240 | for (i = 0; i < ARC_PS2_PORTS; i++) |
241 | serio_unregister_port(serio: arc_ps2->port[i].io); |
242 | |
243 | dev_dbg(&pdev->dev, "interrupt count = %i\n" , arc_ps2->total_int); |
244 | dev_dbg(&pdev->dev, "frame error count = %i\n" , arc_ps2->frame_error); |
245 | dev_dbg(&pdev->dev, "buffer overflow count = %i\n" , |
246 | arc_ps2->buf_overflow); |
247 | |
248 | return 0; |
249 | } |
250 | |
251 | #ifdef CONFIG_OF |
252 | static const struct of_device_id arc_ps2_match[] = { |
253 | { .compatible = "snps,arc_ps2" }, |
254 | { }, |
255 | }; |
256 | MODULE_DEVICE_TABLE(of, arc_ps2_match); |
257 | #endif |
258 | |
259 | static struct platform_driver arc_ps2_driver = { |
260 | .driver = { |
261 | .name = "arc_ps2" , |
262 | .of_match_table = of_match_ptr(arc_ps2_match), |
263 | }, |
264 | .probe = arc_ps2_probe, |
265 | .remove = arc_ps2_remove, |
266 | }; |
267 | |
268 | module_platform_driver(arc_ps2_driver); |
269 | |
270 | MODULE_LICENSE("GPL" ); |
271 | MODULE_AUTHOR("Pavel Sokolov <psokolov@synopsys.com>" ); |
272 | MODULE_DESCRIPTION("ARC PS/2 Driver" ); |
273 | |