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