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/stat.h> 30 #include <sys/file.h> 31 #include <sys/uio.h> 32 #include <sys/modctl.h> 33 #include <sys/open.h> 34 #include <sys/types.h> 35 #include <sys/kmem.h> 36 #include <sys/systm.h> 37 #include <sys/ddi.h> 38 #include <sys/sunddi.h> 39 #include <sys/conf.h> 40 #include <sys/mode.h> 41 #include <sys/note.h> 42 #include <sys/i2c/misc/i2c_svc.h> 43 #include <sys/i2c/clients/ics951601.h> 44 #include <sys/i2c/clients/ics951601_impl.h> 45 46 /* 47 * This is the client driver for the ics951601 device which is a general 48 * purpose clock generator. Setting a clock to 1 enables the clock while 49 * setting it to 0 disables it. All clocks are enabled by default by 50 * the driver. The user can read a clock, enable it or disable it. 51 * The command sent as an ioctl argument should be the bitwise OR of the 52 * clock number and the action upon it. The supported clock numbers and 53 * actions are defined in ics951601.h. A pointer to an integer is sent 54 * as the third ioctl argument. If the clock is to be read the value of the 55 * clock is copied into it and if it is to be modified the integer referred 56 * to should have the appropriate value of either 1 or 0. 57 */ 58 59 /* 60 * cb ops 61 */ 62 static int ics951601_open(dev_t *, int, int, cred_t *); 63 static int ics951601_close(dev_t, int, int, cred_t *); 64 static int ics951601_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 65 66 /* 67 * dev ops 68 */ 69 static int ics951601_s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 70 static int ics951601_s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 71 static int ics951601_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 72 73 static struct cb_ops ics951601_cb_ops = { 74 ics951601_open, /* open */ 75 ics951601_close, /* close */ 76 nodev, /* strategy */ 77 nodev, /* print */ 78 nodev, /* dump */ 79 nodev, /* read */ 80 nodev, /* write */ 81 ics951601_ioctl, /* ioctl */ 82 nodev, /* devmap */ 83 nodev, /* mmap */ 84 nodev, /* segmap */ 85 nochpoll, /* poll */ 86 ddi_prop_op, /* cb_prop_op */ 87 NULL, /* streamtab */ 88 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 89 }; 90 91 static struct dev_ops ics951601_dev_ops = { 92 DEVO_REV, 93 0, 94 ics951601_info, 95 nulldev, 96 nulldev, 97 ics951601_s_attach, 98 ics951601_s_detach, 99 nodev, 100 &ics951601_cb_ops, 101 NULL 102 }; 103 104 static struct modldrv ics951601_modldrv = { 105 &mod_driverops, /* type of module - driver */ 106 "ics951601 device driver v%I%", 107 &ics951601_dev_ops, 108 }; 109 110 static struct modlinkage ics951601_modlinkage = { 111 MODREV_1, 112 &ics951601_modldrv, 113 0 114 }; 115 116 /* 117 * Writes to the clock generator involve sending the dummy command code, the 118 * dummy byte count followed by byte 0 through byte 5. The dummy command code 119 * and the dummy byte count are ignored by the ICS clock but must be sent. 120 * 121 * On reading from the clock generator, the controller will first receive a 122 * byte count followed by byte 0 through byte 5. 123 */ 124 125 /* 126 * The array for initializing the internal registers at attach time. 127 */ 128 static uchar_t init_clock_regs[8] = { 129 0x0, /* Dummy command code */ 130 0x7, /* Dummy byte count */ 131 0x0, /* Initial value for functionality register */ 132 0xff, /* Initial value for PCI1A stop clocks register */ 133 0xff, /* Initial value for PCI2A stop clocks register */ 134 0xff, /* Initial value for PCI2B stop clocks register */ 135 0xff, /* Default value for reserved register */ 136 0xef /* Default value for latched input read back register */ 137 }; 138 139 static void *ics951601_soft_statep; 140 int ics951601_debug = 0; 141 142 int 143 _init(void) 144 { 145 int err; 146 147 err = mod_install(&ics951601_modlinkage); 148 if (err == 0) { 149 (void) ddi_soft_state_init(&ics951601_soft_statep, 150 sizeof (ics951601_unit_t), 1); 151 } 152 return (err); 153 } 154 155 int 156 _fini(void) 157 { 158 int err; 159 160 err = mod_remove(&ics951601_modlinkage); 161 if (err == 0) { 162 ddi_soft_state_fini(&ics951601_soft_statep); 163 } 164 return (err); 165 } 166 167 int 168 _info(struct modinfo *modinfop) 169 { 170 return (mod_info(&ics951601_modlinkage, modinfop)); 171 } 172 173 static int 174 ics951601_open(dev_t *devp, int flags, int otyp, cred_t *credp) 175 { 176 int instance; 177 ics951601_unit_t *icsp; 178 int err = EBUSY; 179 180 /* 181 * Make sure the open is for the right file type 182 */ 183 if (otyp != OTYP_CHR) { 184 return (EINVAL); 185 } 186 187 instance = getminor(*devp); 188 if (instance < 0) { 189 return (ENXIO); 190 } 191 icsp = (ics951601_unit_t *)ddi_get_soft_state(ics951601_soft_statep, 192 instance); 193 if (icsp == NULL) { 194 return (ENXIO); 195 } 196 197 /* must be privileged to access this device */ 198 if (drv_priv(credp) != 0) { 199 return (EPERM); 200 } 201 202 /* 203 * Enforce exclusive access if required 204 */ 205 mutex_enter(&icsp->ics951601_mutex); 206 if (flags & FEXCL) { 207 if (icsp->ics951601_oflag == 0) { 208 icsp->ics951601_oflag = FEXCL; 209 err = DDI_SUCCESS; 210 } 211 } else if (icsp->ics951601_oflag != FEXCL) { 212 icsp->ics951601_oflag = FOPEN; 213 err = DDI_SUCCESS; 214 } 215 mutex_exit(&icsp->ics951601_mutex); 216 return (err); 217 } 218 219 static int 220 ics951601_close(dev_t dev, int flags, int otyp, cred_t *credp) 221 { 222 _NOTE(ARGUNUSED(flags, credp)) 223 224 int instance; 225 ics951601_unit_t *icsp; 226 227 /* 228 * Make sure the close is for the right file type 229 */ 230 if (otyp != OTYP_CHR) { 231 return (EINVAL); 232 } 233 234 instance = getminor(dev); 235 icsp = (ics951601_unit_t *)ddi_get_soft_state(ics951601_soft_statep, 236 instance); 237 if (icsp == NULL) { 238 return (ENXIO); 239 } 240 241 mutex_enter(&icsp->ics951601_mutex); 242 icsp->ics951601_oflag = 0; 243 mutex_exit(&icsp->ics951601_mutex); 244 return (DDI_SUCCESS); 245 } 246 247 static int 248 ics951601_attach(dev_info_t *dip) 249 { 250 ics951601_unit_t *icsp; 251 int instance = ddi_get_instance(dip); 252 253 if (ddi_soft_state_zalloc(ics951601_soft_statep, instance) != 0) { 254 cmn_err(CE_WARN, "%s%d failed to zalloc softstate", 255 ddi_get_name(dip), instance); 256 return (DDI_FAILURE); 257 } 258 259 icsp = ddi_get_soft_state(ics951601_soft_statep, instance); 260 261 if (icsp == NULL) { 262 return (DDI_FAILURE); 263 } 264 265 mutex_init(&icsp->ics951601_mutex, NULL, MUTEX_DRIVER, NULL); 266 cv_init(&icsp->ics951601_cv, NULL, CV_DRIVER, NULL); 267 268 (void) snprintf(icsp->ics951601_name, sizeof (icsp->ics951601_name), 269 "%s_%d", ddi_driver_name(dip), instance); 270 271 272 if (ddi_create_minor_node(dip, icsp->ics951601_name, S_IFCHR, 273 instance, ICS951601_NODE_TYPE, NULL) == DDI_FAILURE) { 274 cmn_err(CE_WARN, "%s ddi_create_minor_node failed", 275 icsp->ics951601_name); 276 goto ATTACH_ERR; 277 } 278 279 /* 280 * preallocate a single buffer for all reads and writes 281 */ 282 if (i2c_transfer_alloc(icsp->ics951601_hdl, &icsp->ics951601_transfer, 283 0, 0, I2C_SLEEP) != I2C_SUCCESS) { 284 cmn_err(CE_WARN, "%s i2c_transfer_alloc failed", 285 icsp->ics951601_name); 286 goto CREATE_NODE_ERR; 287 } 288 icsp->ics951601_cpr_state[0] = 0x0; 289 icsp->ics951601_transfer->i2c_version = I2C_XFER_REV; 290 291 if (i2c_client_register(dip, &icsp->ics951601_hdl) != I2C_SUCCESS) { 292 cmn_err(CE_WARN, "%s i2c_client_register failed", 293 icsp->ics951601_name); 294 goto ALLOC_ERR; 295 } 296 297 /* Enable all clocks */ 298 icsp->ics951601_transfer->i2c_flags = I2C_WR; 299 icsp->ics951601_transfer->i2c_wlen = ICS951601_I2C_WRITE_TRANS_SIZE; 300 icsp->ics951601_transfer->i2c_rlen = 0; 301 icsp->ics951601_transfer->i2c_wbuf = init_clock_regs; 302 303 if (i2c_transfer(icsp->ics951601_hdl, icsp->ics951601_transfer) 304 != I2C_SUCCESS) { 305 goto REG_ERR; 306 } 307 308 /* 309 * Store the dip for future use 310 */ 311 icsp->ics951601_dip = dip; 312 313 return (DDI_SUCCESS); 314 REG_ERR: 315 i2c_client_unregister(icsp->ics951601_hdl); 316 317 ALLOC_ERR: 318 i2c_transfer_free(icsp->ics951601_hdl, icsp->ics951601_transfer); 319 320 CREATE_NODE_ERR: 321 ddi_remove_minor_node(dip, NULL); 322 323 ATTACH_ERR: 324 cv_destroy(&icsp->ics951601_cv); 325 mutex_destroy(&icsp->ics951601_mutex); 326 ddi_soft_state_free(ics951601_soft_statep, instance); 327 328 return (DDI_FAILURE); 329 } 330 331 332 static void 333 ics951601_detach(dev_info_t *dip) 334 { 335 ics951601_unit_t *icsp; 336 int instance; 337 338 instance = ddi_get_instance(dip); 339 icsp = ddi_get_soft_state(ics951601_soft_statep, instance); 340 cv_destroy(&icsp->ics951601_cv); 341 mutex_destroy(&icsp->ics951601_mutex); 342 i2c_client_unregister(icsp->ics951601_hdl); 343 i2c_transfer_free(icsp->ics951601_hdl, icsp->ics951601_transfer); 344 ddi_remove_minor_node(dip, NULL); 345 ddi_soft_state_free(ics951601_soft_statep, instance); 346 } 347 348 static int 349 ics951601_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 350 { 351 _NOTE(ARGUNUSED(dip)) 352 353 ics951601_unit_t *icsp; 354 int instance = getminor((dev_t)arg); 355 356 switch (cmd) { 357 case DDI_INFO_DEVT2DEVINFO: 358 icsp = ddi_get_soft_state(ics951601_soft_statep, instance); 359 if (icsp == NULL) { 360 return (DDI_FAILURE); 361 } 362 *result = (void *)icsp->ics951601_dip; 363 364 return (DDI_SUCCESS); 365 case DDI_INFO_DEVT2INSTANCE: 366 *result = (void *)instance; 367 368 return (DDI_SUCCESS); 369 default: 370 return (DDI_FAILURE); 371 } 372 } 373 374 static int 375 ics951601_suspend(dev_info_t *dip) 376 { 377 ics951601_unit_t *icsp; 378 int instance = ddi_get_instance(dip); 379 380 icsp = ddi_get_soft_state(ics951601_soft_statep, instance); 381 382 /* 383 * Set the busy flag so that future transactions block 384 * until resume. 385 */ 386 mutex_enter(&icsp->ics951601_mutex); 387 while ((icsp->ics951601_flags & ICS951601_BUSYFLAG) == 388 ICS951601_BUSYFLAG) { 389 if (cv_wait_sig(&icsp->ics951601_cv, 390 &icsp->ics951601_mutex) <= 0) { 391 mutex_exit(&icsp->ics951601_mutex); 392 return (DDI_FAILURE); 393 } 394 } 395 icsp->ics951601_flags |= ICS951601_BUSYFLAG; 396 mutex_exit(&icsp->ics951601_mutex); 397 398 icsp->ics951601_transfer->i2c_flags = I2C_RD; 399 icsp->ics951601_transfer->i2c_wlen = 0; 400 icsp->ics951601_transfer->i2c_rlen = ICS951601_I2C_READ_TRANS_SIZE; 401 icsp->ics951601_transfer->i2c_rbuf = icsp->ics951601_cpr_state + 1; 402 403 if (i2c_transfer(icsp->ics951601_hdl, icsp->ics951601_transfer) 404 != I2C_SUCCESS) { 405 cmn_err(CE_WARN, "%s Suspend failed, unable to save registers", 406 icsp->ics951601_name); 407 return (EIO); 408 } 409 return (DDI_SUCCESS); 410 } 411 412 413 static int 414 ics951601_resume(dev_info_t *dip) 415 { 416 int instance = ddi_get_instance(dip); 417 ics951601_unit_t *icsp; 418 int err = DDI_SUCCESS; 419 420 icsp = (ics951601_unit_t *) 421 ddi_get_soft_state(ics951601_soft_statep, instance); 422 423 if (icsp == NULL) { 424 return (ENXIO); 425 } 426 427 /* 428 * Restore registers to status existing before cpr 429 */ 430 icsp->ics951601_transfer->i2c_flags = I2C_WR; 431 icsp->ics951601_transfer->i2c_rlen = 0; 432 icsp->ics951601_transfer->i2c_wlen = ICS951601_I2C_WRITE_TRANS_SIZE; 433 434 icsp->ics951601_transfer->i2c_wbuf = icsp->ics951601_cpr_state; 435 436 if (i2c_transfer(icsp->ics951601_hdl, icsp->ics951601_transfer) 437 != I2C_SUCCESS) { 438 err = EIO; 439 cmn_err(CE_WARN, " %s Unable to restore registers", 440 icsp->ics951601_name); 441 } 442 443 /* 444 * Clear busy flag so that transactions may continue 445 */ 446 mutex_enter(&icsp->ics951601_mutex); 447 icsp->ics951601_flags = icsp->ics951601_flags & ~ICS951601_BUSYFLAG; 448 cv_signal(&icsp->ics951601_cv); 449 mutex_exit(&icsp->ics951601_mutex); 450 return (err); 451 } 452 453 static int 454 ics951601_s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 455 { 456 switch (cmd) { 457 case DDI_ATTACH: 458 return (ics951601_attach(dip)); 459 case DDI_RESUME: 460 return (ics951601_resume(dip)); 461 default: 462 return (DDI_FAILURE); 463 } 464 } 465 466 static int 467 ics951601_s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 468 { 469 switch (cmd) { 470 case DDI_DETACH: 471 ics951601_detach(dip); 472 return (DDI_SUCCESS); 473 case DDI_SUSPEND: 474 return (ics951601_suspend(dip)); 475 default: 476 return (DDI_FAILURE); 477 } 478 } 479 480 static int 481 ics951601_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 482 int *rvalp) 483 { 484 _NOTE(ARGUNUSED(credp, rvalp)) 485 486 ics951601_unit_t *icsp; 487 int err = 0; 488 uchar_t temp_arr[ICS951601_I2C_WRITE_TRANS_SIZE]; 489 int reg_no; 490 uint8_t clock_bit; 491 int ics_temp; 492 int instance = getminor(dev); 493 494 temp_arr[0] = 0x0; 495 496 icsp = (ics951601_unit_t *) 497 ddi_get_soft_state(ics951601_soft_statep, instance); 498 499 if (ics951601_debug) { 500 printf("ics951601_ioctl: instance=%d\n", instance); 501 } 502 503 /* 504 * We serialize here and block if there are pending transacations . 505 */ 506 mutex_enter(&icsp->ics951601_mutex); 507 while ((icsp->ics951601_flags & ICS951601_BUSYFLAG) == 508 ICS951601_BUSYFLAG) { 509 if (cv_wait_sig(&icsp->ics951601_cv, 510 &icsp->ics951601_mutex) <= 0) { 511 mutex_exit(&icsp->ics951601_mutex); 512 return (EINTR); 513 } 514 } 515 icsp->ics951601_flags |= ICS951601_BUSYFLAG; 516 mutex_exit(&icsp->ics951601_mutex); 517 518 reg_no = ICS951601_CMD_TO_CLOCKREG(cmd); 519 clock_bit = ICS951601_CMD_TO_CLOCKBIT(cmd); 520 521 icsp->ics951601_transfer->i2c_flags = I2C_RD; 522 icsp->ics951601_transfer->i2c_wlen = 0; 523 icsp->ics951601_transfer->i2c_rlen = ICS951601_I2C_READ_TRANS_SIZE; 524 icsp->ics951601_transfer->i2c_rbuf = temp_arr + 1; 525 526 if (i2c_transfer(icsp->ics951601_hdl, icsp->ics951601_transfer) 527 != I2C_SUCCESS) { 528 529 err = DDI_FAILURE; 530 goto cleanup; 531 } 532 switch (ICS951601_CMD_TO_ACTION(cmd)) { 533 case ICS951601_READ_CLOCK: 534 temp_arr[reg_no] &= clock_bit; 535 ics_temp = temp_arr[reg_no] ? ICS951601_CLOCK_SET: 536 ICS951601_CLOCK_CLEAR; 537 err = ddi_copyout((caddr_t)&ics_temp, (caddr_t)arg, 538 sizeof (int), mode); 539 goto cleanup; 540 case ICS951601_MODIFY_CLOCK: 541 if (ddi_copyin((caddr_t)arg, (caddr_t)&ics_temp, 542 sizeof (int), mode) != DDI_SUCCESS) { 543 err = EIO; 544 goto cleanup; 545 } 546 if (ics_temp == ICS951601_CLOCK_SET) { 547 temp_arr[reg_no] |= clock_bit; 548 } else if (ics_temp == ICS951601_CLOCK_CLEAR) { 549 temp_arr[reg_no] &= ~clock_bit; 550 } else { 551 cmn_err(CE_WARN, "%s Clock can only be set to 1 or 0", 552 icsp->ics951601_name); 553 err = EINVAL; 554 goto cleanup; 555 } 556 break; 557 default: 558 err = EINVAL; 559 goto cleanup; 560 } 561 562 icsp->ics951601_transfer->i2c_flags = I2C_WR; 563 icsp->ics951601_transfer->i2c_wlen = ICS951601_I2C_WRITE_TRANS_SIZE; 564 icsp->ics951601_transfer->i2c_rlen = 0; 565 icsp->ics951601_transfer->i2c_wbuf = temp_arr; 566 567 if (i2c_transfer(icsp->ics951601_hdl, icsp->ics951601_transfer) 568 != I2C_SUCCESS) { 569 570 err = DDI_FAILURE; 571 goto cleanup; 572 } 573 cleanup: 574 mutex_enter(&icsp->ics951601_mutex); 575 icsp->ics951601_flags = icsp->ics951601_flags & ~ICS951601_BUSYFLAG; 576 cv_signal(&icsp->ics951601_cv); 577 mutex_exit(&icsp->ics951601_mutex); 578 return (err); 579 } 580