xref: /illumos-gate/usr/src/uts/sun4u/io/gpio_87317.c (revision 4cf53d5010eff4a394b33676d54b1311b8e1012e)
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
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
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
164 _info(struct modinfo *modinfop)
165 {
166 	return (mod_info(&modlinkage, modinfop));
167 }
168 
169 
170 /* ARGSUSED */
171 static int
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
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, 0) == 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
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
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
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
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
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