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