gpioc.c revision 299396
1/*-
2 * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/dev/gpio/gpioc.c 299396 2016-05-11 00:34:43Z gonzo $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/bus.h>
33#include <sys/conf.h>
34#include <sys/gpio.h>
35#include <sys/ioccom.h>
36#include <sys/kernel.h>
37#include <sys/malloc.h>
38#include <sys/module.h>
39
40#include <dev/gpio/gpiobusvar.h>
41
42#include "gpio_if.h"
43#include "gpiobus_if.h"
44
45#undef GPIOC_DEBUG
46#ifdef GPIOC_DEBUG
47#define dprintf printf
48#else
49#define dprintf(x, arg...)
50#endif
51
52static int gpioc_probe(device_t dev);
53static int gpioc_attach(device_t dev);
54static int gpioc_detach(device_t dev);
55
56static d_ioctl_t	gpioc_ioctl;
57
58static struct cdevsw gpioc_cdevsw = {
59	.d_version	= D_VERSION,
60	.d_ioctl	= gpioc_ioctl,
61	.d_name		= "gpioc",
62};
63
64struct gpioc_softc {
65	device_t	sc_dev;		/* gpiocX dev */
66	device_t	sc_pdev;	/* gpioX dev */
67	struct cdev	*sc_ctl_dev;	/* controller device */
68	int		sc_unit;
69};
70
71static int
72gpioc_probe(device_t dev)
73{
74	device_set_desc(dev, "GPIO controller");
75	return (0);
76}
77
78static int
79gpioc_attach(device_t dev)
80{
81	struct gpioc_softc *sc = device_get_softc(dev);
82
83	sc->sc_dev = dev;
84	sc->sc_pdev = device_get_parent(dev);
85	sc->sc_unit = device_get_unit(dev);
86	sc->sc_ctl_dev = make_dev(&gpioc_cdevsw, sc->sc_unit,
87	    UID_ROOT, GID_WHEEL, 0600, "gpioc%d", sc->sc_unit);
88	if (!sc->sc_ctl_dev) {
89		printf("Failed to create gpioc%d", sc->sc_unit);
90		return (ENXIO);
91	}
92	sc->sc_ctl_dev->si_drv1 = sc;
93
94	return (0);
95}
96
97static int
98gpioc_detach(device_t dev)
99{
100	struct gpioc_softc *sc = device_get_softc(dev);
101	int err;
102
103	if (sc->sc_ctl_dev)
104		destroy_dev(sc->sc_ctl_dev);
105
106	if ((err = bus_generic_detach(dev)) != 0)
107		return (err);
108
109	return (0);
110}
111
112static int
113gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag,
114    struct thread *td)
115{
116	device_t bus;
117	int max_pin, res;
118	struct gpioc_softc *sc = cdev->si_drv1;
119	struct gpio_pin pin;
120	struct gpio_req req;
121	uint32_t caps;
122
123	bus = GPIO_GET_BUS(sc->sc_pdev);
124	if (bus == NULL)
125		return (EINVAL);
126	switch (cmd) {
127		case GPIOMAXPIN:
128			max_pin = -1;
129			res = GPIO_PIN_MAX(sc->sc_pdev, &max_pin);
130			bcopy(&max_pin, arg, sizeof(max_pin));
131			break;
132		case GPIOGETCONFIG:
133			bcopy(arg, &pin, sizeof(pin));
134			dprintf("get config pin %d\n", pin.gp_pin);
135			res = GPIO_PIN_GETFLAGS(sc->sc_pdev, pin.gp_pin,
136			    &pin.gp_flags);
137			/* Fail early */
138			if (res)
139				break;
140			GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &pin.gp_caps);
141			GPIOBUS_PIN_GETNAME(bus, pin.gp_pin, pin.gp_name);
142			bcopy(&pin, arg, sizeof(pin));
143			break;
144		case GPIOSETCONFIG:
145			bcopy(arg, &pin, sizeof(pin));
146			dprintf("set config pin %d\n", pin.gp_pin);
147			res = GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &caps);
148			if (res == 0)
149				res = gpio_check_flags(caps, pin.gp_flags);
150			if (res == 0)
151				res = GPIO_PIN_SETFLAGS(sc->sc_pdev, pin.gp_pin,
152				    pin.gp_flags);
153			break;
154		case GPIOGET:
155			bcopy(arg, &req, sizeof(req));
156			res = GPIO_PIN_GET(sc->sc_pdev, req.gp_pin,
157			    &req.gp_value);
158			dprintf("read pin %d -> %d\n",
159			    req.gp_pin, req.gp_value);
160			bcopy(&req, arg, sizeof(req));
161			break;
162		case GPIOSET:
163			bcopy(arg, &req, sizeof(req));
164			res = GPIO_PIN_SET(sc->sc_pdev, req.gp_pin,
165			    req.gp_value);
166			dprintf("write pin %d -> %d\n",
167			    req.gp_pin, req.gp_value);
168			break;
169		case GPIOTOGGLE:
170			bcopy(arg, &req, sizeof(req));
171			dprintf("toggle pin %d\n",
172			    req.gp_pin);
173			res = GPIO_PIN_TOGGLE(sc->sc_pdev, req.gp_pin);
174			break;
175		case GPIOSETNAME:
176			bcopy(arg, &pin, sizeof(pin));
177			dprintf("set name on pin %d\n", pin.gp_pin);
178			res = GPIOBUS_PIN_SETNAME(bus, pin.gp_pin,
179			    pin.gp_name);
180			break;
181		default:
182			return (ENOTTY);
183			break;
184	}
185
186	return (res);
187}
188
189static device_method_t gpioc_methods[] = {
190	/* Device interface */
191	DEVMETHOD(device_probe,		gpioc_probe),
192	DEVMETHOD(device_attach,	gpioc_attach),
193	DEVMETHOD(device_detach,	gpioc_detach),
194	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
195	DEVMETHOD(device_suspend,	bus_generic_suspend),
196	DEVMETHOD(device_resume,	bus_generic_resume),
197
198	DEVMETHOD_END
199};
200
201driver_t gpioc_driver = {
202	"gpioc",
203	gpioc_methods,
204	sizeof(struct gpioc_softc)
205};
206
207devclass_t	gpioc_devclass;
208
209DRIVER_MODULE(gpioc, gpio, gpioc_driver, gpioc_devclass, 0, 0);
210MODULE_VERSION(gpioc, 1);
211