| @@ -1,1187 +1,1185 @@ | | | @@ -1,1187 +1,1185 @@ |
1 | /* $NetBSD: gpio.c,v 1.64.10.4 2021/03/21 18:03:32 thorpej Exp $ */ | | 1 | /* $NetBSD: gpio.c,v 1.64.10.5 2021/03/28 20:40:30 thorpej Exp $ */ |
2 | /* $OpenBSD: gpio.c,v 1.6 2006/01/14 12:33:49 grange Exp $ */ | | 2 | /* $OpenBSD: gpio.c,v 1.6 2006/01/14 12:33:49 grange Exp $ */ |
3 | | | 3 | |
4 | /* | | 4 | /* |
5 | * Copyright (c) 2008, 2009, 2010, 2011 Marc Balmer <marc@msys.ch> | | 5 | * Copyright (c) 2008, 2009, 2010, 2011 Marc Balmer <marc@msys.ch> |
6 | * Copyright (c) 2004, 2006 Alexander Yurchenko <grange@openbsd.org> | | 6 | * Copyright (c) 2004, 2006 Alexander Yurchenko <grange@openbsd.org> |
7 | * | | 7 | * |
8 | * Permission to use, copy, modify, and distribute this software for any | | 8 | * Permission to use, copy, modify, and distribute this software for any |
9 | * purpose with or without fee is hereby granted, provided that the above | | 9 | * purpose with or without fee is hereby granted, provided that the above |
10 | * copyright notice and this permission notice appear in all copies. | | 10 | * copyright notice and this permission notice appear in all copies. |
11 | * | | 11 | * |
12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | | 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
13 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | | 13 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
14 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | | 14 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
15 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | | 15 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
16 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | | 16 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
17 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | | 17 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
18 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | | 18 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
19 | */ | | 19 | */ |
20 | | | 20 | |
21 | #include <sys/cdefs.h> | | 21 | #include <sys/cdefs.h> |
22 | __KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.64.10.4 2021/03/21 18:03:32 thorpej Exp $"); | | 22 | __KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.64.10.5 2021/03/28 20:40:30 thorpej Exp $"); |
23 | | | 23 | |
24 | /* | | 24 | /* |
25 | * General Purpose Input/Output framework. | | 25 | * General Purpose Input/Output framework. |
26 | */ | | 26 | */ |
27 | | | 27 | |
28 | #include <sys/param.h> | | 28 | #include <sys/param.h> |
29 | #include <sys/callout.h> | | 29 | #include <sys/callout.h> |
30 | #include <sys/systm.h> | | 30 | #include <sys/systm.h> |
31 | #include <sys/conf.h> | | 31 | #include <sys/conf.h> |
32 | #include <sys/device.h> | | 32 | #include <sys/device.h> |
33 | #include <sys/fcntl.h> | | 33 | #include <sys/fcntl.h> |
34 | #include <sys/ioctl.h> | | 34 | #include <sys/ioctl.h> |
35 | #include <sys/gpio.h> | | 35 | #include <sys/gpio.h> |
36 | #include <sys/kernel.h> | | 36 | #include <sys/kernel.h> |
37 | #include <sys/vnode.h> | | 37 | #include <sys/vnode.h> |
38 | #include <sys/kmem.h> | | 38 | #include <sys/kmem.h> |
39 | #include <sys/mutex.h> | | 39 | #include <sys/mutex.h> |
40 | #include <sys/condvar.h> | | 40 | #include <sys/condvar.h> |
41 | #include <sys/queue.h> | | 41 | #include <sys/queue.h> |
42 | #include <sys/kauth.h> | | 42 | #include <sys/kauth.h> |
43 | #include <sys/module.h> | | 43 | #include <sys/module.h> |
44 | #include <dev/gpio/gpiovar.h> | | 44 | #include <dev/gpio/gpiovar.h> |
45 | | | 45 | |
46 | #include "ioconf.h" | | 46 | #include "ioconf.h" |
47 | #include "locators.h" | | 47 | #include "locators.h" |
48 | | | 48 | |
49 | #ifdef GPIO_DEBUG | | 49 | #ifdef GPIO_DEBUG |
50 | #define DPRINTFN(n, x) do { if (gpiodebug > (n)) printf x; } while (0) | | 50 | #define DPRINTFN(n, x) do { if (gpiodebug > (n)) printf x; } while (0) |
51 | int gpiodebug = 0; | | 51 | int gpiodebug = 0; |
52 | #else | | 52 | #else |
53 | #define DPRINTFN(n, x) | | 53 | #define DPRINTFN(n, x) |
54 | #endif | | 54 | #endif |
55 | #define DPRINTF(x) DPRINTFN(0, x) | | 55 | #define DPRINTF(x) DPRINTFN(0, x) |
56 | | | 56 | |
57 | struct gpio_softc { | | 57 | struct gpio_softc { |
58 | device_t sc_dev; | | 58 | device_t sc_dev; |
59 | | | 59 | |
60 | gpio_chipset_tag_t sc_gc; /* GPIO controller */ | | 60 | gpio_chipset_tag_t sc_gc; /* GPIO controller */ |
61 | gpio_pin_t *sc_pins; /* pins array */ | | 61 | gpio_pin_t *sc_pins; /* pins array */ |
62 | int sc_npins; /* number of pins */ | | 62 | int sc_npins; /* number of pins */ |
63 | | | 63 | |
64 | kmutex_t sc_mtx; | | 64 | kmutex_t sc_mtx; |
65 | kcondvar_t sc_ioctl; /* ioctl in progress */ | | 65 | kcondvar_t sc_ioctl; /* ioctl in progress */ |
66 | int sc_ioctl_busy; /* ioctl is busy */ | | 66 | int sc_ioctl_busy; /* ioctl is busy */ |
67 | kcondvar_t sc_attach; /* attach/detach in progress */ | | 67 | kcondvar_t sc_attach; /* attach/detach in progress */ |
68 | int sc_attach_busy;/* busy in attach/detach */ | | 68 | int sc_attach_busy;/* busy in attach/detach */ |
69 | #ifdef COMPAT_50 | | 69 | #ifdef COMPAT_50 |
70 | LIST_HEAD(, gpio_dev) sc_devs; /* devices */ | | 70 | LIST_HEAD(, gpio_dev) sc_devs; /* devices */ |
71 | #endif | | 71 | #endif |
72 | LIST_HEAD(, gpio_name) sc_names; /* named pins */ | | 72 | LIST_HEAD(, gpio_name) sc_names; /* named pins */ |
73 | }; | | 73 | }; |
74 | | | 74 | |
75 | static int gpio_match(device_t, cfdata_t, void *); | | 75 | static int gpio_match(device_t, cfdata_t, void *); |
76 | int gpio_submatch(device_t, cfdata_t, const int *, void *); | | 76 | int gpio_submatch(device_t, cfdata_t, const int *, void *); |
77 | static void gpio_attach(device_t, device_t, void *); | | 77 | static void gpio_attach(device_t, device_t, void *); |
78 | static int gpio_rescan(device_t, const char *, const int *); | | 78 | static int gpio_rescan(device_t, const char *, const int *); |
79 | static void gpio_childdetached(device_t, device_t); | | 79 | static void gpio_childdetached(device_t, device_t); |
80 | static bool gpio_resume(device_t, const pmf_qual_t *); | | 80 | static bool gpio_resume(device_t, const pmf_qual_t *); |
81 | static int gpio_detach(device_t, int); | | 81 | static int gpio_detach(device_t, int); |
82 | static int gpio_search(device_t, cfdata_t, const int *, void *); | | 82 | static int gpio_search(device_t, cfdata_t, const int *, void *); |
83 | static int gpio_print(void *, const char *); | | 83 | static int gpio_print(void *, const char *); |
84 | static int gpio_pinbyname(struct gpio_softc *, char *); | | 84 | static int gpio_pinbyname(struct gpio_softc *, char *); |
85 | static int gpio_ioctl(struct gpio_softc *, u_long, void *, int, | | 85 | static int gpio_ioctl(struct gpio_softc *, u_long, void *, int, |
86 | struct lwp *); | | 86 | struct lwp *); |
87 | | | 87 | |
88 | #ifdef COMPAT_50 | | 88 | #ifdef COMPAT_50 |
89 | /* Old API */ | | 89 | /* Old API */ |
90 | static int gpio_ioctl_oapi(struct gpio_softc *, u_long, void *, int, | | 90 | static int gpio_ioctl_oapi(struct gpio_softc *, u_long, void *, int, |
91 | kauth_cred_t); | | 91 | kauth_cred_t); |
92 | #endif | | 92 | #endif |
93 | | | 93 | |
94 | CFATTACH_DECL3_NEW(gpio, sizeof(struct gpio_softc), | | 94 | CFATTACH_DECL3_NEW(gpio, sizeof(struct gpio_softc), |
95 | gpio_match, gpio_attach, gpio_detach, NULL, gpio_rescan, | | 95 | gpio_match, gpio_attach, gpio_detach, NULL, gpio_rescan, |
96 | gpio_childdetached, DVF_DETACH_SHUTDOWN); | | 96 | gpio_childdetached, DVF_DETACH_SHUTDOWN); |
97 | | | 97 | |
98 | dev_type_open(gpioopen); | | 98 | dev_type_open(gpioopen); |
99 | dev_type_close(gpioclose); | | 99 | dev_type_close(gpioclose); |
100 | dev_type_ioctl(gpioioctl); | | 100 | dev_type_ioctl(gpioioctl); |
101 | dev_type_ioctl(gpioioctl_locked); | | 101 | dev_type_ioctl(gpioioctl_locked); |
102 | | | 102 | |
103 | const struct cdevsw gpio_cdevsw = { | | 103 | const struct cdevsw gpio_cdevsw = { |
104 | .d_open = gpioopen, | | 104 | .d_open = gpioopen, |
105 | .d_close = gpioclose, | | 105 | .d_close = gpioclose, |
106 | .d_read = noread, | | 106 | .d_read = noread, |
107 | .d_write = nowrite, | | 107 | .d_write = nowrite, |
108 | .d_ioctl = gpioioctl, | | 108 | .d_ioctl = gpioioctl, |
109 | .d_stop = nostop, | | 109 | .d_stop = nostop, |
110 | .d_tty = notty, | | 110 | .d_tty = notty, |
111 | .d_poll = nopoll, | | 111 | .d_poll = nopoll, |
112 | .d_mmap = nommap, | | 112 | .d_mmap = nommap, |
113 | .d_kqfilter = nokqfilter, | | 113 | .d_kqfilter = nokqfilter, |
114 | .d_discard = nodiscard, | | 114 | .d_discard = nodiscard, |
115 | .d_flag = D_OTHER | D_MPSAFE | | 115 | .d_flag = D_OTHER | D_MPSAFE |
116 | }; | | 116 | }; |
117 | | | 117 | |
118 | static int | | 118 | static int |
119 | gpio_match(device_t parent, cfdata_t cf, void *aux) | | 119 | gpio_match(device_t parent, cfdata_t cf, void *aux) |
120 | { | | 120 | { |
121 | return 1; | | 121 | return 1; |
122 | } | | 122 | } |
123 | | | 123 | |
124 | int | | 124 | int |
125 | gpio_submatch(device_t parent, cfdata_t cf, const int *ip, void *aux) | | 125 | gpio_submatch(device_t parent, cfdata_t cf, const int *ip, void *aux) |
126 | { | | 126 | { |
127 | struct gpio_attach_args *ga = aux; | | 127 | struct gpio_attach_args *ga = aux; |
128 | | | 128 | |
129 | if (ga->ga_offset == -1) | | 129 | if (ga->ga_offset == -1) |
130 | return 0; | | 130 | return 0; |
131 | | | 131 | |
132 | return strcmp(ga->ga_dvname, cf->cf_name) == 0; | | 132 | return strcmp(ga->ga_dvname, cf->cf_name) == 0; |
133 | } | | 133 | } |
134 | | | 134 | |
135 | static bool | | 135 | static bool |
136 | gpio_resume(device_t self, const pmf_qual_t *qual) | | 136 | gpio_resume(device_t self, const pmf_qual_t *qual) |
137 | { | | 137 | { |
138 | struct gpio_softc *sc = device_private(self); | | 138 | struct gpio_softc *sc = device_private(self); |
139 | int pin; | | 139 | int pin; |
140 | | | 140 | |
141 | for (pin = 0; pin < sc->sc_npins; pin++) { | | 141 | for (pin = 0; pin < sc->sc_npins; pin++) { |
142 | gpiobus_pin_ctl(sc->sc_gc, pin, sc->sc_pins[pin].pin_flags); | | 142 | gpiobus_pin_ctl(sc->sc_gc, pin, sc->sc_pins[pin].pin_flags); |
143 | gpiobus_pin_write(sc->sc_gc, pin, sc->sc_pins[pin].pin_state); | | 143 | gpiobus_pin_write(sc->sc_gc, pin, sc->sc_pins[pin].pin_state); |
144 | } | | 144 | } |
145 | return true; | | 145 | return true; |
146 | } | | 146 | } |
147 | | | 147 | |
148 | static void | | 148 | static void |
149 | gpio_childdetached(device_t self, device_t child) | | 149 | gpio_childdetached(device_t self, device_t child) |
150 | { | | 150 | { |
151 | #ifdef COMPAT_50 | | 151 | #ifdef COMPAT_50 |
152 | struct gpio_dev *gdev; | | 152 | struct gpio_dev *gdev; |
153 | struct gpio_softc *sc; | | 153 | struct gpio_softc *sc; |
154 | int error; | | 154 | int error; |
155 | | | 155 | |
156 | /* | | 156 | /* |
157 | * gpio_childetached is serialized because it can be entered in | | 157 | * gpio_childetached is serialized because it can be entered in |
158 | * different ways concurrently, e.g. via the GPIODETACH ioctl and | | 158 | * different ways concurrently, e.g. via the GPIODETACH ioctl and |
159 | * drvctl(8) or modunload(8). | | 159 | * drvctl(8) or modunload(8). |
160 | */ | | 160 | */ |
161 | sc = device_private(self); | | 161 | sc = device_private(self); |
162 | error = 0; | | 162 | error = 0; |
163 | mutex_enter(&sc->sc_mtx); | | 163 | mutex_enter(&sc->sc_mtx); |
164 | while (sc->sc_attach_busy) { | | 164 | while (sc->sc_attach_busy) { |
165 | error = cv_wait_sig(&sc->sc_attach, &sc->sc_mtx); | | 165 | error = cv_wait_sig(&sc->sc_attach, &sc->sc_mtx); |
166 | if (error) | | 166 | if (error) |
167 | break; | | 167 | break; |
168 | } | | 168 | } |
169 | if (!error) | | 169 | if (!error) |
170 | sc->sc_attach_busy = 1; | | 170 | sc->sc_attach_busy = 1; |
171 | mutex_exit(&sc->sc_mtx); | | 171 | mutex_exit(&sc->sc_mtx); |
172 | if (error) | | 172 | if (error) |
173 | return; | | 173 | return; |
174 | | | 174 | |
175 | LIST_FOREACH(gdev, &sc->sc_devs, sc_next) | | 175 | LIST_FOREACH(gdev, &sc->sc_devs, sc_next) |
176 | if (gdev->sc_dev == child) { | | 176 | if (gdev->sc_dev == child) { |
177 | LIST_REMOVE(gdev, sc_next); | | 177 | LIST_REMOVE(gdev, sc_next); |
178 | kmem_free(gdev, sizeof(struct gpio_dev)); | | 178 | kmem_free(gdev, sizeof(struct gpio_dev)); |
179 | break; | | 179 | break; |
180 | } | | 180 | } |
181 | | | 181 | |
182 | mutex_enter(&sc->sc_mtx); | | 182 | mutex_enter(&sc->sc_mtx); |
183 | sc->sc_attach_busy = 0; | | 183 | sc->sc_attach_busy = 0; |
184 | cv_signal(&sc->sc_attach); | | 184 | cv_signal(&sc->sc_attach); |
185 | mutex_exit(&sc->sc_mtx); | | 185 | mutex_exit(&sc->sc_mtx); |
186 | #endif | | 186 | #endif |
187 | } | | 187 | } |
188 | | | 188 | |
189 | static int | | 189 | static int |
190 | gpio_rescan(device_t self, const char *ifattr, const int *locators) | | 190 | gpio_rescan(device_t self, const char *ifattr, const int *locators) |
191 | { | | 191 | { |
192 | | | 192 | |
193 | config_search(self, NULL, | | 193 | config_search(self, NULL, |
194 | CFARG_SUBMATCH, gpio_search, | | 194 | CFARG_SUBMATCH, gpio_search, |
195 | CFARG_IATTR, ifattr, | | | |
196 | CFARG_LOCATORS, locators, | | | |
197 | CFARG_EOL); | | 195 | CFARG_EOL); |
198 | | | 196 | |
199 | return 0; | | 197 | return 0; |
200 | } | | 198 | } |
201 | | | 199 | |
202 | static void | | 200 | static void |
203 | gpio_attach(device_t parent, device_t self, void *aux) | | 201 | gpio_attach(device_t parent, device_t self, void *aux) |
204 | { | | 202 | { |
205 | struct gpio_softc *sc = device_private(self); | | 203 | struct gpio_softc *sc = device_private(self); |
206 | struct gpiobus_attach_args *gba = aux; | | 204 | struct gpiobus_attach_args *gba = aux; |
207 | struct gpio_name *nm; | | 205 | struct gpio_name *nm; |
208 | int pin; | | 206 | int pin; |
209 | | | 207 | |
210 | sc->sc_dev = self; | | 208 | sc->sc_dev = self; |
211 | sc->sc_gc = gba->gba_gc; | | 209 | sc->sc_gc = gba->gba_gc; |
212 | sc->sc_pins = gba->gba_pins; | | 210 | sc->sc_pins = gba->gba_pins; |
213 | sc->sc_npins = gba->gba_npins; | | 211 | sc->sc_npins = gba->gba_npins; |
214 | | | 212 | |
215 | aprint_normal(": %d pins\n", sc->sc_npins); | | 213 | aprint_normal(": %d pins\n", sc->sc_npins); |
216 | aprint_naive("\n"); | | 214 | aprint_naive("\n"); |
217 | | | 215 | |
218 | /* Configure default pin names */ | | 216 | /* Configure default pin names */ |
219 | for (pin = 0; pin < sc->sc_npins; pin++) { | | 217 | for (pin = 0; pin < sc->sc_npins; pin++) { |
220 | if (sc->sc_pins[pin].pin_defname[0] == '\0') | | 218 | if (sc->sc_pins[pin].pin_defname[0] == '\0') |
221 | continue; | | 219 | continue; |
222 | nm = kmem_alloc(sizeof(*nm), KM_SLEEP); | | 220 | nm = kmem_alloc(sizeof(*nm), KM_SLEEP); |
223 | strlcpy(nm->gp_name, sc->sc_pins[pin].pin_defname, | | 221 | strlcpy(nm->gp_name, sc->sc_pins[pin].pin_defname, |
224 | sizeof(nm->gp_name)); | | 222 | sizeof(nm->gp_name)); |
225 | nm->gp_pin = pin; | | 223 | nm->gp_pin = pin; |
226 | LIST_INSERT_HEAD(&sc->sc_names, nm, gp_next); | | 224 | LIST_INSERT_HEAD(&sc->sc_names, nm, gp_next); |
227 | } | | 225 | } |
228 | | | 226 | |
229 | if (!pmf_device_register(self, NULL, gpio_resume)) | | 227 | if (!pmf_device_register(self, NULL, gpio_resume)) |
230 | aprint_error_dev(self, "couldn't establish power handler\n"); | | 228 | aprint_error_dev(self, "couldn't establish power handler\n"); |
231 | mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_VM); | | 229 | mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_VM); |
232 | cv_init(&sc->sc_ioctl, "gpioctl"); | | 230 | cv_init(&sc->sc_ioctl, "gpioctl"); |
233 | cv_init(&sc->sc_attach, "gpioatch"); | | 231 | cv_init(&sc->sc_attach, "gpioatch"); |
234 | /* | | 232 | /* |
235 | * Attach all devices that can be connected to the GPIO pins | | 233 | * Attach all devices that can be connected to the GPIO pins |
236 | * described in the kernel configuration file. | | 234 | * described in the kernel configuration file. |
237 | */ | | 235 | */ |
238 | gpio_rescan(self, "gpio", NULL); | | 236 | gpio_rescan(self, "gpio", NULL); |
239 | } | | 237 | } |
240 | | | 238 | |
241 | static int | | 239 | static int |
242 | gpio_detach(device_t self, int flags) | | 240 | gpio_detach(device_t self, int flags) |
243 | { | | 241 | { |
244 | struct gpio_softc *sc; | | 242 | struct gpio_softc *sc; |
245 | int rc; | | 243 | int rc; |
246 | | | 244 | |
247 | sc = device_private(self); | | 245 | sc = device_private(self); |
248 | | | 246 | |
249 | if ((rc = config_detach_children(self, flags)) != 0) | | 247 | if ((rc = config_detach_children(self, flags)) != 0) |
250 | return rc; | | 248 | return rc; |
251 | mutex_destroy(&sc->sc_mtx); | | 249 | mutex_destroy(&sc->sc_mtx); |
252 | cv_destroy(&sc->sc_ioctl); | | 250 | cv_destroy(&sc->sc_ioctl); |
253 | #if 0 | | 251 | #if 0 |
254 | int maj, mn; | | 252 | int maj, mn; |
255 | | | 253 | |
256 | /* Locate the major number */ | | 254 | /* Locate the major number */ |
257 | for (maj = 0; maj < nchrdev; maj++) | | 255 | for (maj = 0; maj < nchrdev; maj++) |
258 | if (cdevsw[maj].d_open == gpioopen) | | 256 | if (cdevsw[maj].d_open == gpioopen) |
259 | break; | | 257 | break; |
260 | | | 258 | |
261 | /* Nuke the vnodes for any open instances (calls close) */ | | 259 | /* Nuke the vnodes for any open instances (calls close) */ |
262 | mn = device_unit(self); | | 260 | mn = device_unit(self); |
263 | vdevgone(maj, mn, mn, VCHR); | | 261 | vdevgone(maj, mn, mn, VCHR); |
264 | #endif | | 262 | #endif |
265 | return 0; | | 263 | return 0; |
266 | } | | 264 | } |
267 | | | 265 | |
268 | static int | | 266 | static int |
269 | gpio_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux) | | 267 | gpio_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux) |
270 | { | | 268 | { |
271 | struct gpio_attach_args ga; | | 269 | struct gpio_attach_args ga; |
272 | size_t namlen; | | 270 | size_t namlen; |
273 | | | 271 | |
274 | ga.ga_gpio = device_private(parent); | | 272 | ga.ga_gpio = device_private(parent); |
275 | ga.ga_offset = cf->cf_loc[GPIOCF_OFFSET]; | | 273 | ga.ga_offset = cf->cf_loc[GPIOCF_OFFSET]; |
276 | ga.ga_mask = cf->cf_loc[GPIOCF_MASK]; | | 274 | ga.ga_mask = cf->cf_loc[GPIOCF_MASK]; |
277 | ga.ga_flags = cf->cf_loc[GPIOCF_FLAG]; | | 275 | ga.ga_flags = cf->cf_loc[GPIOCF_FLAG]; |
278 | namlen = strlen(cf->cf_name) + 1; | | 276 | namlen = strlen(cf->cf_name) + 1; |
279 | ga.ga_dvname = kmem_alloc(namlen, KM_SLEEP); | | 277 | ga.ga_dvname = kmem_alloc(namlen, KM_SLEEP); |
280 | strcpy(ga.ga_dvname, cf->cf_name); | | 278 | strcpy(ga.ga_dvname, cf->cf_name); |
281 | | | 279 | |
282 | if (config_match(parent, cf, &ga) > 0) | | 280 | if (config_match(parent, cf, &ga) > 0) |
283 | config_attach(parent, cf, &ga, gpio_print); | | 281 | config_attach(parent, cf, &ga, gpio_print); |
284 | kmem_free(ga.ga_dvname, namlen); | | 282 | kmem_free(ga.ga_dvname, namlen); |
285 | return 0; | | 283 | return 0; |
286 | } | | 284 | } |
287 | | | 285 | |
288 | int | | 286 | int |
289 | gpio_print(void *aux, const char *pnp) | | 287 | gpio_print(void *aux, const char *pnp) |
290 | { | | 288 | { |
291 | struct gpio_attach_args *ga = aux; | | 289 | struct gpio_attach_args *ga = aux; |
292 | int i; | | 290 | int i; |
293 | | | 291 | |
294 | aprint_normal(" pins"); | | 292 | aprint_normal(" pins"); |
295 | for (i = 0; i < 32; i++) | | 293 | for (i = 0; i < 32; i++) |
296 | if (ga->ga_mask & (1 << i)) | | 294 | if (ga->ga_mask & (1 << i)) |
297 | aprint_normal(" %d", ga->ga_offset + i); | | 295 | aprint_normal(" %d", ga->ga_offset + i); |
298 | | | 296 | |
299 | return UNCONF; | | 297 | return UNCONF; |
300 | } | | 298 | } |
301 | | | 299 | |
302 | int | | 300 | int |
303 | gpiobus_print(void *aux, const char *pnp) | | 301 | gpiobus_print(void *aux, const char *pnp) |
304 | { | | 302 | { |
305 | #if 0 | | 303 | #if 0 |
306 | struct gpiobus_attach_args *gba = aux; | | 304 | struct gpiobus_attach_args *gba = aux; |
307 | #endif | | 305 | #endif |
308 | if (pnp != NULL) | | 306 | if (pnp != NULL) |
309 | aprint_normal("gpiobus at %s", pnp); | | 307 | aprint_normal("gpiobus at %s", pnp); |
310 | | | 308 | |
311 | return UNCONF; | | 309 | return UNCONF; |
312 | } | | 310 | } |
313 | | | 311 | |
314 | void * | | 312 | void * |
315 | gpio_find_device(const char *name) | | 313 | gpio_find_device(const char *name) |
316 | { | | 314 | { |
317 | device_t gpio_dev; | | 315 | device_t gpio_dev; |
318 | gpio_dev = device_find_by_xname(name); | | 316 | gpio_dev = device_find_by_xname(name); |
319 | if (gpio_dev == NULL) | | 317 | if (gpio_dev == NULL) |
320 | return NULL; | | 318 | return NULL; |
321 | return device_private(gpio_dev); | | 319 | return device_private(gpio_dev); |
322 | } | | 320 | } |
323 | | | 321 | |
324 | const char * | | 322 | const char * |
325 | gpio_get_name(void *gpio) | | 323 | gpio_get_name(void *gpio) |
326 | { | | 324 | { |
327 | struct gpio_softc *sc = gpio; | | 325 | struct gpio_softc *sc = gpio; |
328 | return device_xname(sc->sc_dev); | | 326 | return device_xname(sc->sc_dev); |
329 | } | | 327 | } |
330 | | | 328 | |
331 | /* return 1 if all pins can be mapped, 0 if not */ | | 329 | /* return 1 if all pins can be mapped, 0 if not */ |
332 | int | | 330 | int |
333 | gpio_pin_can_map(void *gpio, int offset, uint32_t mask) | | 331 | gpio_pin_can_map(void *gpio, int offset, uint32_t mask) |
334 | { | | 332 | { |
335 | struct gpio_softc *sc = gpio; | | 333 | struct gpio_softc *sc = gpio; |
336 | int npins, pin, i; | | 334 | int npins, pin, i; |
337 | | | 335 | |
338 | npins = gpio_npins(mask); | | 336 | npins = gpio_npins(mask); |
339 | if (npins > sc->sc_npins) | | 337 | if (npins > sc->sc_npins) |
340 | return 0; | | 338 | return 0; |
341 | | | 339 | |
342 | for (npins = 0, i = 0; i < 32; i++) | | 340 | for (npins = 0, i = 0; i < 32; i++) |
343 | if (mask & (1 << i)) { | | 341 | if (mask & (1 << i)) { |
344 | pin = offset + i; | | 342 | pin = offset + i; |
345 | if (pin < 0 || pin >= sc->sc_npins) | | 343 | if (pin < 0 || pin >= sc->sc_npins) |
346 | return 0; | | 344 | return 0; |
347 | if (sc->sc_pins[pin].pin_mapped) | | 345 | if (sc->sc_pins[pin].pin_mapped) |
348 | return 0; | | 346 | return 0; |
349 | } | | 347 | } |
350 | | | 348 | |
351 | return 1; | | 349 | return 1; |
352 | } | | 350 | } |
353 | | | 351 | |
354 | int | | 352 | int |
355 | gpio_pin_map(void *gpio, int offset, uint32_t mask, struct gpio_pinmap *map) | | 353 | gpio_pin_map(void *gpio, int offset, uint32_t mask, struct gpio_pinmap *map) |
356 | { | | 354 | { |
357 | struct gpio_softc *sc = gpio; | | 355 | struct gpio_softc *sc = gpio; |
358 | int npins, pin, i; | | 356 | int npins, pin, i; |
359 | | | 357 | |
360 | npins = gpio_npins(mask); | | 358 | npins = gpio_npins(mask); |
361 | if (npins > sc->sc_npins) | | 359 | if (npins > sc->sc_npins) |
362 | return 1; | | 360 | return 1; |
363 | | | 361 | |
364 | for (npins = 0, i = 0; i < 32; i++) | | 362 | for (npins = 0, i = 0; i < 32; i++) |
365 | if (mask & (1 << i)) { | | 363 | if (mask & (1 << i)) { |
366 | pin = offset + i; | | 364 | pin = offset + i; |
367 | if (pin < 0 || pin >= sc->sc_npins) | | 365 | if (pin < 0 || pin >= sc->sc_npins) |
368 | return 1; | | 366 | return 1; |
369 | if (sc->sc_pins[pin].pin_mapped) | | 367 | if (sc->sc_pins[pin].pin_mapped) |
370 | return 1; | | 368 | return 1; |
371 | sc->sc_pins[pin].pin_mapped = 1; | | 369 | sc->sc_pins[pin].pin_mapped = 1; |
372 | map->pm_map[npins++] = pin; | | 370 | map->pm_map[npins++] = pin; |
373 | } | | 371 | } |
374 | map->pm_size = npins; | | 372 | map->pm_size = npins; |
375 | | | 373 | |
376 | return 0; | | 374 | return 0; |
377 | } | | 375 | } |
378 | | | 376 | |
379 | void | | 377 | void |
380 | gpio_pin_unmap(void *gpio, struct gpio_pinmap *map) | | 378 | gpio_pin_unmap(void *gpio, struct gpio_pinmap *map) |
381 | { | | 379 | { |
382 | struct gpio_softc *sc = gpio; | | 380 | struct gpio_softc *sc = gpio; |
383 | int pin, i; | | 381 | int pin, i; |
384 | | | 382 | |
385 | for (i = 0; i < map->pm_size; i++) { | | 383 | for (i = 0; i < map->pm_size; i++) { |
386 | pin = map->pm_map[i]; | | 384 | pin = map->pm_map[i]; |
387 | sc->sc_pins[pin].pin_mapped = 0; | | 385 | sc->sc_pins[pin].pin_mapped = 0; |
388 | } | | 386 | } |
389 | } | | 387 | } |
390 | | | 388 | |
391 | int | | 389 | int |
392 | gpio_pin_read(void *gpio, struct gpio_pinmap *map, int pin) | | 390 | gpio_pin_read(void *gpio, struct gpio_pinmap *map, int pin) |
393 | { | | 391 | { |
394 | struct gpio_softc *sc = gpio; | | 392 | struct gpio_softc *sc = gpio; |
395 | | | 393 | |
396 | return gpiobus_pin_read(sc->sc_gc, map->pm_map[pin]); | | 394 | return gpiobus_pin_read(sc->sc_gc, map->pm_map[pin]); |
397 | } | | 395 | } |
398 | | | 396 | |
399 | void | | 397 | void |
400 | gpio_pin_write(void *gpio, struct gpio_pinmap *map, int pin, int value) | | 398 | gpio_pin_write(void *gpio, struct gpio_pinmap *map, int pin, int value) |
401 | { | | 399 | { |
402 | struct gpio_softc *sc = gpio; | | 400 | struct gpio_softc *sc = gpio; |
403 | | | 401 | |
404 | gpiobus_pin_write(sc->sc_gc, map->pm_map[pin], value); | | 402 | gpiobus_pin_write(sc->sc_gc, map->pm_map[pin], value); |
405 | sc->sc_pins[map->pm_map[pin]].pin_state = value; | | 403 | sc->sc_pins[map->pm_map[pin]].pin_state = value; |
406 | } | | 404 | } |
407 | | | 405 | |
408 | int | | 406 | int |
409 | gpio_pin_get_conf(void *gpio, struct gpio_pinmap *map, int pin) | | 407 | gpio_pin_get_conf(void *gpio, struct gpio_pinmap *map, int pin) |
410 | { | | 408 | { |
411 | struct gpio_softc *sc = gpio; | | 409 | struct gpio_softc *sc = gpio; |
412 | int rv; | | 410 | int rv; |
413 | | | 411 | |
414 | mutex_enter(&sc->sc_mtx); | | 412 | mutex_enter(&sc->sc_mtx); |
415 | rv = sc->sc_pins[map->pm_map[pin]].pin_flags; | | 413 | rv = sc->sc_pins[map->pm_map[pin]].pin_flags; |
416 | mutex_exit(&sc->sc_mtx); | | 414 | mutex_exit(&sc->sc_mtx); |
417 | | | 415 | |
418 | return (rv); | | 416 | return (rv); |
419 | } | | 417 | } |
420 | | | 418 | |
421 | bool | | 419 | bool |
422 | gpio_pin_set_conf(void *gpio, struct gpio_pinmap *map, int pin, int flags) | | 420 | gpio_pin_set_conf(void *gpio, struct gpio_pinmap *map, int pin, int flags) |
423 | { | | 421 | { |
424 | struct gpio_softc *sc = gpio; | | 422 | struct gpio_softc *sc = gpio; |
425 | int checkflags = flags & GPIO_PIN_HWCAPS; | | 423 | int checkflags = flags & GPIO_PIN_HWCAPS; |
426 | | | 424 | |
427 | if ((sc->sc_pins[map->pm_map[pin]].pin_caps & checkflags) != checkflags) | | 425 | if ((sc->sc_pins[map->pm_map[pin]].pin_caps & checkflags) != checkflags) |
428 | return (false); | | 426 | return (false); |
429 | | | 427 | |
430 | gpio_pin_ctl(gpio, map, pin, flags); | | 428 | gpio_pin_ctl(gpio, map, pin, flags); |
431 | | | 429 | |
432 | return (true); | | 430 | return (true); |
433 | } | | 431 | } |
434 | | | 432 | |
435 | void | | 433 | void |
436 | gpio_pin_ctl(void *gpio, struct gpio_pinmap *map, int pin, int flags) | | 434 | gpio_pin_ctl(void *gpio, struct gpio_pinmap *map, int pin, int flags) |
437 | { | | 435 | { |
438 | struct gpio_softc *sc = gpio; | | 436 | struct gpio_softc *sc = gpio; |
439 | | | 437 | |
440 | /* loosey-goosey version of gpio_pin_set_conf(). */ | | 438 | /* loosey-goosey version of gpio_pin_set_conf(). */ |
441 | | | 439 | |
442 | mutex_enter(&sc->sc_mtx); | | 440 | mutex_enter(&sc->sc_mtx); |
443 | gpiobus_pin_ctl(sc->sc_gc, map->pm_map[pin], flags); | | 441 | gpiobus_pin_ctl(sc->sc_gc, map->pm_map[pin], flags); |
444 | sc->sc_pins[map->pm_map[pin]].pin_flags = flags; | | 442 | sc->sc_pins[map->pm_map[pin]].pin_flags = flags; |
445 | mutex_exit(&sc->sc_mtx); | | 443 | mutex_exit(&sc->sc_mtx); |
446 | } | | 444 | } |
447 | | | 445 | |
448 | int | | 446 | int |
449 | gpio_pin_caps(void *gpio, struct gpio_pinmap *map, int pin) | | 447 | gpio_pin_caps(void *gpio, struct gpio_pinmap *map, int pin) |
450 | { | | 448 | { |
451 | struct gpio_softc *sc = gpio; | | 449 | struct gpio_softc *sc = gpio; |
452 | | | 450 | |
453 | return sc->sc_pins[map->pm_map[pin]].pin_caps; | | 451 | return sc->sc_pins[map->pm_map[pin]].pin_caps; |
454 | } | | 452 | } |
455 | | | 453 | |
456 | int | | 454 | int |
457 | gpio_pin_intrcaps(void *gpio, struct gpio_pinmap *map, int pin) | | 455 | gpio_pin_intrcaps(void *gpio, struct gpio_pinmap *map, int pin) |
458 | { | | 456 | { |
459 | struct gpio_softc *sc = gpio; | | 457 | struct gpio_softc *sc = gpio; |
460 | | | 458 | |
461 | return sc->sc_pins[map->pm_map[pin]].pin_intrcaps; | | 459 | return sc->sc_pins[map->pm_map[pin]].pin_intrcaps; |
462 | } | | 460 | } |
463 | | | 461 | |
464 | static int | | 462 | static int |
465 | gpio_irqmode_sanitize(int irqmode) | | 463 | gpio_irqmode_sanitize(int irqmode) |
466 | { | | 464 | { |
467 | int has_edge, has_level; | | 465 | int has_edge, has_level; |
468 | | | 466 | |
469 | has_edge = irqmode & GPIO_INTR_EDGE_MASK; | | 467 | has_edge = irqmode & GPIO_INTR_EDGE_MASK; |
470 | has_level = irqmode & GPIO_INTR_LEVEL_MASK; | | 468 | has_level = irqmode & GPIO_INTR_LEVEL_MASK; |
471 | | | 469 | |
472 | /* Must specify an interrupt mode. */ | | 470 | /* Must specify an interrupt mode. */ |
473 | if ((irqmode & GPIO_INTR_MODE_MASK) == 0) | | 471 | if ((irqmode & GPIO_INTR_MODE_MASK) == 0) |
474 | return (0); | | 472 | return (0); |
475 | | | 473 | |
476 | /* Can't specify edge and level together */ | | 474 | /* Can't specify edge and level together */ |
477 | if (has_level && has_edge) | | 475 | if (has_level && has_edge) |
478 | return (0); | | 476 | return (0); |
479 | | | 477 | |
480 | /* "Be liberal in what you accept..." */ | | 478 | /* "Be liberal in what you accept..." */ |
481 | if (has_edge) { | | 479 | if (has_edge) { |
482 | if (irqmode & GPIO_INTR_DOUBLE_EDGE) { | | 480 | if (irqmode & GPIO_INTR_DOUBLE_EDGE) { |
483 | /* if DOUBLE is set, just pass through DOUBLE */ | | 481 | /* if DOUBLE is set, just pass through DOUBLE */ |
484 | irqmode = (irqmode & ~GPIO_INTR_EDGE_MASK) | | | 482 | irqmode = (irqmode & ~GPIO_INTR_EDGE_MASK) | |
485 | GPIO_INTR_DOUBLE_EDGE; | | 483 | GPIO_INTR_DOUBLE_EDGE; |
486 | } else if ((irqmode ^ | | 484 | } else if ((irqmode ^ |
487 | (GPIO_INTR_POS_EDGE | GPIO_INTR_NEG_EDGE)) == 0) { | | 485 | (GPIO_INTR_POS_EDGE | GPIO_INTR_NEG_EDGE)) == 0) { |
488 | /* both POS and NEG set; treat as DOUBLE */ | | 486 | /* both POS and NEG set; treat as DOUBLE */ |
489 | irqmode = (irqmode & ~GPIO_INTR_EDGE_MASK) | | | 487 | irqmode = (irqmode & ~GPIO_INTR_EDGE_MASK) | |
490 | GPIO_INTR_DOUBLE_EDGE; | | 488 | GPIO_INTR_DOUBLE_EDGE; |
491 | } | | 489 | } |
492 | } else { | | 490 | } else { |
493 | /* Can't specify both levels together. */ | | 491 | /* Can't specify both levels together. */ |
494 | if (has_level == GPIO_INTR_LEVEL_MASK) | | 492 | if (has_level == GPIO_INTR_LEVEL_MASK) |
495 | return (0); | | 493 | return (0); |
496 | } | | 494 | } |
497 | | | 495 | |
498 | return (irqmode); | | 496 | return (irqmode); |
499 | } | | 497 | } |
500 | | | 498 | |
501 | bool | | 499 | bool |
502 | gpio_pin_irqmode_issupported(void *gpio, struct gpio_pinmap *map, | | 500 | gpio_pin_irqmode_issupported(void *gpio, struct gpio_pinmap *map, |
503 | int pin, int irqmode) | | 501 | int pin, int irqmode) |
504 | { | | 502 | { |
505 | struct gpio_softc *sc = gpio; | | 503 | struct gpio_softc *sc = gpio; |
506 | int match; | | 504 | int match; |
507 | | | 505 | |
508 | irqmode = gpio_irqmode_sanitize(irqmode) & GPIO_INTR_MODE_MASK; | | 506 | irqmode = gpio_irqmode_sanitize(irqmode) & GPIO_INTR_MODE_MASK; |
509 | | | 507 | |
510 | /* Make sure the pin can do what is being asked. */ | | 508 | /* Make sure the pin can do what is being asked. */ |
511 | match = sc->sc_pins[map->pm_map[pin]].pin_intrcaps & irqmode; | | 509 | match = sc->sc_pins[map->pm_map[pin]].pin_intrcaps & irqmode; |
512 | | | 510 | |
513 | return (irqmode && irqmode == match); | | 511 | return (irqmode && irqmode == match); |
514 | } | | 512 | } |
515 | | | 513 | |
516 | void * | | 514 | void * |
517 | gpio_intr_establish(void *gpio, struct gpio_pinmap *map, int pin, int ipl, | | 515 | gpio_intr_establish(void *gpio, struct gpio_pinmap *map, int pin, int ipl, |
518 | int irqmode, int (*func)(void *), void *arg) | | 516 | int irqmode, int (*func)(void *), void *arg) |
519 | { | | 517 | { |
520 | struct gpio_softc *sc = gpio; | | 518 | struct gpio_softc *sc = gpio; |
521 | | | 519 | |
522 | if (sc->sc_gc->gp_intr_establish == NULL) | | 520 | if (sc->sc_gc->gp_intr_establish == NULL) |
523 | return (NULL); | | 521 | return (NULL); |
524 | | | 522 | |
525 | irqmode = gpio_irqmode_sanitize(irqmode); | | 523 | irqmode = gpio_irqmode_sanitize(irqmode); |
526 | if (irqmode == 0) | | 524 | if (irqmode == 0) |
527 | return (NULL); | | 525 | return (NULL); |
528 | | | 526 | |
529 | if (! gpio_pin_irqmode_issupported(gpio, map, pin, irqmode)) | | 527 | if (! gpio_pin_irqmode_issupported(gpio, map, pin, irqmode)) |
530 | return (NULL); | | 528 | return (NULL); |
531 | | | 529 | |
532 | /* XXX Right now, everything has to be at IPL_VM. */ | | 530 | /* XXX Right now, everything has to be at IPL_VM. */ |
533 | if (ipl != IPL_VM) | | 531 | if (ipl != IPL_VM) |
534 | return (NULL); | | 532 | return (NULL); |
535 | | | 533 | |
536 | return ((*sc->sc_gc->gp_intr_establish)(sc->sc_gc->gp_cookie, | | 534 | return ((*sc->sc_gc->gp_intr_establish)(sc->sc_gc->gp_cookie, |
537 | sc->sc_pins[map->pm_map[pin]].pin_num, ipl, irqmode, func, arg)); | | 535 | sc->sc_pins[map->pm_map[pin]].pin_num, ipl, irqmode, func, arg)); |
538 | } | | 536 | } |
539 | | | 537 | |
540 | void | | 538 | void |
541 | gpio_intr_disestablish(void *gpio, void *ih) | | 539 | gpio_intr_disestablish(void *gpio, void *ih) |
542 | { | | 540 | { |
543 | struct gpio_softc *sc = gpio; | | 541 | struct gpio_softc *sc = gpio; |
544 | | | 542 | |
545 | if (sc->sc_gc->gp_intr_disestablish != NULL && ih != NULL) | | 543 | if (sc->sc_gc->gp_intr_disestablish != NULL && ih != NULL) |
546 | (*sc->sc_gc->gp_intr_disestablish)(sc->sc_gc->gp_cookie, ih); | | 544 | (*sc->sc_gc->gp_intr_disestablish)(sc->sc_gc->gp_cookie, ih); |
547 | } | | 545 | } |
548 | | | 546 | |
549 | bool | | 547 | bool |
550 | gpio_intr_str(void *gpio, struct gpio_pinmap *map, int pin, int irqmode, | | 548 | gpio_intr_str(void *gpio, struct gpio_pinmap *map, int pin, int irqmode, |
551 | char *intrstr, size_t intrstrlen) | | 549 | char *intrstr, size_t intrstrlen) |
552 | { | | 550 | { |
553 | struct gpio_softc *sc = gpio; | | 551 | struct gpio_softc *sc = gpio; |
554 | const char *mode; | | 552 | const char *mode; |
555 | char hwstr[64]; | | 553 | char hwstr[64]; |
556 | | | 554 | |
557 | if (sc->sc_gc->gp_intr_str == NULL) | | 555 | if (sc->sc_gc->gp_intr_str == NULL) |
558 | return (false); | | 556 | return (false); |
559 | | | 557 | |
560 | irqmode = gpio_irqmode_sanitize(irqmode); | | 558 | irqmode = gpio_irqmode_sanitize(irqmode); |
561 | if (irqmode == 0) | | 559 | if (irqmode == 0) |
562 | return (false); | | 560 | return (false); |
563 | | | 561 | |
564 | if (irqmode & GPIO_INTR_DOUBLE_EDGE) | | 562 | if (irqmode & GPIO_INTR_DOUBLE_EDGE) |
565 | mode = "double edge"; | | 563 | mode = "double edge"; |
566 | else if (irqmode & GPIO_INTR_POS_EDGE) | | 564 | else if (irqmode & GPIO_INTR_POS_EDGE) |
567 | mode = "positive edge"; | | 565 | mode = "positive edge"; |
568 | else if (irqmode & GPIO_INTR_NEG_EDGE) | | 566 | else if (irqmode & GPIO_INTR_NEG_EDGE) |
569 | mode = "negative edge"; | | 567 | mode = "negative edge"; |
570 | else if (irqmode & GPIO_INTR_HIGH_LEVEL) | | 568 | else if (irqmode & GPIO_INTR_HIGH_LEVEL) |
571 | mode = "high level"; | | 569 | mode = "high level"; |
572 | else if (irqmode & GPIO_INTR_LOW_LEVEL) | | 570 | else if (irqmode & GPIO_INTR_LOW_LEVEL) |
573 | mode = "low level"; | | 571 | mode = "low level"; |
574 | else | | 572 | else |
575 | return (false); | | 573 | return (false); |
576 | | | 574 | |
577 | if (! (*sc->sc_gc->gp_intr_str)(sc->sc_gc->gp_cookie, | | 575 | if (! (*sc->sc_gc->gp_intr_str)(sc->sc_gc->gp_cookie, |
578 | sc->sc_pins[map->pm_map[pin]].pin_num, | | 576 | sc->sc_pins[map->pm_map[pin]].pin_num, |
579 | irqmode, hwstr, sizeof(hwstr))) | | 577 | irqmode, hwstr, sizeof(hwstr))) |
580 | return (false); | | 578 | return (false); |
581 | | | 579 | |
582 | (void) snprintf(intrstr, intrstrlen, "%s (%s)", hwstr, mode); | | 580 | (void) snprintf(intrstr, intrstrlen, "%s (%s)", hwstr, mode); |
583 | | | 581 | |
584 | return (true); | | 582 | return (true); |
585 | } | | 583 | } |
586 | | | 584 | |
587 | int | | 585 | int |
588 | gpio_npins(uint32_t mask) | | 586 | gpio_npins(uint32_t mask) |
589 | { | | 587 | { |
590 | int npins, i; | | 588 | int npins, i; |
591 | | | 589 | |
592 | for (npins = 0, i = 0; i < 32; i++) | | 590 | for (npins = 0, i = 0; i < 32; i++) |
593 | if (mask & (1 << i)) | | 591 | if (mask & (1 << i)) |
594 | npins++; | | 592 | npins++; |
595 | | | 593 | |
596 | return npins; | | 594 | return npins; |
597 | } | | 595 | } |
598 | | | 596 | |
599 | int | | 597 | int |
600 | gpio_lock(void *data) | | 598 | gpio_lock(void *data) |
601 | { | | 599 | { |
602 | struct gpio_softc *sc; | | 600 | struct gpio_softc *sc; |
603 | int error; | | 601 | int error; |
604 | | | 602 | |
605 | error = 0; | | 603 | error = 0; |
606 | sc = data; | | 604 | sc = data; |
607 | mutex_enter(&sc->sc_mtx); | | 605 | mutex_enter(&sc->sc_mtx); |
608 | while (sc->sc_ioctl_busy) { | | 606 | while (sc->sc_ioctl_busy) { |
609 | error = cv_wait_sig(&sc->sc_ioctl, &sc->sc_mtx); | | 607 | error = cv_wait_sig(&sc->sc_ioctl, &sc->sc_mtx); |
610 | if (error) | | 608 | if (error) |
611 | break; | | 609 | break; |
612 | } | | 610 | } |
613 | if (!error) | | 611 | if (!error) |
614 | sc->sc_ioctl_busy = 1; | | 612 | sc->sc_ioctl_busy = 1; |
615 | mutex_exit(&sc->sc_mtx); | | 613 | mutex_exit(&sc->sc_mtx); |
616 | return error; | | 614 | return error; |
617 | } | | 615 | } |
618 | | | 616 | |
619 | void | | 617 | void |
620 | gpio_unlock(void *data) | | 618 | gpio_unlock(void *data) |
621 | { | | 619 | { |
622 | struct gpio_softc *sc; | | 620 | struct gpio_softc *sc; |
623 | | | 621 | |
624 | sc = data; | | 622 | sc = data; |
625 | mutex_enter(&sc->sc_mtx); | | 623 | mutex_enter(&sc->sc_mtx); |
626 | sc->sc_ioctl_busy = 0; | | 624 | sc->sc_ioctl_busy = 0; |
627 | cv_signal(&sc->sc_ioctl); | | 625 | cv_signal(&sc->sc_ioctl); |
628 | mutex_exit(&sc->sc_mtx); | | 626 | mutex_exit(&sc->sc_mtx); |
629 | } | | 627 | } |
630 | | | 628 | |
631 | int | | 629 | int |
632 | gpioopen(dev_t dev, int flag, int mode, struct lwp *l) | | 630 | gpioopen(dev_t dev, int flag, int mode, struct lwp *l) |
633 | { | | 631 | { |
634 | struct gpio_softc *sc; | | 632 | struct gpio_softc *sc; |
635 | | | 633 | |
636 | sc = device_lookup_private(&gpio_cd, minor(dev)); | | 634 | sc = device_lookup_private(&gpio_cd, minor(dev)); |
637 | if (sc == NULL) | | 635 | if (sc == NULL) |
638 | return ENXIO; | | 636 | return ENXIO; |
639 | | | 637 | |
640 | return gpiobus_open(sc->sc_gc, sc->sc_dev); | | 638 | return gpiobus_open(sc->sc_gc, sc->sc_dev); |
641 | } | | 639 | } |
642 | | | 640 | |
643 | int | | 641 | int |
644 | gpioclose(dev_t dev, int flag, int mode, struct lwp *l) | | 642 | gpioclose(dev_t dev, int flag, int mode, struct lwp *l) |
645 | { | | 643 | { |
646 | struct gpio_softc *sc; | | 644 | struct gpio_softc *sc; |
647 | | | 645 | |
648 | sc = device_lookup_private(&gpio_cd, minor(dev)); | | 646 | sc = device_lookup_private(&gpio_cd, minor(dev)); |
649 | return gpiobus_close(sc->sc_gc, sc->sc_dev); | | 647 | return gpiobus_close(sc->sc_gc, sc->sc_dev); |
650 | } | | 648 | } |
651 | | | 649 | |
652 | static int | | 650 | static int |
653 | gpio_pinbyname(struct gpio_softc *sc, char *gp_name) | | 651 | gpio_pinbyname(struct gpio_softc *sc, char *gp_name) |
654 | { | | 652 | { |
655 | struct gpio_name *nm; | | 653 | struct gpio_name *nm; |
656 | | | 654 | |
657 | LIST_FOREACH(nm, &sc->sc_names, gp_next) | | 655 | LIST_FOREACH(nm, &sc->sc_names, gp_next) |
658 | if (!strcmp(nm->gp_name, gp_name)) | | 656 | if (!strcmp(nm->gp_name, gp_name)) |
659 | return nm->gp_pin; | | 657 | return nm->gp_pin; |
660 | return -1; | | 658 | return -1; |
661 | } | | 659 | } |
662 | | | 660 | |
663 | int | | 661 | int |
664 | gpioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) | | 662 | gpioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) |
665 | { | | 663 | { |
666 | int error; | | 664 | int error; |
667 | struct gpio_softc *sc; | | 665 | struct gpio_softc *sc; |
668 | | | 666 | |
669 | sc = device_lookup_private(&gpio_cd, minor(dev)); | | 667 | sc = device_lookup_private(&gpio_cd, minor(dev)); |
670 | | | 668 | |
671 | error = gpio_lock(sc); | | 669 | error = gpio_lock(sc); |
672 | if (error) | | 670 | if (error) |
673 | return error; | | 671 | return error; |
674 | | | 672 | |
675 | error = gpio_ioctl(sc, cmd, data, flag, l); | | 673 | error = gpio_ioctl(sc, cmd, data, flag, l); |
676 | gpio_unlock(sc); | | 674 | gpio_unlock(sc); |
677 | return error; | | 675 | return error; |
678 | } | | 676 | } |
679 | | | 677 | |
680 | static int | | 678 | static int |
681 | gpio_ioctl(struct gpio_softc *sc, u_long cmd, void *data, int flag, | | 679 | gpio_ioctl(struct gpio_softc *sc, u_long cmd, void *data, int flag, |
682 | struct lwp *l) | | 680 | struct lwp *l) |
683 | { | | 681 | { |
684 | gpio_chipset_tag_t gc; | | 682 | gpio_chipset_tag_t gc; |
685 | struct gpio_info *info; | | 683 | struct gpio_info *info; |
686 | struct gpio_attach *attach; | | 684 | struct gpio_attach *attach; |
687 | struct gpio_attach_args ga; | | 685 | struct gpio_attach_args ga; |
688 | struct gpio_req *req; | | 686 | struct gpio_req *req; |
689 | struct gpio_name *nm; | | 687 | struct gpio_name *nm; |
690 | struct gpio_set *set; | | 688 | struct gpio_set *set; |
691 | #ifdef COMPAT_50 | | 689 | #ifdef COMPAT_50 |
692 | struct gpio_dev *gdev; | | 690 | struct gpio_dev *gdev; |
693 | #endif | | 691 | #endif |
694 | device_t dv; | | 692 | device_t dv; |
695 | cfdata_t cf; | | 693 | cfdata_t cf; |
696 | kauth_cred_t cred; | | 694 | kauth_cred_t cred; |
697 | int locs[GPIOCF_NLOCS]; | | 695 | int locs[GPIOCF_NLOCS]; |
698 | int error, pin, value, flags, npins; | | 696 | int error, pin, value, flags, npins; |
699 | | | 697 | |
700 | gc = sc->sc_gc; | | 698 | gc = sc->sc_gc; |
701 | ga.ga_flags = 0; | | 699 | ga.ga_flags = 0; |
702 | | | 700 | |
703 | if (cmd != GPIOINFO && !device_is_active(sc->sc_dev)) { | | 701 | if (cmd != GPIOINFO && !device_is_active(sc->sc_dev)) { |
704 | DPRINTF(("%s: device is not active\n", | | 702 | DPRINTF(("%s: device is not active\n", |
705 | device_xname(sc->sc_dev))); | | 703 | device_xname(sc->sc_dev))); |
706 | return EBUSY; | | 704 | return EBUSY; |
707 | } | | 705 | } |
708 | | | 706 | |
709 | cred = kauth_cred_get(); | | 707 | cred = kauth_cred_get(); |
710 | | | 708 | |
711 | switch (cmd) { | | 709 | switch (cmd) { |
712 | case GPIOINFO: | | 710 | case GPIOINFO: |
713 | info = data; | | 711 | info = data; |
714 | if (!kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, | | 712 | if (!kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, |
715 | NULL, NULL, NULL, NULL)) | | 713 | NULL, NULL, NULL, NULL)) |
716 | info->gpio_npins = sc->sc_npins; | | 714 | info->gpio_npins = sc->sc_npins; |
717 | else { | | 715 | else { |
718 | for (pin = npins = 0; pin < sc->sc_npins; pin++) | | 716 | for (pin = npins = 0; pin < sc->sc_npins; pin++) |
719 | if (sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) | | 717 | if (sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) |
720 | ++npins; | | 718 | ++npins; |
721 | info->gpio_npins = npins; | | 719 | info->gpio_npins = npins; |
722 | } | | 720 | } |
723 | break; | | 721 | break; |
724 | case GPIOREAD: | | 722 | case GPIOREAD: |
725 | req = data; | | 723 | req = data; |
726 | | | 724 | |
727 | if (req->gp_name[0] != '\0') | | 725 | if (req->gp_name[0] != '\0') |
728 | req->gp_pin = gpio_pinbyname(sc, req->gp_name); | | 726 | req->gp_pin = gpio_pinbyname(sc, req->gp_name); |
729 | pin = req->gp_pin; | | 727 | pin = req->gp_pin; |
730 | | | 728 | |
731 | if (pin < 0 || pin >= sc->sc_npins) | | 729 | if (pin < 0 || pin >= sc->sc_npins) |
732 | return EINVAL; | | 730 | return EINVAL; |
733 | | | 731 | |
734 | if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) && | | 732 | if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) && |
735 | kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, | | 733 | kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, |
736 | NULL, NULL, NULL, NULL)) | | 734 | NULL, NULL, NULL, NULL)) |
737 | return EPERM; | | 735 | return EPERM; |
738 | | | 736 | |
739 | /* return read value */ | | 737 | /* return read value */ |
740 | req->gp_value = gpiobus_pin_read(gc, pin); | | 738 | req->gp_value = gpiobus_pin_read(gc, pin); |
741 | LIST_FOREACH(nm, &sc->sc_names, gp_next) | | 739 | LIST_FOREACH(nm, &sc->sc_names, gp_next) |
742 | if (nm->gp_pin == pin) { | | 740 | if (nm->gp_pin == pin) { |
743 | strlcpy(req->gp_name, nm->gp_name, GPIOMAXNAME); | | 741 | strlcpy(req->gp_name, nm->gp_name, GPIOMAXNAME); |
744 | break; | | 742 | break; |
745 | } | | 743 | } |
746 | break; | | 744 | break; |
747 | case GPIOWRITE: | | 745 | case GPIOWRITE: |
748 | if ((flag & FWRITE) == 0) | | 746 | if ((flag & FWRITE) == 0) |
749 | return EBADF; | | 747 | return EBADF; |
750 | | | 748 | |
751 | req = data; | | 749 | req = data; |
752 | | | 750 | |
753 | if (req->gp_name[0] != '\0') | | 751 | if (req->gp_name[0] != '\0') |
754 | pin = gpio_pinbyname(sc, req->gp_name); | | 752 | pin = gpio_pinbyname(sc, req->gp_name); |
755 | else | | 753 | else |
756 | pin = req->gp_pin; | | 754 | pin = req->gp_pin; |
757 | | | 755 | |
758 | if (pin < 0 || pin >= sc->sc_npins) | | 756 | if (pin < 0 || pin >= sc->sc_npins) |
759 | return EINVAL; | | 757 | return EINVAL; |
760 | | | 758 | |
761 | if (sc->sc_pins[pin].pin_mapped) | | 759 | if (sc->sc_pins[pin].pin_mapped) |
762 | return EBUSY; | | 760 | return EBUSY; |
763 | | | 761 | |
764 | if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) && | | 762 | if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) && |
765 | kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, | | 763 | kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, |
766 | NULL, NULL, NULL, NULL)) | | 764 | NULL, NULL, NULL, NULL)) |
767 | return EPERM; | | 765 | return EPERM; |
768 | | | 766 | |
769 | value = req->gp_value; | | 767 | value = req->gp_value; |
770 | if (value != GPIO_PIN_LOW && value != GPIO_PIN_HIGH) | | 768 | if (value != GPIO_PIN_LOW && value != GPIO_PIN_HIGH) |
771 | return EINVAL; | | 769 | return EINVAL; |
772 | | | 770 | |
773 | /* return old value */ | | 771 | /* return old value */ |
774 | req->gp_value = gpiobus_pin_read(gc, pin); | | 772 | req->gp_value = gpiobus_pin_read(gc, pin); |
775 | gpiobus_pin_write(gc, pin, value); | | 773 | gpiobus_pin_write(gc, pin, value); |
776 | /* update current value */ | | 774 | /* update current value */ |
777 | sc->sc_pins[pin].pin_state = value; | | 775 | sc->sc_pins[pin].pin_state = value; |
778 | break; | | 776 | break; |
779 | case GPIOTOGGLE: | | 777 | case GPIOTOGGLE: |
780 | if ((flag & FWRITE) == 0) | | 778 | if ((flag & FWRITE) == 0) |
781 | return EBADF; | | 779 | return EBADF; |
782 | | | 780 | |
783 | req = data; | | 781 | req = data; |
784 | | | 782 | |
785 | if (req->gp_name[0] != '\0') | | 783 | if (req->gp_name[0] != '\0') |
786 | pin = gpio_pinbyname(sc, req->gp_name); | | 784 | pin = gpio_pinbyname(sc, req->gp_name); |
787 | else | | 785 | else |
788 | pin = req->gp_pin; | | 786 | pin = req->gp_pin; |
789 | | | 787 | |
790 | if (pin < 0 || pin >= sc->sc_npins) | | 788 | if (pin < 0 || pin >= sc->sc_npins) |
791 | return EINVAL; | | 789 | return EINVAL; |
792 | | | 790 | |
793 | if (sc->sc_pins[pin].pin_mapped) | | 791 | if (sc->sc_pins[pin].pin_mapped) |
794 | return EBUSY; | | 792 | return EBUSY; |
795 | | | 793 | |
796 | if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) && | | 794 | if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) && |
797 | kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, | | 795 | kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, |
798 | NULL, NULL, NULL, NULL)) | | 796 | NULL, NULL, NULL, NULL)) |
799 | return EPERM; | | 797 | return EPERM; |
800 | | | 798 | |
801 | value = (sc->sc_pins[pin].pin_state == GPIO_PIN_LOW ? | | 799 | value = (sc->sc_pins[pin].pin_state == GPIO_PIN_LOW ? |
802 | GPIO_PIN_HIGH : GPIO_PIN_LOW); | | 800 | GPIO_PIN_HIGH : GPIO_PIN_LOW); |
803 | gpiobus_pin_write(gc, pin, value); | | 801 | gpiobus_pin_write(gc, pin, value); |
804 | /* return old value */ | | 802 | /* return old value */ |
805 | req->gp_value = sc->sc_pins[pin].pin_state; | | 803 | req->gp_value = sc->sc_pins[pin].pin_state; |
806 | /* update current value */ | | 804 | /* update current value */ |
807 | sc->sc_pins[pin].pin_state = value; | | 805 | sc->sc_pins[pin].pin_state = value; |
808 | break; | | 806 | break; |
809 | case GPIOATTACH: | | 807 | case GPIOATTACH: |
810 | attach = data; | | 808 | attach = data; |
811 | ga.ga_flags = attach->ga_flags; | | 809 | ga.ga_flags = attach->ga_flags; |
812 | #ifdef COMPAT_50 | | 810 | #ifdef COMPAT_50 |
813 | /* FALLTHROUGH */ | | 811 | /* FALLTHROUGH */ |
814 | case GPIOATTACH50: | | 812 | case GPIOATTACH50: |
815 | /* | | 813 | /* |
816 | * The double assignment to 'attach' in case of GPIOATTACH | | 814 | * The double assignment to 'attach' in case of GPIOATTACH |
817 | * and COMPAT_50 is on purpose. It ensures backward | | 815 | * and COMPAT_50 is on purpose. It ensures backward |
818 | * compatability in case we are called through the old | | 816 | * compatability in case we are called through the old |
819 | * GPIOATTACH50 ioctl(2), which had not the ga_flags field | | 817 | * GPIOATTACH50 ioctl(2), which had not the ga_flags field |
820 | * in struct gpio_attach. | | 818 | * in struct gpio_attach. |
821 | */ | | 819 | */ |
822 | attach = data; | | 820 | attach = data; |
823 | #endif | | 821 | #endif |
824 | if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, | | 822 | if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, |
825 | NULL, NULL, NULL, NULL)) | | 823 | NULL, NULL, NULL, NULL)) |
826 | return EPERM; | | 824 | return EPERM; |
827 | | | 825 | |
828 | /* do not try to attach if the pins are already mapped */ | | 826 | /* do not try to attach if the pins are already mapped */ |
829 | if (!gpio_pin_can_map(sc, attach->ga_offset, attach->ga_mask)) | | 827 | if (!gpio_pin_can_map(sc, attach->ga_offset, attach->ga_mask)) |
830 | return EBUSY; | | 828 | return EBUSY; |
831 | | | 829 | |
832 | error = 0; | | 830 | error = 0; |
833 | mutex_enter(&sc->sc_mtx); | | 831 | mutex_enter(&sc->sc_mtx); |
834 | while (sc->sc_attach_busy) { | | 832 | while (sc->sc_attach_busy) { |
835 | error = cv_wait_sig(&sc->sc_attach, &sc->sc_mtx); | | 833 | error = cv_wait_sig(&sc->sc_attach, &sc->sc_mtx); |
836 | if (error) | | 834 | if (error) |
837 | break; | | 835 | break; |
838 | } | | 836 | } |
839 | if (!error) | | 837 | if (!error) |
840 | sc->sc_attach_busy = 1; | | 838 | sc->sc_attach_busy = 1; |
841 | mutex_exit(&sc->sc_mtx); | | 839 | mutex_exit(&sc->sc_mtx); |
842 | if (error) | | 840 | if (error) |
843 | return EBUSY; | | 841 | return EBUSY; |
844 | | | 842 | |
845 | ga.ga_gpio = sc; | | 843 | ga.ga_gpio = sc; |
846 | /* Don't access attach->ga_flags here. */ | | 844 | /* Don't access attach->ga_flags here. */ |
847 | ga.ga_dvname = attach->ga_dvname; | | 845 | ga.ga_dvname = attach->ga_dvname; |
848 | ga.ga_offset = attach->ga_offset; | | 846 | ga.ga_offset = attach->ga_offset; |
849 | ga.ga_mask = attach->ga_mask; | | 847 | ga.ga_mask = attach->ga_mask; |
850 | DPRINTF(("%s: attach %s with offset %d, mask " | | 848 | DPRINTF(("%s: attach %s with offset %d, mask " |
851 | "0x%02x, and flags 0x%02x\n", device_xname(sc->sc_dev), | | 849 | "0x%02x, and flags 0x%02x\n", device_xname(sc->sc_dev), |
852 | ga.ga_dvname, ga.ga_offset, ga.ga_mask, ga.ga_flags)); | | 850 | ga.ga_dvname, ga.ga_offset, ga.ga_mask, ga.ga_flags)); |
853 | | | 851 | |
854 | locs[GPIOCF_OFFSET] = ga.ga_offset; | | 852 | locs[GPIOCF_OFFSET] = ga.ga_offset; |
855 | locs[GPIOCF_MASK] = ga.ga_mask; | | 853 | locs[GPIOCF_MASK] = ga.ga_mask; |
856 | locs[GPIOCF_FLAG] = ga.ga_flags; | | 854 | locs[GPIOCF_FLAG] = ga.ga_flags; |
857 | | | 855 | |
858 | cf = config_search(sc->sc_dev, &ga, | | 856 | cf = config_search(sc->sc_dev, &ga, |
859 | CFARG_LOCATORS, locs, | | 857 | CFARG_LOCATORS, locs, |
860 | CFARG_EOL); | | 858 | CFARG_EOL); |
861 | if (cf != NULL) { | | 859 | if (cf != NULL) { |
862 | dv = config_attach_loc(sc->sc_dev, cf, locs, &ga, | | 860 | dv = config_attach_loc(sc->sc_dev, cf, locs, &ga, |
863 | gpiobus_print); | | 861 | gpiobus_print); |
864 | #ifdef COMPAT_50 | | 862 | #ifdef COMPAT_50 |
865 | if (dv != NULL) { | | 863 | if (dv != NULL) { |
866 | gdev = kmem_alloc(sizeof(struct gpio_dev), | | 864 | gdev = kmem_alloc(sizeof(struct gpio_dev), |
867 | KM_SLEEP); | | 865 | KM_SLEEP); |
868 | gdev->sc_dev = dv; | | 866 | gdev->sc_dev = dv; |
869 | LIST_INSERT_HEAD(&sc->sc_devs, gdev, sc_next); | | 867 | LIST_INSERT_HEAD(&sc->sc_devs, gdev, sc_next); |
870 | } else | | 868 | } else |
871 | error = EINVAL; | | 869 | error = EINVAL; |
872 | #else | | 870 | #else |
873 | if (dv == NULL) | | 871 | if (dv == NULL) |
874 | error = EINVAL; | | 872 | error = EINVAL; |
875 | #endif | | 873 | #endif |
876 | } else | | 874 | } else |
877 | error = EINVAL; | | 875 | error = EINVAL; |
878 | mutex_enter(&sc->sc_mtx); | | 876 | mutex_enter(&sc->sc_mtx); |
879 | sc->sc_attach_busy = 0; | | 877 | sc->sc_attach_busy = 0; |
880 | cv_signal(&sc->sc_attach); | | 878 | cv_signal(&sc->sc_attach); |
881 | mutex_exit(&sc->sc_mtx); | | 879 | mutex_exit(&sc->sc_mtx); |
882 | return error; | | 880 | return error; |
883 | case GPIOSET: | | 881 | case GPIOSET: |
884 | if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, | | 882 | if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, |
885 | NULL, NULL, NULL, NULL)) | | 883 | NULL, NULL, NULL, NULL)) |
886 | return EPERM; | | 884 | return EPERM; |
887 | | | 885 | |
888 | set = data; | | 886 | set = data; |
889 | | | 887 | |
890 | if (set->gp_name[0] != '\0') | | 888 | if (set->gp_name[0] != '\0') |
891 | pin = gpio_pinbyname(sc, set->gp_name); | | 889 | pin = gpio_pinbyname(sc, set->gp_name); |
892 | else | | 890 | else |
893 | pin = set->gp_pin; | | 891 | pin = set->gp_pin; |
894 | | | 892 | |
895 | if (pin < 0 || pin >= sc->sc_npins) | | 893 | if (pin < 0 || pin >= sc->sc_npins) |
896 | return EINVAL; | | 894 | return EINVAL; |
897 | flags = set->gp_flags; | | 895 | flags = set->gp_flags; |
898 | | | 896 | |
899 | /* check that the controller supports all requested flags */ | | 897 | /* check that the controller supports all requested flags */ |
900 | if ((flags & sc->sc_pins[pin].pin_caps) != flags) | | 898 | if ((flags & sc->sc_pins[pin].pin_caps) != flags) |
901 | return ENODEV; | | 899 | return ENODEV; |
902 | flags = set->gp_flags; | | 900 | flags = set->gp_flags; |
903 | | | 901 | |
904 | set->gp_caps = sc->sc_pins[pin].pin_caps; | | 902 | set->gp_caps = sc->sc_pins[pin].pin_caps; |
905 | /* return old value */ | | 903 | /* return old value */ |
906 | set->gp_flags = sc->sc_pins[pin].pin_flags; | | 904 | set->gp_flags = sc->sc_pins[pin].pin_flags; |
907 | | | 905 | |
908 | if (flags > 0) { | | 906 | if (flags > 0) { |
909 | flags |= GPIO_PIN_SET; | | 907 | flags |= GPIO_PIN_SET; |
910 | gpiobus_pin_ctl(gc, pin, flags); | | 908 | gpiobus_pin_ctl(gc, pin, flags); |
911 | /* update current value */ | | 909 | /* update current value */ |
912 | sc->sc_pins[pin].pin_flags = flags; | | 910 | sc->sc_pins[pin].pin_flags = flags; |
913 | } | | 911 | } |
914 | | | 912 | |
915 | /* rename pin or new pin? */ | | 913 | /* rename pin or new pin? */ |
916 | if (set->gp_name2[0] != '\0') { | | 914 | if (set->gp_name2[0] != '\0') { |
917 | struct gpio_name *gnm; | | 915 | struct gpio_name *gnm; |
918 | | | 916 | |
919 | gnm = NULL; | | 917 | gnm = NULL; |
920 | LIST_FOREACH(nm, &sc->sc_names, gp_next) { | | 918 | LIST_FOREACH(nm, &sc->sc_names, gp_next) { |
921 | if (!strcmp(nm->gp_name, set->gp_name2) && | | 919 | if (!strcmp(nm->gp_name, set->gp_name2) && |
922 | nm->gp_pin != pin) | | 920 | nm->gp_pin != pin) |
923 | return EINVAL; /* duplicate name */ | | 921 | return EINVAL; /* duplicate name */ |
924 | if (nm->gp_pin == pin) | | 922 | if (nm->gp_pin == pin) |
925 | gnm = nm; | | 923 | gnm = nm; |
926 | } | | 924 | } |
927 | if (gnm != NULL) | | 925 | if (gnm != NULL) |
928 | strlcpy(gnm->gp_name, set->gp_name2, | | 926 | strlcpy(gnm->gp_name, set->gp_name2, |
929 | sizeof(gnm->gp_name)); | | 927 | sizeof(gnm->gp_name)); |
930 | else { | | 928 | else { |
931 | nm = kmem_alloc(sizeof(struct gpio_name), | | 929 | nm = kmem_alloc(sizeof(struct gpio_name), |
932 | KM_SLEEP); | | 930 | KM_SLEEP); |
933 | strlcpy(nm->gp_name, set->gp_name2, | | 931 | strlcpy(nm->gp_name, set->gp_name2, |
934 | sizeof(nm->gp_name)); | | 932 | sizeof(nm->gp_name)); |
935 | nm->gp_pin = set->gp_pin; | | 933 | nm->gp_pin = set->gp_pin; |
936 | LIST_INSERT_HEAD(&sc->sc_names, nm, gp_next); | | 934 | LIST_INSERT_HEAD(&sc->sc_names, nm, gp_next); |
937 | } | | 935 | } |
938 | } | | 936 | } |
939 | break; | | 937 | break; |
940 | case GPIOUNSET: | | 938 | case GPIOUNSET: |
941 | if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, | | 939 | if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, |
942 | NULL, NULL, NULL, NULL)) | | 940 | NULL, NULL, NULL, NULL)) |
943 | return EPERM; | | 941 | return EPERM; |
944 | | | 942 | |
945 | set = data; | | 943 | set = data; |
946 | if (set->gp_name[0] != '\0') | | 944 | if (set->gp_name[0] != '\0') |
947 | pin = gpio_pinbyname(sc, set->gp_name); | | 945 | pin = gpio_pinbyname(sc, set->gp_name); |
948 | else | | 946 | else |
949 | pin = set->gp_pin; | | 947 | pin = set->gp_pin; |
950 | | | 948 | |
951 | if (pin < 0 || pin >= sc->sc_npins) | | 949 | if (pin < 0 || pin >= sc->sc_npins) |
952 | return EINVAL; | | 950 | return EINVAL; |
953 | if (sc->sc_pins[pin].pin_mapped) | | 951 | if (sc->sc_pins[pin].pin_mapped) |
954 | return EBUSY; | | 952 | return EBUSY; |
955 | if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET)) | | 953 | if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET)) |
956 | return EINVAL; | | 954 | return EINVAL; |
957 | | | 955 | |
958 | LIST_FOREACH(nm, &sc->sc_names, gp_next) { | | 956 | LIST_FOREACH(nm, &sc->sc_names, gp_next) { |
959 | if (nm->gp_pin == pin) { | | 957 | if (nm->gp_pin == pin) { |
960 | LIST_REMOVE(nm, gp_next); | | 958 | LIST_REMOVE(nm, gp_next); |
961 | kmem_free(nm, sizeof(struct gpio_name)); | | 959 | kmem_free(nm, sizeof(struct gpio_name)); |
962 | break; | | 960 | break; |
963 | } | | 961 | } |
964 | } | | 962 | } |
965 | sc->sc_pins[pin].pin_flags &= ~GPIO_PIN_SET; | | 963 | sc->sc_pins[pin].pin_flags &= ~GPIO_PIN_SET; |
966 | break; | | 964 | break; |
967 | default: | | 965 | default: |
968 | #ifdef COMPAT_50 | | 966 | #ifdef COMPAT_50 |
969 | /* Try the old API */ | | 967 | /* Try the old API */ |
970 | DPRINTF(("%s: trying the old API\n", device_xname(sc->sc_dev))); | | 968 | DPRINTF(("%s: trying the old API\n", device_xname(sc->sc_dev))); |
971 | return gpio_ioctl_oapi(sc, cmd, data, flag, cred); | | 969 | return gpio_ioctl_oapi(sc, cmd, data, flag, cred); |
972 | #else | | 970 | #else |
973 | return ENOTTY; | | 971 | return ENOTTY; |
974 | #endif | | 972 | #endif |
975 | } | | 973 | } |
976 | return 0; | | 974 | return 0; |
977 | } | | 975 | } |
978 | | | 976 | |
979 | #ifdef COMPAT_50 | | 977 | #ifdef COMPAT_50 |
980 | static int | | 978 | static int |
981 | gpio_ioctl_oapi(struct gpio_softc *sc, u_long cmd, void *data, int flag, | | 979 | gpio_ioctl_oapi(struct gpio_softc *sc, u_long cmd, void *data, int flag, |
982 | kauth_cred_t cred) | | 980 | kauth_cred_t cred) |
983 | { | | 981 | { |
984 | gpio_chipset_tag_t gc; | | 982 | gpio_chipset_tag_t gc; |
985 | struct gpio_pin_op *op; | | 983 | struct gpio_pin_op *op; |
986 | struct gpio_pin_ctl *ctl; | | 984 | struct gpio_pin_ctl *ctl; |
987 | struct gpio_attach *attach; | | 985 | struct gpio_attach *attach; |
988 | struct gpio_dev *gdev; | | 986 | struct gpio_dev *gdev; |
989 | | | 987 | |
990 | int error, pin, value, flags; | | 988 | int error, pin, value, flags; |
991 | | | 989 | |
992 | gc = sc->sc_gc; | | 990 | gc = sc->sc_gc; |
993 | | | 991 | |
994 | switch (cmd) { | | 992 | switch (cmd) { |
995 | case GPIOPINREAD: | | 993 | case GPIOPINREAD: |
996 | op = data; | | 994 | op = data; |
997 | | | 995 | |
998 | pin = op->gp_pin; | | 996 | pin = op->gp_pin; |
999 | | | 997 | |
1000 | if (pin < 0 || pin >= sc->sc_npins) | | 998 | if (pin < 0 || pin >= sc->sc_npins) |
1001 | return EINVAL; | | 999 | return EINVAL; |
1002 | | | 1000 | |
1003 | if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) && | | 1001 | if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) && |
1004 | kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, | | 1002 | kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, |
1005 | NULL, NULL, NULL, NULL)) | | 1003 | NULL, NULL, NULL, NULL)) |
1006 | return EPERM; | | 1004 | return EPERM; |
1007 | | | 1005 | |
1008 | /* return read value */ | | 1006 | /* return read value */ |
1009 | op->gp_value = gpiobus_pin_read(gc, pin); | | 1007 | op->gp_value = gpiobus_pin_read(gc, pin); |
1010 | break; | | 1008 | break; |
1011 | case GPIOPINWRITE: | | 1009 | case GPIOPINWRITE: |
1012 | if ((flag & FWRITE) == 0) | | 1010 | if ((flag & FWRITE) == 0) |
1013 | return EBADF; | | 1011 | return EBADF; |
1014 | | | 1012 | |
1015 | op = data; | | 1013 | op = data; |
1016 | | | 1014 | |
1017 | pin = op->gp_pin; | | 1015 | pin = op->gp_pin; |
1018 | | | 1016 | |
1019 | if (pin < 0 || pin >= sc->sc_npins) | | 1017 | if (pin < 0 || pin >= sc->sc_npins) |
1020 | return EINVAL; | | 1018 | return EINVAL; |
1021 | | | 1019 | |
1022 | if (sc->sc_pins[pin].pin_mapped) | | 1020 | if (sc->sc_pins[pin].pin_mapped) |
1023 | return EBUSY; | | 1021 | return EBUSY; |
1024 | | | 1022 | |
1025 | if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) && | | 1023 | if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) && |
1026 | kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, | | 1024 | kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, |
1027 | NULL, NULL, NULL, NULL)) | | 1025 | NULL, NULL, NULL, NULL)) |
1028 | return EPERM; | | 1026 | return EPERM; |
1029 | | | 1027 | |
1030 | value = op->gp_value; | | 1028 | value = op->gp_value; |
1031 | if (value != GPIO_PIN_LOW && value != GPIO_PIN_HIGH) | | 1029 | if (value != GPIO_PIN_LOW && value != GPIO_PIN_HIGH) |
1032 | return EINVAL; | | 1030 | return EINVAL; |
1033 | | | 1031 | |
1034 | gpiobus_pin_write(gc, pin, value); | | 1032 | gpiobus_pin_write(gc, pin, value); |
1035 | /* return old value */ | | 1033 | /* return old value */ |
1036 | op->gp_value = sc->sc_pins[pin].pin_state; | | 1034 | op->gp_value = sc->sc_pins[pin].pin_state; |
1037 | /* update current value */ | | 1035 | /* update current value */ |
1038 | sc->sc_pins[pin].pin_state = value; | | 1036 | sc->sc_pins[pin].pin_state = value; |
1039 | break; | | 1037 | break; |
1040 | case GPIOPINTOGGLE: | | 1038 | case GPIOPINTOGGLE: |
1041 | if ((flag & FWRITE) == 0) | | 1039 | if ((flag & FWRITE) == 0) |
1042 | return EBADF; | | 1040 | return EBADF; |
1043 | | | 1041 | |
1044 | op = data; | | 1042 | op = data; |
1045 | | | 1043 | |
1046 | pin = op->gp_pin; | | 1044 | pin = op->gp_pin; |
1047 | | | 1045 | |
1048 | if (pin < 0 || pin >= sc->sc_npins) | | 1046 | if (pin < 0 || pin >= sc->sc_npins) |
1049 | return EINVAL; | | 1047 | return EINVAL; |
1050 | | | 1048 | |
1051 | if (sc->sc_pins[pin].pin_mapped) | | 1049 | if (sc->sc_pins[pin].pin_mapped) |
1052 | return EBUSY; | | 1050 | return EBUSY; |
1053 | | | 1051 | |
1054 | if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) && | | 1052 | if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) && |
1055 | kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, | | 1053 | kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, |
1056 | NULL, NULL, NULL, NULL)) | | 1054 | NULL, NULL, NULL, NULL)) |
1057 | return EPERM; | | 1055 | return EPERM; |
1058 | | | 1056 | |
1059 | value = (sc->sc_pins[pin].pin_state == GPIO_PIN_LOW ? | | 1057 | value = (sc->sc_pins[pin].pin_state == GPIO_PIN_LOW ? |
1060 | GPIO_PIN_HIGH : GPIO_PIN_LOW); | | 1058 | GPIO_PIN_HIGH : GPIO_PIN_LOW); |
1061 | gpiobus_pin_write(gc, pin, value); | | 1059 | gpiobus_pin_write(gc, pin, value); |
1062 | /* return old value */ | | 1060 | /* return old value */ |
1063 | op->gp_value = sc->sc_pins[pin].pin_state; | | 1061 | op->gp_value = sc->sc_pins[pin].pin_state; |
1064 | /* update current value */ | | 1062 | /* update current value */ |
1065 | sc->sc_pins[pin].pin_state = value; | | 1063 | sc->sc_pins[pin].pin_state = value; |
1066 | break; | | 1064 | break; |
1067 | case GPIOPINCTL: | | 1065 | case GPIOPINCTL: |
1068 | ctl = data; | | 1066 | ctl = data; |
1069 | | | 1067 | |
1070 | if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, | | 1068 | if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, |
1071 | NULL, NULL, NULL, NULL)) | | 1069 | NULL, NULL, NULL, NULL)) |
1072 | return EPERM; | | 1070 | return EPERM; |
1073 | | | 1071 | |
1074 | pin = ctl->gp_pin; | | 1072 | pin = ctl->gp_pin; |
1075 | | | 1073 | |
1076 | if (pin < 0 || pin >= sc->sc_npins) | | 1074 | if (pin < 0 || pin >= sc->sc_npins) |
1077 | return EINVAL; | | 1075 | return EINVAL; |
1078 | if (sc->sc_pins[pin].pin_mapped) | | 1076 | if (sc->sc_pins[pin].pin_mapped) |
1079 | return EBUSY; | | 1077 | return EBUSY; |
1080 | flags = ctl->gp_flags; | | 1078 | flags = ctl->gp_flags; |
1081 | | | 1079 | |
1082 | /* check that the controller supports all requested flags */ | | 1080 | /* check that the controller supports all requested flags */ |
1083 | if ((flags & sc->sc_pins[pin].pin_caps) != flags) | | 1081 | if ((flags & sc->sc_pins[pin].pin_caps) != flags) |
1084 | return ENODEV; | | 1082 | return ENODEV; |
1085 | | | 1083 | |
1086 | ctl->gp_caps = sc->sc_pins[pin].pin_caps; | | 1084 | ctl->gp_caps = sc->sc_pins[pin].pin_caps; |
1087 | /* return old value */ | | 1085 | /* return old value */ |
1088 | ctl->gp_flags = sc->sc_pins[pin].pin_flags; | | 1086 | ctl->gp_flags = sc->sc_pins[pin].pin_flags; |
1089 | if (flags > 0) { | | 1087 | if (flags > 0) { |
1090 | gpiobus_pin_ctl(gc, pin, flags); | | 1088 | gpiobus_pin_ctl(gc, pin, flags); |
1091 | /* update current value */ | | 1089 | /* update current value */ |
1092 | sc->sc_pins[pin].pin_flags = flags; | | 1090 | sc->sc_pins[pin].pin_flags = flags; |
1093 | } | | 1091 | } |
1094 | break; | | 1092 | break; |
1095 | case GPIODETACH50: | | 1093 | case GPIODETACH50: |
1096 | /* FALLTHOUGH */ | | 1094 | /* FALLTHOUGH */ |
1097 | case GPIODETACH: | | 1095 | case GPIODETACH: |
1098 | if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, | | 1096 | if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, |
1099 | NULL, NULL, NULL, NULL)) | | 1097 | NULL, NULL, NULL, NULL)) |
1100 | return EPERM; | | 1098 | return EPERM; |
1101 | | | 1099 | |
1102 | error = 0; | | 1100 | error = 0; |
1103 | mutex_enter(&sc->sc_mtx); | | 1101 | mutex_enter(&sc->sc_mtx); |
1104 | while (sc->sc_attach_busy) { | | 1102 | while (sc->sc_attach_busy) { |
1105 | error = cv_wait_sig(&sc->sc_attach, &sc->sc_mtx); | | 1103 | error = cv_wait_sig(&sc->sc_attach, &sc->sc_mtx); |
1106 | if (error) | | 1104 | if (error) |
1107 | break; | | 1105 | break; |
1108 | } | | 1106 | } |
1109 | if (!error) | | 1107 | if (!error) |
1110 | sc->sc_attach_busy = 1; | | 1108 | sc->sc_attach_busy = 1; |
1111 | mutex_exit(&sc->sc_mtx); | | 1109 | mutex_exit(&sc->sc_mtx); |
1112 | if (error) | | 1110 | if (error) |
1113 | return EBUSY; | | 1111 | return EBUSY; |
1114 | | | 1112 | |
1115 | attach = data; | | 1113 | attach = data; |
1116 | LIST_FOREACH(gdev, &sc->sc_devs, sc_next) { | | 1114 | LIST_FOREACH(gdev, &sc->sc_devs, sc_next) { |
1117 | if (strcmp(device_xname(gdev->sc_dev), | | 1115 | if (strcmp(device_xname(gdev->sc_dev), |
1118 | attach->ga_dvname) == 0) { | | 1116 | attach->ga_dvname) == 0) { |
1119 | mutex_enter(&sc->sc_mtx); | | 1117 | mutex_enter(&sc->sc_mtx); |
1120 | sc->sc_attach_busy = 0; | | 1118 | sc->sc_attach_busy = 0; |
1121 | cv_signal(&sc->sc_attach); | | 1119 | cv_signal(&sc->sc_attach); |
1122 | mutex_exit(&sc->sc_mtx); | | 1120 | mutex_exit(&sc->sc_mtx); |
1123 | | | 1121 | |
1124 | if (config_detach(gdev->sc_dev, 0) == 0) | | 1122 | if (config_detach(gdev->sc_dev, 0) == 0) |
1125 | return 0; | | 1123 | return 0; |
1126 | break; | | 1124 | break; |
1127 | } | | 1125 | } |
1128 | } | | 1126 | } |
1129 | if (gdev == NULL) { | | 1127 | if (gdev == NULL) { |
1130 | mutex_enter(&sc->sc_mtx); | | 1128 | mutex_enter(&sc->sc_mtx); |
1131 | sc->sc_attach_busy = 0; | | 1129 | sc->sc_attach_busy = 0; |
1132 | cv_signal(&sc->sc_attach); | | 1130 | cv_signal(&sc->sc_attach); |
1133 | mutex_exit(&sc->sc_mtx); | | 1131 | mutex_exit(&sc->sc_mtx); |
1134 | } | | 1132 | } |
1135 | return EINVAL; | | 1133 | return EINVAL; |
1136 | | | 1134 | |
1137 | default: | | 1135 | default: |
1138 | return ENOTTY; | | 1136 | return ENOTTY; |
1139 | } | | 1137 | } |
1140 | return 0; | | 1138 | return 0; |
1141 | } | | 1139 | } |
1142 | #endif /* COMPAT_50 */ | | 1140 | #endif /* COMPAT_50 */ |
1143 | | | 1141 | |
1144 | MODULE(MODULE_CLASS_DRIVER, gpio, NULL); | | 1142 | MODULE(MODULE_CLASS_DRIVER, gpio, NULL); |
1145 | | | 1143 | |
1146 | #ifdef _MODULE | | 1144 | #ifdef _MODULE |
1147 | #include "ioconf.c" | | 1145 | #include "ioconf.c" |
1148 | #endif | | 1146 | #endif |
1149 | | | 1147 | |
1150 | static int | | 1148 | static int |
1151 | gpio_modcmd(modcmd_t cmd, void *opaque) | | 1149 | gpio_modcmd(modcmd_t cmd, void *opaque) |
1152 | { | | 1150 | { |
1153 | #ifdef _MODULE | | 1151 | #ifdef _MODULE |
1154 | devmajor_t cmajor = NODEVMAJOR, bmajor = NODEVMAJOR; | | 1152 | devmajor_t cmajor = NODEVMAJOR, bmajor = NODEVMAJOR; |
1155 | int error; | | 1153 | int error; |
1156 | #endif | | 1154 | #endif |
1157 | switch (cmd) { | | 1155 | switch (cmd) { |
1158 | case MODULE_CMD_INIT: | | 1156 | case MODULE_CMD_INIT: |
1159 | #ifdef _MODULE | | 1157 | #ifdef _MODULE |
1160 | error = config_init_component(cfdriver_ioconf_gpio, | | 1158 | error = config_init_component(cfdriver_ioconf_gpio, |
1161 | cfattach_ioconf_gpio, cfdata_ioconf_gpio); | | 1159 | cfattach_ioconf_gpio, cfdata_ioconf_gpio); |
1162 | if (error) { | | 1160 | if (error) { |
1163 | aprint_error("%s: unable to init component\n", | | 1161 | aprint_error("%s: unable to init component\n", |
1164 | gpio_cd.cd_name); | | 1162 | gpio_cd.cd_name); |
1165 | return error; | | 1163 | return error; |
1166 | } | | 1164 | } |
1167 | error = devsw_attach(gpio_cd.cd_name, NULL, &bmajor, | | 1165 | error = devsw_attach(gpio_cd.cd_name, NULL, &bmajor, |
1168 | &gpio_cdevsw, &cmajor); | | 1166 | &gpio_cdevsw, &cmajor); |
1169 | if (error) { | | 1167 | if (error) { |
1170 | aprint_error("%s: unable to register devsw\n", | | 1168 | aprint_error("%s: unable to register devsw\n", |
1171 | gpio_cd.cd_name); | | 1169 | gpio_cd.cd_name); |
1172 | return config_fini_component(cfdriver_ioconf_gpio, | | 1170 | return config_fini_component(cfdriver_ioconf_gpio, |
1173 | cfattach_ioconf_gpio, cfdata_ioconf_gpio); | | 1171 | cfattach_ioconf_gpio, cfdata_ioconf_gpio); |
1174 | } | | 1172 | } |
1175 | #endif | | 1173 | #endif |
1176 | return 0; | | 1174 | return 0; |
1177 | case MODULE_CMD_FINI: | | 1175 | case MODULE_CMD_FINI: |
1178 | #ifdef _MODULE | | 1176 | #ifdef _MODULE |
1179 | config_fini_component(cfdriver_ioconf_gpio, | | 1177 | config_fini_component(cfdriver_ioconf_gpio, |
1180 | cfattach_ioconf_gpio, cfdata_ioconf_gpio); | | 1178 | cfattach_ioconf_gpio, cfdata_ioconf_gpio); |
1181 | devsw_detach(NULL, &gpio_cdevsw); | | 1179 | devsw_detach(NULL, &gpio_cdevsw); |
1182 | #endif | | 1180 | #endif |
1183 | return 0; | | 1181 | return 0; |
1184 | default: | | 1182 | default: |
1185 | return ENOTTY; | | 1183 | return ENOTTY; |
1186 | } | | 1184 | } |
1187 | } | | 1185 | } |