xref: /dflybsd-src/sys/dev/misc/gpio/gpio_led.c (revision 475c7069e94570a897d1467613efd2b3f0212ff9)
1a0e087c3SAlex Hornung /*
2a0e087c3SAlex Hornung  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
3a0e087c3SAlex Hornung  *
4a0e087c3SAlex Hornung  * This code is derived from software contributed to The DragonFly Project
5a0e087c3SAlex Hornung  * by Alex Hornung <ahornung@gmail.com>
6a0e087c3SAlex Hornung  *
7a0e087c3SAlex Hornung  * Redistribution and use in source and binary forms, with or without
8a0e087c3SAlex Hornung  * modification, are permitted provided that the following conditions
9a0e087c3SAlex Hornung  * are met:
10a0e087c3SAlex Hornung  *
11a0e087c3SAlex Hornung  * 1. Redistributions of source code must retain the above copyright
12a0e087c3SAlex Hornung  *    notice, this list of conditions and the following disclaimer.
13a0e087c3SAlex Hornung  * 2. Redistributions in binary form must reproduce the above copyright
14a0e087c3SAlex Hornung  *    notice, this list of conditions and the following disclaimer in
15a0e087c3SAlex Hornung  *    the documentation and/or other materials provided with the
16a0e087c3SAlex Hornung  *    distribution.
17a0e087c3SAlex Hornung  * 3. Neither the name of The DragonFly Project nor the names of its
18a0e087c3SAlex Hornung  *    contributors may be used to endorse or promote products derived
19a0e087c3SAlex Hornung  *    from this software without specific, prior written permission.
20a0e087c3SAlex Hornung  *
21a0e087c3SAlex Hornung  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22a0e087c3SAlex Hornung  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23a0e087c3SAlex Hornung  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24a0e087c3SAlex Hornung  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25a0e087c3SAlex Hornung  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26a0e087c3SAlex Hornung  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27a0e087c3SAlex Hornung  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28a0e087c3SAlex Hornung  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29a0e087c3SAlex Hornung  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30a0e087c3SAlex Hornung  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31a0e087c3SAlex Hornung  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32a0e087c3SAlex Hornung  * SUCH DAMAGE.
33a0e087c3SAlex Hornung  */
34a0e087c3SAlex Hornung 
35a0e087c3SAlex Hornung #include <sys/param.h>
36a0e087c3SAlex Hornung #include <sys/conf.h>
37a0e087c3SAlex Hornung #include <sys/kernel.h>
38a0e087c3SAlex Hornung #include <sys/systm.h>
39a0e087c3SAlex Hornung #include <sys/limits.h>
40a0e087c3SAlex Hornung #include <sys/malloc.h>
41a0e087c3SAlex Hornung #include <sys/ctype.h>
42a0e087c3SAlex Hornung #include <sys/sbuf.h>
43a0e087c3SAlex Hornung #include <sys/queue.h>
44a0e087c3SAlex Hornung #include <dev/misc/gpio/gpio.h>
45a0e087c3SAlex Hornung #include <sys/uio.h>
46a0e087c3SAlex Hornung #include <sys/lock.h>
47a0e087c3SAlex Hornung #include <sys/devfs.h>
48a0e087c3SAlex Hornung 
49a0e087c3SAlex Hornung struct ledsc {
50a0e087c3SAlex Hornung 	LIST_ENTRY(ledsc)	list;
51a0e087c3SAlex Hornung 	struct gpio *gp;
52a0e087c3SAlex Hornung 	int		pin;
53a0e087c3SAlex Hornung 	cdev_t	dev;
54a0e087c3SAlex Hornung 	int		unit;
55a0e087c3SAlex Hornung 	int		opened;
56a0e087c3SAlex Hornung 	char	*name;
57a0e087c3SAlex Hornung 	struct gpio_mapping *gp_map;
58a0e087c3SAlex Hornung };
59a0e087c3SAlex Hornung 
600cf7fc2cSSascha Wildner DEVFS_DEFINE_CLONE_BITMAP(nled);
61a0e087c3SAlex Hornung static struct lock led_lock;
62a0e087c3SAlex Hornung static LIST_HEAD(, ledsc) led_list = LIST_HEAD_INITIALIZER(&led_list);
63a0e087c3SAlex Hornung static MALLOC_DEFINE(M_LED, "LED", "LED driver");
64a0e087c3SAlex Hornung 
65a0e087c3SAlex Hornung 
66a0e087c3SAlex Hornung static int
led_open(struct dev_open_args * ap)67a0e087c3SAlex Hornung led_open(struct dev_open_args *ap)
68a0e087c3SAlex Hornung {
69a0e087c3SAlex Hornung 	struct ledsc *sc;
70a0e087c3SAlex Hornung 	cdev_t	dev;
71a0e087c3SAlex Hornung 
72a0e087c3SAlex Hornung 	dev = ap->a_head.a_dev;
73a0e087c3SAlex Hornung 	sc = dev->si_drv1;
74a0e087c3SAlex Hornung 
75*475c7069SMatthew Dillon 	lockmgr(&led_lock, LK_EXCLUSIVE);
76*475c7069SMatthew Dillon 	if (sc->opened) {
77*475c7069SMatthew Dillon 		lockmgr(&led_lock, LK_RELEASE);
78a0e087c3SAlex Hornung 		return EBUSY;
79*475c7069SMatthew Dillon 	}
80a0e087c3SAlex Hornung 	sc->opened = 1;
81*475c7069SMatthew Dillon 	lockmgr(&led_lock, LK_RELEASE);
82a0e087c3SAlex Hornung 
83a0e087c3SAlex Hornung 	return 0;
84a0e087c3SAlex Hornung }
85a0e087c3SAlex Hornung 
86a0e087c3SAlex Hornung static int
led_close(struct dev_close_args * ap)87a0e087c3SAlex Hornung led_close(struct dev_close_args *ap)
88a0e087c3SAlex Hornung {
89a0e087c3SAlex Hornung 	struct ledsc *sc;
90a0e087c3SAlex Hornung 	cdev_t	dev;
91a0e087c3SAlex Hornung 
92a0e087c3SAlex Hornung 	dev = ap->a_head.a_dev;
93a0e087c3SAlex Hornung 	sc = dev->si_drv1;
94a0e087c3SAlex Hornung 
95*475c7069SMatthew Dillon 	lockmgr(&led_lock, LK_EXCLUSIVE);
96a0e087c3SAlex Hornung 	if (sc->opened)
97a0e087c3SAlex Hornung 		sc->opened = 0;
98*475c7069SMatthew Dillon 	lockmgr(&led_lock, LK_RELEASE);
99a0e087c3SAlex Hornung 
100a0e087c3SAlex Hornung 	return 0;
101a0e087c3SAlex Hornung }
102a0e087c3SAlex Hornung 
103a0e087c3SAlex Hornung static int
led_write(struct dev_write_args * ap)104a0e087c3SAlex Hornung led_write(struct dev_write_args *ap)
105a0e087c3SAlex Hornung {
106a0e087c3SAlex Hornung 	struct ledsc *sc;
107a0e087c3SAlex Hornung 	cdev_t	dev;
108a0e087c3SAlex Hornung 	int		error;
109a0e087c3SAlex Hornung 	int		data = 0;
110a0e087c3SAlex Hornung 	int		len;
111a0e087c3SAlex Hornung 
112a0e087c3SAlex Hornung 	dev = ap->a_head.a_dev;
113a0e087c3SAlex Hornung 	sc = dev->si_drv1;
114a0e087c3SAlex Hornung 
115a0e087c3SAlex Hornung 	if (ap->a_uio->uio_resid > sizeof(int))
116a0e087c3SAlex Hornung 		return EINVAL;
117a0e087c3SAlex Hornung 
118a0e087c3SAlex Hornung 	len = ap->a_uio->uio_resid;
119a0e087c3SAlex Hornung 
120a0e087c3SAlex Hornung 	error = uiomove((void *)&data, ap->a_uio->uio_resid, ap->a_uio);
121a0e087c3SAlex Hornung 	if (error)
122a0e087c3SAlex Hornung 		return error;
123a0e087c3SAlex Hornung 
124a0e087c3SAlex Hornung 	if (len > 1)
125a0e087c3SAlex Hornung 		data = ((char *)&data)[0];
126a0e087c3SAlex Hornung 
127a0e087c3SAlex Hornung 	if (data >= '0')
128a0e087c3SAlex Hornung 		data -= '0';
129a0e087c3SAlex Hornung 
130*475c7069SMatthew Dillon 	lockmgr(&led_lock, LK_EXCLUSIVE);
131a0e087c3SAlex Hornung 	gpio_pin_write(sc->gp, sc->gp_map, 0, data);
132*475c7069SMatthew Dillon 	lockmgr(&led_lock, LK_RELEASE);
133a0e087c3SAlex Hornung 
134a0e087c3SAlex Hornung 	return 0;
135a0e087c3SAlex Hornung }
136a0e087c3SAlex Hornung 
137a0e087c3SAlex Hornung static int
led_read(struct dev_read_args * ap)138a0e087c3SAlex Hornung led_read(struct dev_read_args *ap)
139a0e087c3SAlex Hornung {
140a0e087c3SAlex Hornung 	struct ledsc *sc;
141a0e087c3SAlex Hornung 	cdev_t	dev;
142a0e087c3SAlex Hornung 	int		error;
143a0e087c3SAlex Hornung 	int		data = 0;
144a0e087c3SAlex Hornung 
145a0e087c3SAlex Hornung 	dev = ap->a_head.a_dev;
146a0e087c3SAlex Hornung 	sc = dev->si_drv1;
147a0e087c3SAlex Hornung 
148a0e087c3SAlex Hornung 	if (ap->a_uio->uio_resid < sizeof(int))
149a0e087c3SAlex Hornung 		return EINVAL;
150a0e087c3SAlex Hornung 
151*475c7069SMatthew Dillon 	lockmgr(&led_lock, LK_EXCLUSIVE);
152a0e087c3SAlex Hornung 	data = gpio_pin_read(sc->gp, sc->gp_map, 0);
153*475c7069SMatthew Dillon 	lockmgr(&led_lock, LK_RELEASE);
154a0e087c3SAlex Hornung 
155a0e087c3SAlex Hornung 	error = uiomove((void *)&data,
156a0e087c3SAlex Hornung 	    (ap->a_uio->uio_resid > sizeof(int))?(sizeof(int)):(ap->a_uio->uio_resid),
157a0e087c3SAlex Hornung 		ap->a_uio);
158a0e087c3SAlex Hornung 
159a0e087c3SAlex Hornung 	return error;
160a0e087c3SAlex Hornung }
161a0e087c3SAlex Hornung 
162a0e087c3SAlex Hornung static int
led_ioctl(struct dev_ioctl_args * ap)163a0e087c3SAlex Hornung led_ioctl(struct dev_ioctl_args *ap)
164a0e087c3SAlex Hornung {
165a0e087c3SAlex Hornung 	/* XXX: set a name */
166*475c7069SMatthew Dillon 	lockmgr(&led_lock, LK_EXCLUSIVE);
167*475c7069SMatthew Dillon 	lockmgr(&led_lock, LK_RELEASE);
168a0e087c3SAlex Hornung 	return 0;
169a0e087c3SAlex Hornung }
170a0e087c3SAlex Hornung 
171a0e087c3SAlex Hornung static struct dev_ops nled_ops = {
172*475c7069SMatthew Dillon 	{ "gpio", 0, D_MPSAFE },
173a0e087c3SAlex Hornung 	.d_open  =	led_open,
174a0e087c3SAlex Hornung 	.d_close =	led_close,
175a0e087c3SAlex Hornung 	.d_write = 	led_write,
176a0e087c3SAlex Hornung 	.d_read  =	led_read,
177a0e087c3SAlex Hornung 	.d_ioctl =	led_ioctl,
178a0e087c3SAlex Hornung };
179a0e087c3SAlex Hornung 
180a0e087c3SAlex Hornung 
181a0e087c3SAlex Hornung static int
led_attach(struct gpio * gp,void * arg,int pin,u_int32_t mask)182a0e087c3SAlex Hornung led_attach(struct gpio *gp, void *arg, int pin, u_int32_t mask)
183a0e087c3SAlex Hornung {
184a0e087c3SAlex Hornung 	struct ledsc *sc;
185a0e087c3SAlex Hornung 
186a0e087c3SAlex Hornung 	if (arg == NULL)
187a0e087c3SAlex Hornung 		return 1;
188a0e087c3SAlex Hornung 
189a0e087c3SAlex Hornung 	lockmgr(&led_lock, LK_EXCLUSIVE);
190a0e087c3SAlex Hornung 	sc = kmalloc(sizeof(struct ledsc), M_LED, M_WAITOK);
191a0e087c3SAlex Hornung 
192a0e087c3SAlex Hornung 	/* XXX: check for name collisions */
193a0e087c3SAlex Hornung 	sc->name = kstrdup((char *)arg, M_LED);
194a0e087c3SAlex Hornung 	sc->pin = pin;
195a0e087c3SAlex Hornung 	sc->gp = gp;
196a0e087c3SAlex Hornung 
197a0e087c3SAlex Hornung 	sc->gp_map = gpio_map(gp, NULL, pin, 1);
198a0e087c3SAlex Hornung 	if (sc->gp_map == NULL) {
1992d12c69aSSascha Wildner 		lockmgr(&led_lock, LK_RELEASE);
200a0e087c3SAlex Hornung 		return 2;
201a0e087c3SAlex Hornung 	}
202a0e087c3SAlex Hornung 
203a0e087c3SAlex Hornung 	sc->unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(nled), 0);
204a0e087c3SAlex Hornung 
205a0e087c3SAlex Hornung 	LIST_INSERT_HEAD(&led_list, sc, list);
206a0e087c3SAlex Hornung 	sc->dev = make_dev(&nled_ops, sc->unit,
207a0e087c3SAlex Hornung 	    UID_ROOT, GID_WHEEL, 0600, "led/%s", sc->name);
208a0e087c3SAlex Hornung 	sc->dev->si_drv1 = sc;
209a0e087c3SAlex Hornung 	lockmgr(&led_lock, LK_RELEASE);
210a0e087c3SAlex Hornung 
211a0e087c3SAlex Hornung 	kprintf("gpio_led: Attached led '%s' to gpio %s, pin %d\n",
212a0e087c3SAlex Hornung 	    sc->name, sc->gp->driver_name, pin);
213a0e087c3SAlex Hornung 
214a0e087c3SAlex Hornung 	return 0;
215a0e087c3SAlex Hornung }
216a0e087c3SAlex Hornung 
21792b817bbSAlex Hornung static int
led_detach(struct gpio * gp,void * arg,int pin)218a0e087c3SAlex Hornung led_detach(struct gpio *gp, void *arg, int pin)
219a0e087c3SAlex Hornung {
220a0e087c3SAlex Hornung 	/* XXX: implement */
22192b817bbSAlex Hornung 	return 0;
222a0e087c3SAlex Hornung }
223a0e087c3SAlex Hornung 
224a0e087c3SAlex Hornung void
led_switch(const char * name,int on_off)225a0e087c3SAlex Hornung led_switch(const char *name, int on_off)
226a0e087c3SAlex Hornung {
227a0e087c3SAlex Hornung 	struct ledsc *sc;
228a0e087c3SAlex Hornung 
229a0e087c3SAlex Hornung 	if (name == NULL)
230a0e087c3SAlex Hornung 		return;
231a0e087c3SAlex Hornung 
232a0e087c3SAlex Hornung 	lockmgr(&led_lock, LK_EXCLUSIVE);
233a0e087c3SAlex Hornung 	LIST_FOREACH(sc, &led_list, list) {
234a0e087c3SAlex Hornung 		if (strcmp(name, sc->name) != 0)
235a0e087c3SAlex Hornung 			continue;
236a0e087c3SAlex Hornung 
237a0e087c3SAlex Hornung 		gpio_pin_write(sc->gp, sc->gp_map, 0, on_off);
238a0e087c3SAlex Hornung 		break;
239a0e087c3SAlex Hornung 	}
240a0e087c3SAlex Hornung 
241a0e087c3SAlex Hornung 	lockmgr(&led_lock, LK_RELEASE);
242a0e087c3SAlex Hornung }
243a0e087c3SAlex Hornung 
244a0e087c3SAlex Hornung struct gpio_consumer led_gpio_cons = {
245a0e087c3SAlex Hornung 	.consumer_name = "led",
246a0e087c3SAlex Hornung 	.consumer_attach = led_attach,
247a0e087c3SAlex Hornung 	.consumer_detach = led_detach,
248a0e087c3SAlex Hornung };
249a0e087c3SAlex Hornung 
250a0e087c3SAlex Hornung static void
led_drvinit(void * unused)251a0e087c3SAlex Hornung led_drvinit(void *unused)
252a0e087c3SAlex Hornung {
253a0e087c3SAlex Hornung 	lockinit(&led_lock, "led_lock", 0, 0);
254a0e087c3SAlex Hornung 	devfs_clone_bitmap_init(&DEVFS_CLONE_BITMAP(nled));
255a0e087c3SAlex Hornung 	gpio_consumer_register(&led_gpio_cons);
256a0e087c3SAlex Hornung }
257a0e087c3SAlex Hornung 
258a0e087c3SAlex Hornung SYSINIT(leddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, led_drvinit, NULL);
259