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, 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 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