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