1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Nano River Technologies viperboard GPIO lib driver |
4 | * |
5 | * (C) 2012 by Lemonage GmbH |
6 | * Author: Lars Poeschel <poeschel@lemonage.de> |
7 | * All rights reserved. |
8 | */ |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/errno.h> |
12 | #include <linux/module.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/types.h> |
15 | #include <linux/mutex.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/usb.h> |
18 | #include <linux/gpio/driver.h> |
19 | |
20 | #include <linux/mfd/viperboard.h> |
21 | |
22 | #define VPRBRD_GPIOA_CLK_1MHZ 0 |
23 | #define VPRBRD_GPIOA_CLK_100KHZ 1 |
24 | #define VPRBRD_GPIOA_CLK_10KHZ 2 |
25 | #define VPRBRD_GPIOA_CLK_1KHZ 3 |
26 | #define VPRBRD_GPIOA_CLK_100HZ 4 |
27 | #define VPRBRD_GPIOA_CLK_10HZ 5 |
28 | |
29 | #define VPRBRD_GPIOA_FREQ_DEFAULT 1000 |
30 | |
31 | #define VPRBRD_GPIOA_CMD_CONT 0x00 |
32 | #define VPRBRD_GPIOA_CMD_PULSE 0x01 |
33 | #define VPRBRD_GPIOA_CMD_PWM 0x02 |
34 | #define VPRBRD_GPIOA_CMD_SETOUT 0x03 |
35 | #define VPRBRD_GPIOA_CMD_SETIN 0x04 |
36 | #define VPRBRD_GPIOA_CMD_SETINT 0x05 |
37 | #define VPRBRD_GPIOA_CMD_GETIN 0x06 |
38 | |
39 | #define VPRBRD_GPIOB_CMD_SETDIR 0x00 |
40 | #define VPRBRD_GPIOB_CMD_SETVAL 0x01 |
41 | |
42 | struct vprbrd_gpioa_msg { |
43 | u8 cmd; |
44 | u8 clk; |
45 | u8 offset; |
46 | u8 t1; |
47 | u8 t2; |
48 | u8 invert; |
49 | u8 pwmlevel; |
50 | u8 outval; |
51 | u8 risefall; |
52 | u8 answer; |
53 | u8 __fill; |
54 | } __packed; |
55 | |
56 | struct vprbrd_gpiob_msg { |
57 | u8 cmd; |
58 | u16 val; |
59 | u16 mask; |
60 | } __packed; |
61 | |
62 | struct vprbrd_gpio { |
63 | struct gpio_chip gpioa; /* gpio a related things */ |
64 | u32 gpioa_out; |
65 | u32 gpioa_val; |
66 | struct gpio_chip gpiob; /* gpio b related things */ |
67 | u32 gpiob_out; |
68 | u32 gpiob_val; |
69 | struct vprbrd *vb; |
70 | }; |
71 | |
72 | /* gpioa sampling clock module parameter */ |
73 | static unsigned char gpioa_clk; |
74 | static unsigned int gpioa_freq = VPRBRD_GPIOA_FREQ_DEFAULT; |
75 | module_param(gpioa_freq, uint, 0); |
76 | MODULE_PARM_DESC(gpioa_freq, |
77 | "gpio-a sampling freq in Hz (default is 1000Hz) valid values: 10, 100, 1000, 10000, 100000, 1000000" ); |
78 | |
79 | /* ----- begin of gipo a chip -------------------------------------------- */ |
80 | |
81 | static int vprbrd_gpioa_get(struct gpio_chip *chip, |
82 | unsigned int offset) |
83 | { |
84 | int ret, answer, error = 0; |
85 | struct vprbrd_gpio *gpio = gpiochip_get_data(gc: chip); |
86 | struct vprbrd *vb = gpio->vb; |
87 | struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; |
88 | |
89 | /* if io is set to output, just return the saved value */ |
90 | if (gpio->gpioa_out & (1 << offset)) |
91 | return !!(gpio->gpioa_val & (1 << offset)); |
92 | |
93 | mutex_lock(&vb->lock); |
94 | |
95 | gamsg->cmd = VPRBRD_GPIOA_CMD_GETIN; |
96 | gamsg->clk = 0x00; |
97 | gamsg->offset = offset; |
98 | gamsg->t1 = 0x00; |
99 | gamsg->t2 = 0x00; |
100 | gamsg->invert = 0x00; |
101 | gamsg->pwmlevel = 0x00; |
102 | gamsg->outval = 0x00; |
103 | gamsg->risefall = 0x00; |
104 | gamsg->answer = 0x00; |
105 | gamsg->__fill = 0x00; |
106 | |
107 | ret = usb_control_msg(dev: vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), |
108 | VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, value: 0x0000, |
109 | index: 0x0000, data: gamsg, size: sizeof(struct vprbrd_gpioa_msg), |
110 | VPRBRD_USB_TIMEOUT_MS); |
111 | if (ret != sizeof(struct vprbrd_gpioa_msg)) |
112 | error = -EREMOTEIO; |
113 | |
114 | ret = usb_control_msg(dev: vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0), |
115 | VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_IN, value: 0x0000, |
116 | index: 0x0000, data: gamsg, size: sizeof(struct vprbrd_gpioa_msg), |
117 | VPRBRD_USB_TIMEOUT_MS); |
118 | answer = gamsg->answer & 0x01; |
119 | |
120 | mutex_unlock(lock: &vb->lock); |
121 | |
122 | if (ret != sizeof(struct vprbrd_gpioa_msg)) |
123 | error = -EREMOTEIO; |
124 | |
125 | if (error) |
126 | return error; |
127 | |
128 | return answer; |
129 | } |
130 | |
131 | static void vprbrd_gpioa_set(struct gpio_chip *chip, |
132 | unsigned int offset, int value) |
133 | { |
134 | int ret; |
135 | struct vprbrd_gpio *gpio = gpiochip_get_data(gc: chip); |
136 | struct vprbrd *vb = gpio->vb; |
137 | struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; |
138 | |
139 | if (gpio->gpioa_out & (1 << offset)) { |
140 | if (value) |
141 | gpio->gpioa_val |= (1 << offset); |
142 | else |
143 | gpio->gpioa_val &= ~(1 << offset); |
144 | |
145 | mutex_lock(&vb->lock); |
146 | |
147 | gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT; |
148 | gamsg->clk = 0x00; |
149 | gamsg->offset = offset; |
150 | gamsg->t1 = 0x00; |
151 | gamsg->t2 = 0x00; |
152 | gamsg->invert = 0x00; |
153 | gamsg->pwmlevel = 0x00; |
154 | gamsg->outval = value; |
155 | gamsg->risefall = 0x00; |
156 | gamsg->answer = 0x00; |
157 | gamsg->__fill = 0x00; |
158 | |
159 | ret = usb_control_msg(dev: vb->usb_dev, |
160 | usb_sndctrlpipe(vb->usb_dev, 0), |
161 | VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, |
162 | value: 0x0000, index: 0x0000, data: gamsg, |
163 | size: sizeof(struct vprbrd_gpioa_msg), VPRBRD_USB_TIMEOUT_MS); |
164 | |
165 | mutex_unlock(lock: &vb->lock); |
166 | |
167 | if (ret != sizeof(struct vprbrd_gpioa_msg)) |
168 | dev_err(chip->parent, "usb error setting pin value\n" ); |
169 | } |
170 | } |
171 | |
172 | static int vprbrd_gpioa_direction_input(struct gpio_chip *chip, |
173 | unsigned int offset) |
174 | { |
175 | int ret; |
176 | struct vprbrd_gpio *gpio = gpiochip_get_data(gc: chip); |
177 | struct vprbrd *vb = gpio->vb; |
178 | struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; |
179 | |
180 | gpio->gpioa_out &= ~(1 << offset); |
181 | |
182 | mutex_lock(&vb->lock); |
183 | |
184 | gamsg->cmd = VPRBRD_GPIOA_CMD_SETIN; |
185 | gamsg->clk = gpioa_clk; |
186 | gamsg->offset = offset; |
187 | gamsg->t1 = 0x00; |
188 | gamsg->t2 = 0x00; |
189 | gamsg->invert = 0x00; |
190 | gamsg->pwmlevel = 0x00; |
191 | gamsg->outval = 0x00; |
192 | gamsg->risefall = 0x00; |
193 | gamsg->answer = 0x00; |
194 | gamsg->__fill = 0x00; |
195 | |
196 | ret = usb_control_msg(dev: vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), |
197 | VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, value: 0x0000, |
198 | index: 0x0000, data: gamsg, size: sizeof(struct vprbrd_gpioa_msg), |
199 | VPRBRD_USB_TIMEOUT_MS); |
200 | |
201 | mutex_unlock(lock: &vb->lock); |
202 | |
203 | if (ret != sizeof(struct vprbrd_gpioa_msg)) |
204 | return -EREMOTEIO; |
205 | |
206 | return 0; |
207 | } |
208 | |
209 | static int vprbrd_gpioa_direction_output(struct gpio_chip *chip, |
210 | unsigned int offset, int value) |
211 | { |
212 | int ret; |
213 | struct vprbrd_gpio *gpio = gpiochip_get_data(gc: chip); |
214 | struct vprbrd *vb = gpio->vb; |
215 | struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; |
216 | |
217 | gpio->gpioa_out |= (1 << offset); |
218 | if (value) |
219 | gpio->gpioa_val |= (1 << offset); |
220 | else |
221 | gpio->gpioa_val &= ~(1 << offset); |
222 | |
223 | mutex_lock(&vb->lock); |
224 | |
225 | gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT; |
226 | gamsg->clk = 0x00; |
227 | gamsg->offset = offset; |
228 | gamsg->t1 = 0x00; |
229 | gamsg->t2 = 0x00; |
230 | gamsg->invert = 0x00; |
231 | gamsg->pwmlevel = 0x00; |
232 | gamsg->outval = value; |
233 | gamsg->risefall = 0x00; |
234 | gamsg->answer = 0x00; |
235 | gamsg->__fill = 0x00; |
236 | |
237 | ret = usb_control_msg(dev: vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), |
238 | VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, value: 0x0000, |
239 | index: 0x0000, data: gamsg, size: sizeof(struct vprbrd_gpioa_msg), |
240 | VPRBRD_USB_TIMEOUT_MS); |
241 | |
242 | mutex_unlock(lock: &vb->lock); |
243 | |
244 | if (ret != sizeof(struct vprbrd_gpioa_msg)) |
245 | return -EREMOTEIO; |
246 | |
247 | return 0; |
248 | } |
249 | |
250 | /* ----- end of gpio a chip ---------------------------------------------- */ |
251 | |
252 | /* ----- begin of gipo b chip -------------------------------------------- */ |
253 | |
254 | static int vprbrd_gpiob_setdir(struct vprbrd *vb, unsigned int offset, |
255 | unsigned int dir) |
256 | { |
257 | struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf; |
258 | int ret; |
259 | |
260 | gbmsg->cmd = VPRBRD_GPIOB_CMD_SETDIR; |
261 | gbmsg->val = cpu_to_be16(dir << offset); |
262 | gbmsg->mask = cpu_to_be16(0x0001 << offset); |
263 | |
264 | ret = usb_control_msg(dev: vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), |
265 | VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, value: 0x0000, |
266 | index: 0x0000, data: gbmsg, size: sizeof(struct vprbrd_gpiob_msg), |
267 | VPRBRD_USB_TIMEOUT_MS); |
268 | |
269 | if (ret != sizeof(struct vprbrd_gpiob_msg)) |
270 | return -EREMOTEIO; |
271 | |
272 | return 0; |
273 | } |
274 | |
275 | static int vprbrd_gpiob_get(struct gpio_chip *chip, |
276 | unsigned int offset) |
277 | { |
278 | int ret; |
279 | u16 val; |
280 | struct vprbrd_gpio *gpio = gpiochip_get_data(gc: chip); |
281 | struct vprbrd *vb = gpio->vb; |
282 | struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf; |
283 | |
284 | /* if io is set to output, just return the saved value */ |
285 | if (gpio->gpiob_out & (1 << offset)) |
286 | return gpio->gpiob_val & (1 << offset); |
287 | |
288 | mutex_lock(&vb->lock); |
289 | |
290 | ret = usb_control_msg(dev: vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0), |
291 | VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_IN, value: 0x0000, |
292 | index: 0x0000, data: gbmsg, size: sizeof(struct vprbrd_gpiob_msg), |
293 | VPRBRD_USB_TIMEOUT_MS); |
294 | val = gbmsg->val; |
295 | |
296 | mutex_unlock(lock: &vb->lock); |
297 | |
298 | if (ret != sizeof(struct vprbrd_gpiob_msg)) |
299 | return ret; |
300 | |
301 | /* cache the read values */ |
302 | gpio->gpiob_val = be16_to_cpu(val); |
303 | |
304 | return (gpio->gpiob_val >> offset) & 0x1; |
305 | } |
306 | |
307 | static void vprbrd_gpiob_set(struct gpio_chip *chip, |
308 | unsigned int offset, int value) |
309 | { |
310 | int ret; |
311 | struct vprbrd_gpio *gpio = gpiochip_get_data(gc: chip); |
312 | struct vprbrd *vb = gpio->vb; |
313 | struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf; |
314 | |
315 | if (gpio->gpiob_out & (1 << offset)) { |
316 | if (value) |
317 | gpio->gpiob_val |= (1 << offset); |
318 | else |
319 | gpio->gpiob_val &= ~(1 << offset); |
320 | |
321 | mutex_lock(&vb->lock); |
322 | |
323 | gbmsg->cmd = VPRBRD_GPIOB_CMD_SETVAL; |
324 | gbmsg->val = cpu_to_be16(value << offset); |
325 | gbmsg->mask = cpu_to_be16(0x0001 << offset); |
326 | |
327 | ret = usb_control_msg(dev: vb->usb_dev, |
328 | usb_sndctrlpipe(vb->usb_dev, 0), |
329 | VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, |
330 | value: 0x0000, index: 0x0000, data: gbmsg, |
331 | size: sizeof(struct vprbrd_gpiob_msg), VPRBRD_USB_TIMEOUT_MS); |
332 | |
333 | mutex_unlock(lock: &vb->lock); |
334 | |
335 | if (ret != sizeof(struct vprbrd_gpiob_msg)) |
336 | dev_err(chip->parent, "usb error setting pin value\n" ); |
337 | } |
338 | } |
339 | |
340 | static int vprbrd_gpiob_direction_input(struct gpio_chip *chip, |
341 | unsigned int offset) |
342 | { |
343 | int ret; |
344 | struct vprbrd_gpio *gpio = gpiochip_get_data(gc: chip); |
345 | struct vprbrd *vb = gpio->vb; |
346 | |
347 | gpio->gpiob_out &= ~(1 << offset); |
348 | |
349 | mutex_lock(&vb->lock); |
350 | |
351 | ret = vprbrd_gpiob_setdir(vb, offset, dir: 0); |
352 | |
353 | mutex_unlock(lock: &vb->lock); |
354 | |
355 | if (ret) |
356 | dev_err(chip->parent, "usb error setting pin to input\n" ); |
357 | |
358 | return ret; |
359 | } |
360 | |
361 | static int vprbrd_gpiob_direction_output(struct gpio_chip *chip, |
362 | unsigned int offset, int value) |
363 | { |
364 | int ret; |
365 | struct vprbrd_gpio *gpio = gpiochip_get_data(gc: chip); |
366 | struct vprbrd *vb = gpio->vb; |
367 | |
368 | gpio->gpiob_out |= (1 << offset); |
369 | |
370 | mutex_lock(&vb->lock); |
371 | |
372 | ret = vprbrd_gpiob_setdir(vb, offset, dir: 1); |
373 | if (ret) |
374 | dev_err(chip->parent, "usb error setting pin to output\n" ); |
375 | |
376 | mutex_unlock(lock: &vb->lock); |
377 | |
378 | vprbrd_gpiob_set(chip, offset, value); |
379 | |
380 | return ret; |
381 | } |
382 | |
383 | /* ----- end of gpio b chip ---------------------------------------------- */ |
384 | |
385 | static int vprbrd_gpio_probe(struct platform_device *pdev) |
386 | { |
387 | struct vprbrd *vb = dev_get_drvdata(dev: pdev->dev.parent); |
388 | struct vprbrd_gpio *vb_gpio; |
389 | int ret; |
390 | |
391 | vb_gpio = devm_kzalloc(dev: &pdev->dev, size: sizeof(*vb_gpio), GFP_KERNEL); |
392 | if (vb_gpio == NULL) |
393 | return -ENOMEM; |
394 | |
395 | vb_gpio->vb = vb; |
396 | /* registering gpio a */ |
397 | vb_gpio->gpioa.label = "viperboard gpio a" ; |
398 | vb_gpio->gpioa.parent = &pdev->dev; |
399 | vb_gpio->gpioa.owner = THIS_MODULE; |
400 | vb_gpio->gpioa.base = -1; |
401 | vb_gpio->gpioa.ngpio = 16; |
402 | vb_gpio->gpioa.can_sleep = true; |
403 | vb_gpio->gpioa.set = vprbrd_gpioa_set; |
404 | vb_gpio->gpioa.get = vprbrd_gpioa_get; |
405 | vb_gpio->gpioa.direction_input = vprbrd_gpioa_direction_input; |
406 | vb_gpio->gpioa.direction_output = vprbrd_gpioa_direction_output; |
407 | |
408 | ret = devm_gpiochip_add_data(&pdev->dev, &vb_gpio->gpioa, vb_gpio); |
409 | if (ret < 0) |
410 | return ret; |
411 | |
412 | /* registering gpio b */ |
413 | vb_gpio->gpiob.label = "viperboard gpio b" ; |
414 | vb_gpio->gpiob.parent = &pdev->dev; |
415 | vb_gpio->gpiob.owner = THIS_MODULE; |
416 | vb_gpio->gpiob.base = -1; |
417 | vb_gpio->gpiob.ngpio = 16; |
418 | vb_gpio->gpiob.can_sleep = true; |
419 | vb_gpio->gpiob.set = vprbrd_gpiob_set; |
420 | vb_gpio->gpiob.get = vprbrd_gpiob_get; |
421 | vb_gpio->gpiob.direction_input = vprbrd_gpiob_direction_input; |
422 | vb_gpio->gpiob.direction_output = vprbrd_gpiob_direction_output; |
423 | |
424 | return devm_gpiochip_add_data(&pdev->dev, &vb_gpio->gpiob, vb_gpio); |
425 | } |
426 | |
427 | static struct platform_driver vprbrd_gpio_driver = { |
428 | .driver.name = "viperboard-gpio" , |
429 | .probe = vprbrd_gpio_probe, |
430 | }; |
431 | |
432 | static int __init vprbrd_gpio_init(void) |
433 | { |
434 | switch (gpioa_freq) { |
435 | case 1000000: |
436 | gpioa_clk = VPRBRD_GPIOA_CLK_1MHZ; |
437 | break; |
438 | case 100000: |
439 | gpioa_clk = VPRBRD_GPIOA_CLK_100KHZ; |
440 | break; |
441 | case 10000: |
442 | gpioa_clk = VPRBRD_GPIOA_CLK_10KHZ; |
443 | break; |
444 | case 1000: |
445 | gpioa_clk = VPRBRD_GPIOA_CLK_1KHZ; |
446 | break; |
447 | case 100: |
448 | gpioa_clk = VPRBRD_GPIOA_CLK_100HZ; |
449 | break; |
450 | case 10: |
451 | gpioa_clk = VPRBRD_GPIOA_CLK_10HZ; |
452 | break; |
453 | default: |
454 | pr_warn("invalid gpioa_freq (%d)\n" , gpioa_freq); |
455 | gpioa_clk = VPRBRD_GPIOA_CLK_1KHZ; |
456 | } |
457 | |
458 | return platform_driver_register(&vprbrd_gpio_driver); |
459 | } |
460 | subsys_initcall(vprbrd_gpio_init); |
461 | |
462 | static void __exit vprbrd_gpio_exit(void) |
463 | { |
464 | platform_driver_unregister(&vprbrd_gpio_driver); |
465 | } |
466 | module_exit(vprbrd_gpio_exit); |
467 | |
468 | MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>" ); |
469 | MODULE_DESCRIPTION("GPIO driver for Nano River Techs Viperboard" ); |
470 | MODULE_LICENSE("GPL" ); |
471 | MODULE_ALIAS("platform:viperboard-gpio" ); |
472 | |