xref: /illumos-gate/usr/src/uts/sun4u/io/gpio_87317.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
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 (c) 2000 by Sun Microsystems, Inc.
24  * All rights reserved.
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 *)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%x", softc->gp_regs, 0, 0, 0, 0);
232 	    DBG(dip, "attach: port 1 data is %x",
233 		ddi_get8(softc->gp_handle, &softc->gp_regs[0]), 0, 0, 0, 0);
234 	    DBG(dip, "attach: port 1 direction is %x",
235 		ddi_get8(softc->gp_handle, &softc->gp_regs[1]), 0, 0, 0, 0);
236 	    DBG(dip, "attach: port 1 output type is %x",
237 		ddi_get8(softc->gp_handle, &softc->gp_regs[2]), 0, 0, 0, 0);
238 	    DBG(dip, "attach: port 1 pull up control type is %x",
239 		ddi_get8(softc->gp_handle, &softc->gp_regs[3]), 0, 0, 0, 0);
240 	    DBG(dip, "attach: port 2 data is %x",
241 		ddi_get8(softc->gp_handle, &softc->gp_regs[4]), 0, 0, 0, 0);
242 	    DBG(dip, "attach: port 2 direction is %x",
243 		ddi_get8(softc->gp_handle, &softc->gp_regs[5]), 0, 0, 0, 0);
244 	    DBG(dip, "attach: port 2 output type is %x",
245 		ddi_get8(softc->gp_handle, &softc->gp_regs[6]), 0, 0, 0, 0);
246 	    DBG(dip, "attach: port 2 pull up control type is %x",
247 		ddi_get8(softc->gp_handle, &softc->gp_regs[7]), 0, 0, 0, 0);
248 
249 	    /* Create device minor nodes. */
250 
251 	    if (ddi_create_minor_node(dip, "gpio", S_IFCHR,
252 		instance, NULL, NULL) == DDI_FAILURE) {
253 		ddi_regs_map_free(&softc->gp_handle);
254 		goto attach_failed;
255 	    }
256 
257 	    ddi_report_dev(dip);
258 	    return (DDI_SUCCESS);
259 
260 	case DDI_RESUME:
261 
262 	    /* Nothing to do for a resume. */
263 
264 	    return (DDI_SUCCESS);
265 
266 	default:
267 	    return (DDI_FAILURE);
268 	}
269 
270 attach_failed:
271 	if (softc) {
272 	    mutex_destroy(&softc->gp_mutex);
273 	    if (softc->gp_handle)
274 		ddi_regs_map_free(&softc->gp_handle);
275 	    ddi_soft_state_free(statep, instance);
276 	    ddi_remove_minor_node(dip, NULL);
277 	}
278 	return (DDI_FAILURE);
279 }
280 
281 
282 static int
283 gpio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
284 {
285 	int instance;
286 	struct gpio_softc *softc;
287 
288 	switch (cmd) {
289 	case DDI_DETACH:
290 		instance = ddi_get_instance(dip);
291 		DBG(dip, "detach: instance is %d", instance, 0, 0, 0, 0);
292 		if ((softc = getsoftc(instance)) == NULL)
293 			return (ENXIO);
294 		mutex_destroy(&softc->gp_mutex);
295 		ddi_regs_map_free(&softc->gp_handle);
296 		ddi_soft_state_free(statep, instance);
297 		ddi_remove_minor_node(dip, NULL);
298 		return (DDI_SUCCESS);
299 
300 	case DDI_SUSPEND:
301 		/* Nothing to do in the suspend case. */
302 		return (DDI_SUCCESS);
303 
304 	default:
305 		return (DDI_FAILURE);
306 	}
307 }
308 
309 
310 /* ARGSUSED */
311 static int
312 gpio_open(dev_t *devp, int flag, int otyp, cred_t *credp)
313 {
314 	int instance = getminor(*devp);
315 
316 	DBG(NULL, "open: instance is %d", instance, 0, 0, 0, 0);
317 	return (getsoftc(instance) == NULL ? ENXIO : 0);
318 }
319 
320 
321 /* ARGSUSED */
322 static int
323 gpio_close(dev_t dev, int flag, int otyp, cred_t *credp)
324 {
325 	int instance = getminor(dev);
326 
327 	DBG(NULL, "close: instance is %d", instance, 0, 0, 0, 0);
328 	return (getsoftc(instance) == NULL ? ENXIO : 0);
329 }
330 
331 
332 /* ARGSUSED */
333 static int
334 gpio_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
335 	int *rvalp)
336 {
337 	int instance = getminor(dev);
338 	struct gpio_softc *softc = getsoftc(instance);
339 	gpio_87317_op_t info;
340 	uint8_t byte;
341 
342 	DBG(softc->gp_dip, "ioctl: instance is %d", instance, 0, 0, 0, 0);
343 
344 	if (softc == NULL)
345 		return (ENXIO);
346 
347 	/* Copy the command from user space. */
348 	if (ddi_copyin((caddr_t)arg, (caddr_t)&info, sizeof (gpio_87317_op_t),
349 	    mode) != 0)
350 		return (EFAULT);
351 
352 	/* Check the command arguments.  We only support port 1 in bank 0. */
353 	if ((info.gpio_bank != 0) ||
354 	    (info.gpio_offset != GPIO_87317_PORT1_DATA)) {
355 		return (EINVAL);
356 	}
357 
358 	/* Grap the instance's mutex to insure exclusive access. */
359 	mutex_enter(&softc->gp_mutex);
360 
361 	/* Get the contents of the GPIO register we're suppose to modify. */
362 	byte = ddi_get8(softc->gp_handle, &softc->gp_regs[info.gpio_offset]);
363 
364 	switch (cmd) {
365 	case GPIO_CMD_SET_BITS:
366 		DBG(softc->gp_dip, "ioctl: SET_BITS, byte is %x", byte, 0, 0,
367 		    0, 0);
368 		byte |= info.gpio_data;
369 		ddi_put8(softc->gp_handle, &softc->gp_regs[info.gpio_offset],
370 		    byte);
371 		byte = ddi_get8(softc->gp_handle,
372 		    &softc->gp_regs[info.gpio_offset]);
373 		DBG(softc->gp_dip, "ioctl: SET_BITS, byte is %x", byte, 0, 0,
374 		    0, 0);
375 		break;
376 
377 	case GPIO_CMD_CLR_BITS:
378 		DBG(softc->gp_dip, "ioctl: CLR_BITS, byte is %x", byte, 0, 0,
379 		    0, 0);
380 		byte &= ~info.gpio_data;
381 		ddi_put8(softc->gp_handle, &softc->gp_regs[info.gpio_offset],
382 		    byte);
383 		byte = ddi_get8(softc->gp_handle,
384 		    &softc->gp_regs[info.gpio_offset]);
385 		DBG(softc->gp_dip, "ioctl: CLR_BITS, byte is %x", byte, 0, 0,
386 		    0, 0);
387 		break;
388 
389 	case GPIO_CMD_GET:
390 		DBG(softc->gp_dip, "ioctl: GPIO_CMD_GET", 0, 0, 0, 0, 0);
391 		info.gpio_data = byte;
392 		if (ddi_copyout((caddr_t)&info, (caddr_t)arg,
393 		    sizeof (gpio_87317_op_t), mode) != 0) {
394 			mutex_exit(&softc->gp_mutex);
395 			return (EFAULT);
396 		}
397 		break;
398 
399 	case GPIO_CMD_SET:
400 		DBG(softc->gp_dip, "ioctl: GPIO_CMD_SET", 0, 0, 0, 0, 0);
401 		ddi_put8(softc->gp_handle, &softc->gp_regs[info.gpio_offset],
402 		    info.gpio_data);
403 		break;
404 
405 	default:
406 		mutex_exit(&softc->gp_mutex);
407 		return (EINVAL);
408 	}
409 
410 	mutex_exit(&softc->gp_mutex);
411 	return (0);
412 }
413 
414 
415 #ifdef DEBUG
416 void
417 gpio_debug(dev_info_t *dip, char *format, uint_t arg1, uint_t arg2, uint_t arg3,
418 	uint_t arg4, uint_t arg5)
419 {
420 	if (gpio_debug_flag == 0) {
421 		return;
422 	}
423 
424 	if (dip == NULL) {
425 		prom_printf("gpio: ");
426 	} else {
427 		prom_printf("%s%d: ", ddi_driver_name(dip),
428 			ddi_get_instance(dip));
429 	}
430 	prom_printf(format, arg1, arg2, arg3, arg4, arg5);
431 	prom_printf("\n");
432 }
433 #endif
434