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