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