1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/errno.h>
30 #include <sys/cmn_err.h>
31 #include <sys/param.h>
32 #include <sys/modctl.h>
33 #include <sys/conf.h>
34 #include <sys/open.h>
35 #include <sys/stat.h>
36 #include <sys/clock.h>
37 #include <sys/gpio_87317.h>
38 #include <sys/ddi.h>
39 #include <sys/sunddi.h>
40 #include <sys/file.h>
41 #ifdef DEBUG
42 #include <sys/promif.h>
43 #endif
44
45
46 /* a non zero value causes debug info to be displayed */
47 uint_t gpio_debug_flag = 0;
48
49
50 #ifdef DEBUG
51 static void gpio_debug(dev_info_t *dip, char *format, uint_t arg1, uint_t arg2,
52 uint_t arg3, uint_t arg4, uint_t arg5);
53
54 #define DBG(dip, format, arg1, arg2, arg3, arg4, arg5) \
55 gpio_debug(dip, format, (uint_t)arg1, (uint_t)arg2, (uint_t)arg3, \
56 (uint_t)arg4, (uint_t)arg5)
57 #else
58 #define DBG(dip, format, arg1, arg2, arg3, arg4, arg5)
59 #endif
60
61
62 /* Driver soft state structure */
63 struct gpio_softc {
64 dev_info_t *gp_dip;
65 kmutex_t gp_mutex;
66 int gp_state;
67 ddi_acc_handle_t gp_handle;
68 uint8_t *gp_regs;
69 };
70
71 #define getsoftc(minor) \
72 ((struct gpio_softc *)ddi_get_soft_state(statep, (minor)))
73
74 /* dev_ops and cb_ops entry point function declarations */
75 static int gpio_attach(dev_info_t *, ddi_attach_cmd_t);
76 static int gpio_detach(dev_info_t *, ddi_detach_cmd_t);
77 static int gpio_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
78 static int gpio_open(dev_t *, int, int, cred_t *);
79 static int gpio_close(dev_t, int, int, cred_t *);
80 static int gpio_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
81
82 struct cb_ops gpio_cb_ops = {
83 gpio_open,
84 gpio_close,
85 nodev,
86 nodev,
87 nodev, /* dump */
88 nodev,
89 nodev,
90 gpio_ioctl,
91 nodev, /* devmap */
92 nodev,
93 nodev,
94 nochpoll,
95 ddi_prop_op,
96 NULL, /* for STREAMS drivers */
97 D_NEW | D_MP, /* driver compatibility flag */
98 CB_REV,
99 nodev,
100 nodev
101 };
102
103 static struct dev_ops gpio_dev_ops = {
104 DEVO_REV, /* driver build version */
105 0, /* device reference count */
106 gpio_getinfo,
107 nulldev,
108 nulldev, /* probe */
109 gpio_attach,
110 gpio_detach,
111 nulldev, /* reset */
112 &gpio_cb_ops,
113 (struct bus_ops *)NULL,
114 nulldev, /* power */
115 ddi_quiesce_not_needed, /* quiesce */
116 };
117
118 /* module configuration stuff */
119 static void *statep;
120 extern struct mod_ops mod_driverops;
121 static struct modldrv modldrv = {
122 &mod_driverops,
123 "gpio driver",
124 &gpio_dev_ops
125 };
126 static struct modlinkage modlinkage = {
127 MODREV_1,
128 &modldrv,
129 0
130 };
131
132
133 int
_init(void)134 _init(void)
135 {
136 int e;
137
138 if (e = ddi_soft_state_init(&statep, sizeof (struct gpio_softc), 1)) {
139 return (e);
140 }
141 if ((e = mod_install(&modlinkage)) != 0) {
142 ddi_soft_state_fini(&statep);
143 }
144
145 return (e);
146 }
147
148
149 int
_fini(void)150 _fini(void)
151 {
152 int e;
153
154 if ((e = mod_remove(&modlinkage)) != 0) {
155 return (e);
156 }
157 ddi_soft_state_fini(&statep);
158
159 return (DDI_SUCCESS);
160 }
161
162
163 int
_info(struct modinfo * modinfop)164 _info(struct modinfo *modinfop)
165 {
166 return (mod_info(&modlinkage, modinfop));
167 }
168
169
170 /* ARGSUSED */
171 static int
gpio_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)172 gpio_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
173 {
174 int instance = getminor((dev_t)arg);
175 int retval = DDI_SUCCESS;
176 struct gpio_softc *softc;
177
178 switch (cmd) {
179 case DDI_INFO_DEVT2DEVINFO:
180 if ((softc = getsoftc(instance)) == NULL) {
181 *result = (void *)NULL;
182 retval = DDI_FAILURE;
183 } else
184 *result = (void *)softc->gp_dip;
185 break;
186
187 case DDI_INFO_DEVT2INSTANCE:
188 *result = (void *)(uintptr_t)instance;
189 break;
190
191 default:
192 retval = DDI_FAILURE;
193 }
194
195 return (retval);
196 }
197
198
199 static int
gpio_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)200 gpio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
201 {
202
203 int instance;
204 struct gpio_softc *softc = NULL;
205 ddi_device_acc_attr_t dev_attr;
206
207 switch (cmd) {
208
209 case DDI_ATTACH:
210
211 /* Allocate and get the soft state structure for this instance. */
212
213 instance = ddi_get_instance(dip);
214 DBG(dip, "attach: instance is %d", instance, 0, 0, 0, 0);
215 if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS)
216 goto attach_failed;
217 softc = getsoftc(instance);
218 softc->gp_dip = dip;
219 softc->gp_state = 0;
220 mutex_init(&softc->gp_mutex, NULL, MUTEX_DRIVER, NULL);
221
222 /* Map in the gpio device registers. */
223
224 dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
225 dev_attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
226 dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
227 if (ddi_regs_map_setup(dip, 0, (caddr_t *)&softc->gp_regs, 0, 0,
228 &dev_attr, &softc->gp_handle) != DDI_SUCCESS)
229 goto attach_failed;
230 DBG(dip, "attach: regs=0x%p", (uintptr_t)softc->gp_regs,
231 0, 0, 0, 0);
232 DBG(dip, "attach: port 1 data is %x",
233 (uintptr_t)ddi_get8(softc->gp_handle, &softc->gp_regs[0]),
234 0, 0, 0, 0);
235 DBG(dip, "attach: port 1 direction is %x",
236 (uintptr_t)ddi_get8(softc->gp_handle, &softc->gp_regs[1]),
237 0, 0, 0, 0);
238 DBG(dip, "attach: port 1 output type is %x",
239 (uintptr_t)ddi_get8(softc->gp_handle, &softc->gp_regs[2]),
240 0, 0, 0, 0);
241 DBG(dip, "attach: port 1 pull up control type is %x",
242 (uintptr_t)ddi_get8(softc->gp_handle, &softc->gp_regs[3]),
243 0, 0, 0, 0);
244 DBG(dip, "attach: port 2 data is %x",
245 (uintptr_t)ddi_get8(softc->gp_handle, &softc->gp_regs[4]),
246 0, 0, 0, 0);
247 DBG(dip, "attach: port 2 direction is %x",
248 (uintptr_t)ddi_get8(softc->gp_handle, &softc->gp_regs[5]),
249 0, 0, 0, 0);
250 DBG(dip, "attach: port 2 output type is %x",
251 (uintptr_t)ddi_get8(softc->gp_handle, &softc->gp_regs[6]),
252 0, 0, 0, 0);
253 DBG(dip, "attach: port 2 pull up control type is %x",
254 (uintptr_t)ddi_get8(softc->gp_handle, &softc->gp_regs[7]),
255 0, 0, 0, 0);
256
257 /* Create device minor nodes. */
258
259 if (ddi_create_minor_node(dip, "gpio", S_IFCHR,
260 instance, NULL, NULL) == DDI_FAILURE) {
261 ddi_regs_map_free(&softc->gp_handle);
262 goto attach_failed;
263 }
264
265 ddi_report_dev(dip);
266 return (DDI_SUCCESS);
267
268 case DDI_RESUME:
269
270 /* Nothing to do for a resume. */
271
272 return (DDI_SUCCESS);
273
274 default:
275 return (DDI_FAILURE);
276 }
277
278 attach_failed:
279 if (softc) {
280 mutex_destroy(&softc->gp_mutex);
281 if (softc->gp_handle)
282 ddi_regs_map_free(&softc->gp_handle);
283 ddi_soft_state_free(statep, instance);
284 ddi_remove_minor_node(dip, NULL);
285 }
286 return (DDI_FAILURE);
287 }
288
289
290 static int
gpio_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)291 gpio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
292 {
293 int instance;
294 struct gpio_softc *softc;
295
296 switch (cmd) {
297 case DDI_DETACH:
298 instance = ddi_get_instance(dip);
299 DBG(dip, "detach: instance is %d", instance, 0, 0, 0, 0);
300 if ((softc = getsoftc(instance)) == NULL)
301 return (ENXIO);
302 mutex_destroy(&softc->gp_mutex);
303 ddi_regs_map_free(&softc->gp_handle);
304 ddi_soft_state_free(statep, instance);
305 ddi_remove_minor_node(dip, NULL);
306 return (DDI_SUCCESS);
307
308 case DDI_SUSPEND:
309 /* Nothing to do in the suspend case. */
310 return (DDI_SUCCESS);
311
312 default:
313 return (DDI_FAILURE);
314 }
315 }
316
317
318 /* ARGSUSED */
319 static int
gpio_open(dev_t * devp,int flag,int otyp,cred_t * credp)320 gpio_open(dev_t *devp, int flag, int otyp, cred_t *credp)
321 {
322 int instance = getminor(*devp);
323
324 DBG(NULL, "open: instance is %d", instance, 0, 0, 0, 0);
325 return (getsoftc(instance) == NULL ? ENXIO : 0);
326 }
327
328
329 /* ARGSUSED */
330 static int
gpio_close(dev_t dev,int flag,int otyp,cred_t * credp)331 gpio_close(dev_t dev, int flag, int otyp, cred_t *credp)
332 {
333 int instance = getminor(dev);
334
335 DBG(NULL, "close: instance is %d", instance, 0, 0, 0, 0);
336 return (getsoftc(instance) == NULL ? ENXIO : 0);
337 }
338
339
340 /* ARGSUSED */
341 static int
gpio_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)342 gpio_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
343 int *rvalp)
344 {
345 int instance = getminor(dev);
346 struct gpio_softc *softc = getsoftc(instance);
347 gpio_87317_op_t info;
348 uint8_t byte;
349
350 DBG(softc->gp_dip, "ioctl: instance is %d", instance, 0, 0, 0, 0);
351
352 if (softc == NULL)
353 return (ENXIO);
354
355 /* Copy the command from user space. */
356 if (ddi_copyin((caddr_t)arg, (caddr_t)&info, sizeof (gpio_87317_op_t),
357 mode) != 0)
358 return (EFAULT);
359
360 /* Check the command arguments. We only support port 1 in bank 0. */
361 if ((info.gpio_bank != 0) ||
362 (info.gpio_offset != GPIO_87317_PORT1_DATA)) {
363 return (EINVAL);
364 }
365
366 /* Grap the instance's mutex to insure exclusive access. */
367 mutex_enter(&softc->gp_mutex);
368
369 /* Get the contents of the GPIO register we're suppose to modify. */
370 byte = ddi_get8(softc->gp_handle, &softc->gp_regs[info.gpio_offset]);
371
372 switch (cmd) {
373 case GPIO_CMD_SET_BITS:
374 DBG(softc->gp_dip, "ioctl: SET_BITS, byte is %x", byte, 0, 0,
375 0, 0);
376 byte |= info.gpio_data;
377 ddi_put8(softc->gp_handle, &softc->gp_regs[info.gpio_offset],
378 byte);
379 byte = ddi_get8(softc->gp_handle,
380 &softc->gp_regs[info.gpio_offset]);
381 DBG(softc->gp_dip, "ioctl: SET_BITS, byte is %x", byte, 0, 0,
382 0, 0);
383 break;
384
385 case GPIO_CMD_CLR_BITS:
386 DBG(softc->gp_dip, "ioctl: CLR_BITS, byte is %x", byte, 0, 0,
387 0, 0);
388 byte &= ~info.gpio_data;
389 ddi_put8(softc->gp_handle, &softc->gp_regs[info.gpio_offset],
390 byte);
391 byte = ddi_get8(softc->gp_handle,
392 &softc->gp_regs[info.gpio_offset]);
393 DBG(softc->gp_dip, "ioctl: CLR_BITS, byte is %x", byte, 0, 0,
394 0, 0);
395 break;
396
397 case GPIO_CMD_GET:
398 DBG(softc->gp_dip, "ioctl: GPIO_CMD_GET", 0, 0, 0, 0, 0);
399 info.gpio_data = byte;
400 if (ddi_copyout((caddr_t)&info, (caddr_t)arg,
401 sizeof (gpio_87317_op_t), mode) != 0) {
402 mutex_exit(&softc->gp_mutex);
403 return (EFAULT);
404 }
405 break;
406
407 case GPIO_CMD_SET:
408 DBG(softc->gp_dip, "ioctl: GPIO_CMD_SET", 0, 0, 0, 0, 0);
409 ddi_put8(softc->gp_handle, &softc->gp_regs[info.gpio_offset],
410 info.gpio_data);
411 break;
412
413 default:
414 mutex_exit(&softc->gp_mutex);
415 return (EINVAL);
416 }
417
418 mutex_exit(&softc->gp_mutex);
419 return (0);
420 }
421
422
423 #ifdef DEBUG
424 void
gpio_debug(dev_info_t * dip,char * format,uint_t arg1,uint_t arg2,uint_t arg3,uint_t arg4,uint_t arg5)425 gpio_debug(dev_info_t *dip, char *format, uint_t arg1, uint_t arg2, uint_t arg3,
426 uint_t arg4, uint_t arg5)
427 {
428 if (gpio_debug_flag == 0) {
429 return;
430 }
431
432 if (dip == NULL) {
433 prom_printf("gpio: ");
434 } else {
435 prom_printf("%s%d: ", ddi_driver_name(dip),
436 ddi_get_instance(dip));
437 }
438 prom_printf(format, arg1, arg2, arg3, arg4, arg5);
439 prom_printf("\n");
440 }
441 #endif
442