Remove inclusion of <sys/cdefs.h> from kernel .c files.
[dragonfly.git] / sys / dev / misc / gpio / gpio.c
CommitLineData
a0e087c3
AH
1/*
2 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Alex Hornung <ahornung@gmail.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 *
35 * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
36 * Copyright (c) 2004, 2006 Alexander Yurchenko <grange@openbsd.org>
37 *
38 * Permission to use, copy, modify, and distribute this software for any
39 * purpose with or without fee is hereby granted, provided that the above
40 * copyright notice and this permission notice appear in all copies.
41 *
42 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
43 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
44 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
45 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
46 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
47 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
48 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
49 */
50/*
51 * XXX: consumer_detach stuff.
52 * XXX: userland stuff.
53 */
a0e087c3
AH
54
55#include <sys/param.h>
56#include <sys/conf.h>
57#include <sys/kernel.h>
58#include <sys/systm.h>
59#include <sys/limits.h>
60#include <sys/thread.h>
61#include <sys/thread2.h>
62#include <sys/malloc.h>
63#include <sys/ctype.h>
64#include <sys/sbuf.h>
65#include <sys/queue.h>
66#include <sys/uio.h>
67#include <sys/lock.h>
68#include <sys/ioccom.h>
69#include <dev/misc/gpio/gpio.h>
70#include <sys/devfs.h>
71
72struct gpio_driver {
73 char *name;
74 struct devfs_bitmap unit_bitmap;
75 LIST_ENTRY(gpio_driver) link;
76};
77
78static LIST_HEAD(, gpio_consumer) gpio_conslist = LIST_HEAD_INITIALIZER(&gpio_conslist);
79static LIST_HEAD(, gpio_driver) gpio_driverlist = LIST_HEAD_INITIALIZER(&gpio_driverlist);
80DEVFS_DECLARE_CLONE_BITMAP(gpio);
81static struct lock gpio_lock;
82
83void
84gpio_consumer_register(struct gpio_consumer *gcp)
85{
86 lockmgr(&gpio_lock, LK_EXCLUSIVE);
87 LIST_INSERT_HEAD(&gpio_conslist, gcp, link);
88 lockmgr(&gpio_lock, LK_RELEASE);
89}
90
91void
92gpio_consumer_unregister(struct gpio_consumer *gcp)
93{
94 lockmgr(&gpio_lock, LK_EXCLUSIVE);
95 LIST_REMOVE(gcp, link);
96 lockmgr(&gpio_lock, LK_RELEASE);
97}
98
99int
100gpio_consumer_attach(const char *consumer, void *arg, struct gpio *gp,
101 int pin, u_int32_t mask)
102{
103 struct gpio_consumer *gcp;
104 int error = -1;
105 int locked = 0;
106
107 /* Check if it is locked already. if not, we acquire the lock */
108 if (!(lockstatus(&gpio_lock, curthread)) == LK_EXCLUSIVE) {
109 lockmgr(&gpio_lock, LK_EXCLUSIVE);
110 locked = 1;
111 }
112
113 LIST_FOREACH(gcp, &gpio_conslist, link) {
114 if (strcmp(gcp->consumer_name, consumer) != 0)
115 continue;
116
117 if (gcp->consumer_attach)
118 error = gcp->consumer_attach(gp, arg, pin, mask);
119 if (error) {
120 kprintf("gpio: Attach of consumer %s to gpio %s%d pin %d failed "
121 "(consumer error %d)\n", consumer, gp->driver_name,
122 gp->driver_unit, pin, error);
123 goto end;
124 }
125
126 kprintf("gpio: Attached consumer %s to gpio %s%d pin %d\n",
127 consumer, gp->driver_name, gp->driver_unit, pin);
128 goto end;
129 }
130
131 kprintf("gpio: Attach of consumer %s to gpio %s%d pin %d failed "
132 "(unknown consumer)\n", consumer, gp->driver_name, gp->driver_unit, pin);
133
134end:
135 /* If we acquired the lock, we also get rid of it */
136 if (locked)
137 lockmgr(&gpio_lock, LK_RELEASE);
138 return error;
139}
140
141int
142gpio_consumer_detach(const char *consumer, struct gpio *gp,
143 int pin)
144{
145 struct gpio_consumer *gcp;
146 int error = -1;
147 int locked = 0;
148
149 /* Check if it is locked already. if not, we acquire the lock */
150 if (!(lockstatus(&gpio_lock, curthread)) == LK_EXCLUSIVE) {
151 lockmgr(&gpio_lock, LK_EXCLUSIVE);
152 locked = 1;
153 }
154
155 LIST_FOREACH(gcp, &gpio_conslist, link) {
156 if (strcmp(gcp->consumer_name, consumer) != 0)
157 continue;
158
159 if (gcp->consumer_detach)
160 error = gcp->consumer_detach(gp, NULL, pin);
161 if (error) {
162 kprintf("gpio: Detach of consumer %s from gpio %s%d pin %d failed "
163 "(consumer error %d)\n", consumer, gp->driver_name,
164 gp->driver_unit, pin, error);
165 goto end;
166 }
167
168 kprintf("gpio: Detached consumer %s from gpio %s%d pin %d\n",
169 consumer, gp->driver_name, gp->driver_unit, pin);
170 goto end;
171 }
172
173 kprintf("gpio: Detach of consumer %s from gpio %s%d pin %d failed "
174 "(unknown consumer)\n", consumer, gp->driver_name, gp->driver_unit, pin);
175
176end:
177 /* If we acquired the lock, we also get rid of it */
178 if (locked)
179 lockmgr(&gpio_lock, LK_RELEASE);
180 return error;
181}
182
183struct gpio_mapping *
184gpio_map(struct gpio *gp, int *map, int offset, u_int32_t mask)
185{
186 struct gpio_mapping *gmp;
187 int npins, pin, i;
188 int locked = 0;
189
190 npins = gpio_npins(mask);
191 if (npins > gp->npins)
192 return NULL;
193 if (npins == 0)
194 return NULL;
195
196 /* Check if it is locked already. if not, we acquire the lock */
197 if (!(lockstatus(&gpio_lock, curthread)) == LK_EXCLUSIVE) {
198 lockmgr(&gpio_lock, LK_EXCLUSIVE);
199 locked = 1;
200 }
201
202 gmp = kmalloc(sizeof(struct gpio_mapping), M_TEMP, M_WAITOK);
203 gmp->gp = gp;
204 if (map) {
205 gmp->map = map;
206 gmp->map_alloced = 0;
207 } else {
208 gmp->map = kmalloc(sizeof(int) * npins, M_TEMP, M_WAITOK);
209 gmp->map_alloced = 1;
210 }
211
212 for (npins = 0, i = 0; i < 32; i++)
213 if (mask & (1 << i)) {
214 pin = offset + i;
215 if (pin < 0 || pin >= gp->npins ||
216 gp->pins[pin].pin_mapped || gp->pins[pin].pin_opened) {
217 if (map == NULL)
218 kfree(gmp->map, M_TEMP);
219 kfree(gmp, M_TEMP);
220 /* If we acquired the lock, we also get rid of it */
221 if (locked)
222 lockmgr(&gpio_lock, LK_RELEASE);
223 return NULL;
224 }
225 gp->pins[pin].pin_mapped = 1;
226 gmp->map[npins++] = pin;
227 }
228 gmp->size = npins;
229
230 /* If we acquired the lock, we also get rid of it */
231 if (locked)
232 lockmgr(&gpio_lock, LK_RELEASE);
233
234 return gmp;
235}
236
237void
238gpio_unmap(struct gpio_mapping *gmp)
239{
240 int pin, i;
241 int locked = 0;
242
243 /* Check if it is locked already. if not, we acquire the lock */
244 if (!(lockstatus(&gpio_lock, curthread)) == LK_EXCLUSIVE) {
245 lockmgr(&gpio_lock, LK_EXCLUSIVE);
246 locked = 1;
247 }
248
249 for (i = 0; i < gmp->size; i++) {
250 pin = gmp->map[i];
251 gmp->gp->pins[pin].pin_mapped = 0;
252 }
253
254 if (gmp->map_alloced)
255 kfree(gmp->map, M_TEMP);
256 kfree(gmp, M_TEMP);
257
258 /* If we acquired the lock, we also get rid of it */
259 if (locked)
260 lockmgr(&gpio_lock, LK_RELEASE);
261}
262
263int
264gpio_npins(u_int32_t mask)
265{
266 int npins, i;
267
268 for (npins = 0, i = 0; i < 32; i++)
269 if (mask & (1 << i))
270 npins++;
271
272 return (npins);
273}
274
275int
276gpio_pin_read(struct gpio *gp, struct gpio_mapping *map, int pin)
277{
278 return gp->pin_read(gp->arg, map->map[pin]);
279}
280
281void
282gpio_pin_write(struct gpio *gp, struct gpio_mapping *map, int pin, int data)
283{
284 return gp->pin_write(gp->arg, map->map[pin], data);
285}
286
287void
288gpio_pin_ctl(struct gpio *gp, struct gpio_mapping *map, int pin, int flags)
289{
290 return gp->pin_ctl(gp->arg, map->map[pin], flags);
291}
292
293int
294gpio_pin_caps(struct gpio *gp, struct gpio_mapping *map, int pin)
295{
296 return (gp->pins[map->map[pin]].pin_caps);
297}
298
299static int
300gpio_open(struct dev_open_args *ap)
301{
302 struct gpio *gp;
303 gpio_pin_t *pin;
304 cdev_t dev;
305
306 dev = ap->a_head.a_dev;
307 gp = dev->si_drv1;
308 pin = dev->si_drv2;
309
310 if (pin->pin_opened || pin->pin_mapped)
311 return EBUSY;
312
313 pin->pin_opened = 1;
314
315 return 0;
316}
317
318static int
319gpio_close(struct dev_close_args *ap)
320{
321 struct gpio *gp;
322 gpio_pin_t *pin;
323 cdev_t dev;
324
325 dev = ap->a_head.a_dev;
326 gp = dev->si_drv1;
327 pin = dev->si_drv2;
328
329 if (pin->pin_opened)
330 pin->pin_opened = 0;
331
332 return 0;
333}
334
335static int
336gpio_write(struct dev_write_args *ap)
337{
338 struct gpio *gp;
339 gpio_pin_t *pin;
340 cdev_t dev;
341 int error;
342 int data = 0;
343
344 dev = ap->a_head.a_dev;
345 gp = dev->si_drv1;
346 pin = dev->si_drv2;
347
348 if (ap->a_uio->uio_resid > sizeof(int))
349 return EINVAL;
350
351 error = uiomove((void *)&data, ap->a_uio->uio_resid, ap->a_uio);
352 if (error)
353 return error;
354
355 if (data != GPIO_PIN_LOW && data != GPIO_PIN_HIGH)
356 return EINVAL;
357
358 gp->pin_write(gp->arg, pin->pin_num, data);
359 pin->pin_state = data;
360
361 return 0;
362}
363
364static int
365gpio_read(struct dev_read_args *ap)
366{
367 struct gpio *gp;
368 gpio_pin_t *pin;
369 cdev_t dev;
370 int error;
371 int data = 0;
372
373 dev = ap->a_head.a_dev;
374 gp = dev->si_drv1;
375 pin = dev->si_drv2;
376
377 if (ap->a_uio->uio_resid < sizeof(char))
378 return EINVAL;
379
380 data = gp->pin_read(gp->arg, pin->pin_num);
381
382 error = uiomove((void *)&data,
383 (ap->a_uio->uio_resid > sizeof(int))?(sizeof(int)):(ap->a_uio->uio_resid),
384 ap->a_uio);
385
386 return error;
387}
388
389static int
390gpio_ioctl(struct dev_ioctl_args *ap)
391{
392 struct gpio_pin_set_args *gpsa;
393 struct gpio *gp;
394 gpio_pin_t *pin;
395 cdev_t dev;
396 int error = 0;
397
398 dev = ap->a_head.a_dev;
399 gp = dev->si_drv1;
400 pin = dev->si_drv2;
401
402 switch(ap->a_cmd) {
403 case GPIOPINSET:
404 gpsa = (struct gpio_pin_set_args *)ap->a_data;
405 if (pin->pin_opened || pin->pin_mapped)
406 return EBUSY;
407
408 gpsa->caps = pin->pin_caps;
409 gpsa->flags = pin->pin_flags;
410
411 if ((gpsa->flags & pin->pin_caps) != gpsa->flags)
412 return ENODEV;
413
414 if (gpsa->flags > 0) {
415 gp->pin_ctl(gp->arg, pin->pin_num, gpsa->flags);
416 pin->pin_flags = gpsa->flags | GPIO_PIN_SET;
417 }
418 break;
419
420 case GPIOPINUNSET:
421 gpsa = (struct gpio_pin_set_args *)ap->a_data;
422 error = EINVAL;
423 break;
424
425 default:
426 return EINVAL;
427 }
428 return 0;
429}
430
431static int
432gpio_master_ioctl(struct dev_ioctl_args *ap)
433{
434 struct gpio_pin_set_args *gpsa;
435 struct gpio_info *gpi;
436 struct gpio_attach_args *gpaa;
437 struct gpio *gp;
438 cdev_t dev;
439 gpio_pin_t *pin;
440 int error = 0;
441
442 dev = ap->a_head.a_dev;
443 gp = dev->si_drv1;
444
445 switch(ap->a_cmd) {
446 case GPIOINFO:
447 gpi = (struct gpio_info *)ap->a_data;
448 gpi->npins = gp->npins;
449 if (gpi->pins != NULL) {
450 error = copyout(gp->pins, gpi->pins,
451 sizeof(struct gpio_pin)*gp->npins);
452 }
453 break;
454
455 case GPIOATTACH:
456 gpaa = (struct gpio_attach_args *)ap->a_data;
457 error = gpio_consumer_attach(gpaa->consumer_name,
458 (gpaa->arg_type == GPIO_TYPE_INT)?
459 ((void *)gpaa->consumer_arg.lint):
460 (gpaa->consumer_arg.string),
461 gp, gpaa->pin_offset, gpaa->pin_mask);
462 break;
463
464 case GPIODETACH:
465 gpaa = (struct gpio_attach_args *)ap->a_data;
466 error = gpio_consumer_detach(gpaa->consumer_name, gp,
467 gpaa->pin_offset);
468 break;
469
470 case GPIOPINSET:
471 gpsa = (struct gpio_pin_set_args *)ap->a_data;
472 if (gpsa->pin < 0 || gpsa->pin >= gp->npins)
473 return EINVAL;
474
475 pin = &gp->pins[gpsa->pin];
476
477 if (pin->pin_opened || pin->pin_mapped)
478 return EBUSY;
479
480 gpsa->caps = pin->pin_caps;
481 gpsa->flags = pin->pin_flags;
482
483 if ((gpsa->flags & pin->pin_caps) != gpsa->flags)
484 return ENODEV;
485
486 if (gpsa->flags > 0) {
487 gp->pin_ctl(gp->arg, gpsa->pin, gpsa->flags);
488 pin->pin_flags = gpsa->flags | GPIO_PIN_SET;
489 }
490 break;
491
492 case GPIOPINUNSET:
493 gpsa = (struct gpio_pin_set_args *)ap->a_data;
494 error = EINVAL;
495 break;
496
497 default:
498 return EINVAL;
499 }
500
501 return error;
502}
503
504static struct dev_ops gpio_ops = {
505 { "gpio", 0, 0 },
506 .d_open = gpio_open,
507 .d_close = gpio_close,
508 .d_write = gpio_write,
509 .d_read = gpio_read,
510 .d_ioctl = gpio_ioctl,
511};
512
513static struct dev_ops gpio_master_ops = {
514 { "gpio", 0, 0 },
515 .d_ioctl = gpio_master_ioctl,
516};
517
518void
519gpio_register(struct gpio *gp)
520{
521 struct gpio_driver *gpd;
522 int i, unit, master_unit = -1;
523
524 KKASSERT(gp->npins > 0);
525 KKASSERT(gp->pins);
526
527 lockmgr(&gpio_lock, LK_EXCLUSIVE);
528 LIST_FOREACH(gpd, &gpio_driverlist, link) {
529 if (strcmp(gpd->name, gp->driver_name) != 0)
530 continue;
531
532 master_unit = devfs_clone_bitmap_get(&gpd->unit_bitmap, 0);
533 break;
534 }
535 if (master_unit == -1) {
536 gpd = kmalloc(sizeof(struct gpio_driver),
537 M_TEMP, M_WAITOK | M_ZERO);
538 gpd->name = kstrdup(gp->driver_name, M_TEMP);
539 devfs_clone_bitmap_init(&gpd->unit_bitmap);
540 master_unit = devfs_clone_bitmap_get(&gpd->unit_bitmap, 0);
541 LIST_INSERT_HEAD(&gpio_driverlist, gpd, link);
542 }
543 lockmgr(&gpio_lock, LK_RELEASE);
544
545 gp->driver_unit = master_unit;
546 kprintf("gpio: GPIO driver %s%d registered, npins = %d\n",
547 gp->driver_name, master_unit, gp->npins);
548
549 unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(gpio), 0);
550 gp->master_dev = make_dev(&gpio_master_ops, unit, UID_ROOT, GID_WHEEL, 0600,
551 "gpio/%s%d/master", gp->driver_name, master_unit);
552 gp->master_dev->si_drv1 = gp;
553
554 for (i = 0; i < gp->npins; i++) {
555 unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(gpio), 0);
556 gp->pins[i].dev = make_dev(&gpio_ops, unit, UID_ROOT, GID_WHEEL, 0600,
557 "gpio/%s%d/%d", gp->driver_name, master_unit, gp->pins[i].pin_num);
558 gp->pins[i].dev->si_drv1 = gp;
559 gp->pins[i].dev->si_drv2 = &gp->pins[i];
560 }
561}
562
563void
564gpio_unregister(struct gpio *gp)
565{
566 struct gpio_driver *gpd;
567 int i;
568
569 KKASSERT(gp->npins > 0);
570 KKASSERT(gp->pins);
571
572 for (i = 0; i < gp->npins; i++) {
573 devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(gpio),
574 minor(gp->pins[i].dev));
575 destroy_dev(gp->pins[i].dev);
576 }
577
578 destroy_dev(gp->master_dev);
579
580 lockmgr(&gpio_lock, LK_EXCLUSIVE);
581 LIST_FOREACH(gpd, &gpio_driverlist, link) {
582 if (strcmp(gpd->name, gp->driver_name) != 0)
583 continue;
584
585 devfs_clone_bitmap_put(&gpd->unit_bitmap, gp->driver_unit);
586 LIST_REMOVE(gpd, link);
587 break;
588 }
589 lockmgr(&gpio_lock, LK_RELEASE);
590
591 kprintf("gpio: GPIO driver %s%d unregistered\n",
592 gp->driver_name, gp->driver_unit);
593}
594
595static void
596gpio_drvinit(void *unused)
597{
598 lockinit(&gpio_lock, "gpio_lock", 0, 0);
599 devfs_clone_bitmap_init(&DEVFS_CLONE_BITMAP(gpio));
600}
601
602SYSINIT(gpio, SI_SUB_PRE_DRIVERS, SI_ORDER_FIRST, gpio_drvinit, NULL);