xref: /dflybsd-src/sys/dev/misc/gpio/gpio.c (revision fcf6efefc03a35111797b109fa4994034ebe39ba)
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