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 #include <sys/stat.h> /* ddi_create_minor_node S_IFCHR */ 27 #include <sys/modctl.h> /* for modldrv */ 28 #include <sys/open.h> /* for open params. */ 29 #include <sys/types.h> 30 #include <sys/sunddi.h> 31 #include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */ 32 #include <sys/ddi.h> 33 #include <sys/file.h> 34 #include <sys/note.h> 35 #include <sys/i2c/clients/i2c_client.h> 36 #include <sys/i2c/clients/ssc050.h> 37 38 #define SSC050_NUM_PORTS 5 39 #define SSC050_DATADIRECTION_REG(port) (0x10 | (port)) 40 #define SSC050_COUNT_REG(port) (0x32 | ((port) << 2)) 41 #define SSC050_GP_REG(port) (port) 42 #define SSC050_BIT_REG(port, bit) (SSC050_PORT_BIT_REG(port) | (bit)) 43 44 #define SSC050_FAN_SPEED(div, count) (1200000 / ((count) * (1<<(div)))) 45 #define SSC050_FAN_CONTROL_ENABLE 0x80 46 #define SSC050_FAN_CONTROL_DIVISOR 0x03 47 48 #define SSC050_DATADIRECTION_BIT 0x02 49 50 struct ssc050_unit { 51 kmutex_t mutex; 52 int oflag; 53 i2c_client_hdl_t hdl; 54 char name[12]; 55 }; 56 57 #ifdef DEBUG 58 59 static int ssc050debug = 0; 60 #define D1CMN_ERR(ARGS) if (ssc050debug & 0x01) cmn_err ARGS; 61 #define D2CMN_ERR(ARGS) if (ssc050debug & 0x02) cmn_err ARGS; 62 #define D3CMN_ERR(ARGS) if (ssc050debug & 0x04) cmn_err ARGS; 63 64 #else 65 66 #define D1CMN_ERR(ARGS) 67 #define D2CMN_ERR(ARGS) 68 #define D3CMN_ERR(ARGS) 69 70 #endif 71 72 static void *ssc050soft_statep; 73 74 static int ssc050_do_attach(dev_info_t *); 75 static int ssc050_do_detach(dev_info_t *); 76 static int ssc050_set(struct ssc050_unit *, int, uchar_t); 77 static int ssc050_get(struct ssc050_unit *, int, uchar_t *, int); 78 79 /* 80 * cb ops 81 */ 82 static int ssc050_open(dev_t *, int, int, cred_t *); 83 static int ssc050_close(dev_t, int, int, cred_t *); 84 static int ssc050_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 85 86 87 static struct cb_ops ssc050_cbops = { 88 ssc050_open, /* open */ 89 ssc050_close, /* close */ 90 nodev, /* strategy */ 91 nodev, /* print */ 92 nodev, /* dump */ 93 nodev, /* read */ 94 nodev, /* write */ 95 ssc050_ioctl, /* ioctl */ 96 nodev, /* devmap */ 97 nodev, /* mmap */ 98 nodev, /* segmap */ 99 nochpoll, /* poll */ 100 ddi_prop_op, /* cb_prop_op */ 101 NULL, /* streamtab */ 102 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 103 CB_REV, /* rev */ 104 nodev, /* int (*cb_aread)() */ 105 nodev /* int (*cb_awrite)() */ 106 }; 107 108 /* 109 * dev ops 110 */ 111 static int ssc050_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 112 void **result); 113 static int ssc050_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 114 static int ssc050_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 115 116 static struct dev_ops ssc050_ops = { 117 DEVO_REV, 118 0, 119 ssc050_info, 120 nulldev, 121 nulldev, 122 ssc050_attach, 123 ssc050_detach, 124 nodev, 125 &ssc050_cbops, 126 NULL, 127 NULL, 128 ddi_quiesce_not_needed, /* quiesce */ 129 }; 130 131 extern struct mod_ops mod_driverops; 132 133 static struct modldrv ssc050_modldrv = { 134 &mod_driverops, /* type of module - driver */ 135 "SSC050 i2c device driver", 136 &ssc050_ops 137 }; 138 139 static struct modlinkage ssc050_modlinkage = { 140 MODREV_1, 141 &ssc050_modldrv, 142 0 143 }; 144 145 146 int 147 _init(void) 148 { 149 int error; 150 151 error = mod_install(&ssc050_modlinkage); 152 153 if (!error) 154 (void) ddi_soft_state_init(&ssc050soft_statep, 155 sizeof (struct ssc050_unit), 1); 156 return (error); 157 } 158 159 int 160 _fini(void) 161 { 162 int error; 163 164 error = mod_remove(&ssc050_modlinkage); 165 if (!error) 166 ddi_soft_state_fini(&ssc050soft_statep); 167 168 return (error); 169 } 170 171 int 172 _info(struct modinfo *modinfop) 173 { 174 return (mod_info(&ssc050_modlinkage, modinfop)); 175 } 176 177 static int 178 ssc050_open(dev_t *devp, int flags, int otyp, cred_t *credp) 179 { 180 _NOTE(ARGUNUSED(credp)) 181 182 struct ssc050_unit *unitp; 183 int instance; 184 int error = 0; 185 186 instance = MINOR_TO_INST(getminor(*devp)); 187 188 if (instance < 0) { 189 return (ENXIO); 190 } 191 192 unitp = (struct ssc050_unit *) 193 ddi_get_soft_state(ssc050soft_statep, instance); 194 195 if (unitp == NULL) { 196 return (ENXIO); 197 } 198 199 if (otyp != OTYP_CHR) { 200 return (EINVAL); 201 } 202 203 mutex_enter(&unitp->mutex); 204 205 if (flags & FEXCL) { 206 if (unitp->oflag != 0) { 207 error = EBUSY; 208 } else { 209 unitp->oflag = FEXCL; 210 } 211 } else { 212 if (unitp->oflag == FEXCL) { 213 error = EBUSY; 214 } else { 215 unitp->oflag = FOPEN; 216 } 217 } 218 219 mutex_exit(&unitp->mutex); 220 221 return (error); 222 } 223 224 static int 225 ssc050_close(dev_t dev, int flags, int otyp, cred_t *credp) 226 { 227 _NOTE(ARGUNUSED(flags, otyp, credp)) 228 229 struct ssc050_unit *unitp; 230 int instance; 231 232 instance = MINOR_TO_INST(getminor(dev)); 233 234 if (instance < 0) { 235 return (ENXIO); 236 } 237 238 unitp = (struct ssc050_unit *) 239 ddi_get_soft_state(ssc050soft_statep, instance); 240 241 if (unitp == NULL) { 242 return (ENXIO); 243 } 244 245 mutex_enter(&unitp->mutex); 246 247 unitp->oflag = 0; 248 249 mutex_exit(&unitp->mutex); 250 return (DDI_SUCCESS); 251 } 252 253 static int 254 ssc050_get(struct ssc050_unit *unitp, int reg, uchar_t *byte, int flags) 255 { 256 i2c_transfer_t *i2c_tran_pointer; 257 int err; 258 259 (void) i2c_transfer_alloc(unitp->hdl, &i2c_tran_pointer, 260 1, 1, flags); 261 if (i2c_tran_pointer == NULL) { 262 return (ENOMEM); 263 } 264 265 i2c_tran_pointer->i2c_flags = I2C_WR_RD; 266 i2c_tran_pointer->i2c_wbuf[0] = (uchar_t)reg; 267 err = i2c_transfer(unitp->hdl, i2c_tran_pointer); 268 if (err) { 269 D2CMN_ERR((CE_WARN, "%s: ssc050_get failed reg=%x", 270 unitp->name, reg)); 271 } else { 272 *byte = i2c_tran_pointer->i2c_rbuf[0]; 273 } 274 275 i2c_transfer_free(unitp->hdl, i2c_tran_pointer); 276 return (err); 277 } 278 279 static int 280 ssc050_set(struct ssc050_unit *unitp, int reg, uchar_t byte) 281 { 282 i2c_transfer_t *i2c_tran_pointer; 283 int err; 284 285 (void) i2c_transfer_alloc(unitp->hdl, &i2c_tran_pointer, 286 2, 0, I2C_SLEEP); 287 if (i2c_tran_pointer == NULL) { 288 D2CMN_ERR((CE_WARN, "%s: Failed in ssc050_set " 289 "i2c_tran_pointer not allocated", unitp->name)); 290 return (ENOMEM); 291 } 292 293 i2c_tran_pointer->i2c_flags = I2C_WR; 294 i2c_tran_pointer->i2c_wbuf[0] = (uchar_t)reg; 295 i2c_tran_pointer->i2c_wbuf[1] = byte; 296 D1CMN_ERR((CE_NOTE, "%s: set reg %x to %x", unitp->name, reg, byte)); 297 298 err = i2c_transfer(unitp->hdl, i2c_tran_pointer); 299 if (err) { 300 D2CMN_ERR((CE_WARN, "%s: Failed in the ssc050_set" 301 " i2c_transfer routine", unitp->name)); 302 } 303 i2c_transfer_free(unitp->hdl, i2c_tran_pointer); 304 return (err); 305 } 306 307 static int 308 ssc050_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 309 int *rvalp) 310 { 311 _NOTE(ARGUNUSED(credp, rvalp)) 312 313 struct ssc050_unit *unitp; 314 int err = 0; 315 i2c_bit_t ioctl_bit; 316 i2c_port_t ioctl_port; 317 i2c_reg_t ioctl_reg; 318 int port = MINOR_TO_PORT(getminor(dev)); 319 int instance = MINOR_TO_INST(getminor(dev)); 320 uchar_t reg, val8; 321 uchar_t control; 322 uchar_t fan_count; 323 int divisor; 324 int32_t fan_speed; 325 uint8_t inverted_mask; 326 327 if (arg == NULL) { 328 D2CMN_ERR((CE_WARN, "SSC050: ioctl: arg passed in to ioctl " 329 "= NULL")); 330 return (EINVAL); 331 } 332 unitp = (struct ssc050_unit *) 333 ddi_get_soft_state(ssc050soft_statep, instance); 334 335 if (unitp == NULL) { 336 return (ENXIO); 337 } 338 339 mutex_enter(&unitp->mutex); 340 341 D3CMN_ERR((CE_NOTE, "%s: ioctl: port = %d", unitp->name, port)); 342 343 switch (cmd) { 344 case I2C_GET_PORT: 345 if (ddi_copyin((caddr_t)arg, (caddr_t)&ioctl_port, 346 sizeof (i2c_port_t), mode) != DDI_SUCCESS) { 347 err = EFAULT; 348 break; 349 } 350 351 if (ioctl_port.direction == DIR_INPUT) { 352 reg = SSC050_DATADIRECTION_REG(port); 353 354 err = ssc050_get(unitp, reg, &val8, I2C_SLEEP); 355 if (err != I2C_SUCCESS) { 356 break; 357 } 358 359 if (val8 != ioctl_port.dir_mask) { 360 D2CMN_ERR((CE_NOTE, "GET_PORT sleeping! " 361 "wanted %x, had %x", 362 ioctl_port.dir_mask, val8)); 363 err = ssc050_set(unitp, reg, 364 ioctl_port.dir_mask); 365 if (err != I2C_SUCCESS) { 366 break; 367 } 368 delay(10); 369 } 370 } 371 372 err = ssc050_get(unitp, port, &val8, I2C_SLEEP); 373 if (err != I2C_SUCCESS) { 374 break; 375 } 376 ioctl_port.value = val8; 377 if (ddi_copyout((caddr_t)&ioctl_port, (caddr_t)arg, 378 sizeof (i2c_port_t), mode) != DDI_SUCCESS) { 379 err = EFAULT; 380 } 381 break; 382 383 case I2C_SET_PORT: 384 if (ddi_copyin((caddr_t)arg, (caddr_t)&ioctl_port, 385 sizeof (i2c_port_t), mode) != DDI_SUCCESS) { 386 err = EFAULT; 387 break; 388 } 389 390 reg = SSC050_DATADIRECTION_REG(port); 391 392 err = ssc050_get(unitp, reg, &val8, I2C_SLEEP); 393 if (err != I2C_SUCCESS) { 394 break; 395 } 396 397 D1CMN_ERR((CE_NOTE, "%s: ioctl: Data Direction Register " 398 "contains %x", unitp->name, val8)); 399 400 inverted_mask = ioctl_port.dir_mask ^ 0xff; 401 val8 = val8 & inverted_mask; 402 403 D1CMN_ERR((CE_NOTE, "%s: ioctl: Data Direction Register " 404 "NOW contains %x", unitp->name, val8)); 405 406 err = ssc050_set(unitp, reg, val8); 407 if (err != I2C_SUCCESS) { 408 break; 409 } 410 411 err = ssc050_get(unitp, port, &val8, I2C_SLEEP); 412 if (err != I2C_SUCCESS) { 413 break; 414 } 415 416 D1CMN_ERR((CE_NOTE, "%s: ioctl: GP Register " 417 "contains %x", unitp->name, val8)); 418 419 val8 = val8 & inverted_mask; 420 val8 = val8 | ioctl_port.value; 421 422 D1CMN_ERR((CE_NOTE, "%s: ioctl: GP Register " 423 "NOW contains %x", unitp->name, val8)); 424 425 err = ssc050_set(unitp, SSC050_GP_REG(port), val8); 426 break; 427 428 case I2C_GET_FAN_SPEED: 429 err = ssc050_get(unitp, SSC050_FAN_CONTROL_REG(port), 430 &control, I2C_SLEEP); 431 if (err != I2C_SUCCESS) { 432 break; 433 } 434 435 D1CMN_ERR((CE_NOTE, "%s: port %d: control = %x", unitp->name, 436 port, control)); 437 438 if (!(control & SSC050_FAN_CONTROL_ENABLE)) { 439 err = EIO; 440 break; 441 } 442 443 err = ssc050_get(unitp, SSC050_COUNT_REG(port), &fan_count, 444 I2C_SLEEP); 445 if (err != I2C_SUCCESS) { 446 break; 447 } 448 449 if (fan_count == 0) { 450 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_FAN_SPEED " 451 "i2c_rbuf = 0", unitp->name)); 452 err = EIO; 453 break; 454 } 455 if (fan_count == 0xff) { 456 fan_speed = 0; 457 if (ddi_copyout((caddr_t)&fan_speed, (caddr_t)arg, 458 sizeof (int32_t), mode) != DDI_SUCCESS) { 459 err = EFAULT; 460 break; 461 } 462 break; 463 } 464 465 divisor = control & SSC050_FAN_CONTROL_DIVISOR; 466 fan_speed = SSC050_FAN_SPEED(divisor, fan_count); 467 if (ddi_copyout((caddr_t)&fan_speed, (caddr_t)arg, 468 sizeof (int32_t), mode) != DDI_SUCCESS) { 469 err = EFAULT; 470 } 471 break; 472 473 case I2C_GET_BIT: 474 if (ddi_copyin((caddr_t)arg, (caddr_t)&ioctl_bit, 475 sizeof (i2c_bit_t), mode) != DDI_SUCCESS) { 476 err = EFAULT; 477 break; 478 } 479 480 if (ioctl_bit.bit_num > 7) { 481 err = EINVAL; 482 break; 483 } 484 485 reg = (uchar_t)SSC050_BIT_REG(port, ioctl_bit.bit_num); 486 D3CMN_ERR((CE_NOTE, "%s: reg = %x", unitp->name, reg)); 487 488 if (ioctl_bit.direction == DIR_INPUT) { 489 err = ssc050_get(unitp, reg, &val8, I2C_SLEEP); 490 if (err != I2C_SUCCESS) { 491 break; 492 } 493 494 if (!(val8 & SSC050_DATADIRECTION_BIT)) { 495 D2CMN_ERR((CE_NOTE, "GET_PORT sleeping! " 496 "wanted %x, had %x", 497 val8 | SSC050_DATADIRECTION_BIT, 498 val8)); 499 err = ssc050_set(unitp, reg, 500 val8 | SSC050_DATADIRECTION_BIT); 501 if (err != I2C_SUCCESS) { 502 break; 503 } 504 delay(10); 505 } 506 } 507 508 err = ssc050_get(unitp, reg, &val8, I2C_SLEEP); 509 if (err != I2C_SUCCESS) { 510 break; 511 } 512 D3CMN_ERR((CE_NOTE, "byte back from device = %x", val8)); 513 val8 = val8 & 0x01; 514 ioctl_bit.bit_value = (boolean_t)val8; 515 if (ddi_copyout((caddr_t)&ioctl_bit, (caddr_t)arg, 516 sizeof (i2c_bit_t), mode) != DDI_SUCCESS) { 517 err = EFAULT; 518 } 519 break; 520 521 case I2C_SET_BIT: 522 if (ddi_copyin((caddr_t)arg, (caddr_t)&ioctl_bit, 523 sizeof (i2c_bit_t), mode) != DDI_SUCCESS) { 524 err = EFAULT; 525 break; 526 } 527 528 if (ioctl_bit.bit_num > 7) { 529 err = EINVAL; 530 break; 531 } 532 533 reg = (uchar_t)SSC050_BIT_REG(port, ioctl_bit.bit_num); 534 D3CMN_ERR((CE_NOTE, "%s: reg = %x", unitp->name, reg)); 535 536 val8 = (uchar_t)ioctl_bit.bit_value; 537 err = ssc050_set(unitp, reg, val8); 538 break; 539 540 case I2C_GET_REG: 541 if (ddi_copyin((caddr_t)arg, (caddr_t)&ioctl_reg, 542 sizeof (i2c_reg_t), mode) != DDI_SUCCESS) { 543 err = EFAULT; 544 break; 545 } 546 err = ssc050_get(unitp, ioctl_reg.reg_num, &val8, 547 I2C_SLEEP); 548 if (err != I2C_SUCCESS) { 549 break; 550 } 551 552 ioctl_reg.reg_value = val8; 553 if (ddi_copyout((caddr_t)&ioctl_reg, (caddr_t)arg, 554 sizeof (i2c_reg_t), mode) != DDI_SUCCESS) { 555 err = EFAULT; 556 } 557 break; 558 559 case I2C_SET_REG: 560 if (ddi_copyin((caddr_t)arg, (caddr_t)&ioctl_reg, 561 sizeof (i2c_reg_t), mode) != DDI_SUCCESS) { 562 err = EFAULT; 563 break; 564 } 565 err = ssc050_set(unitp, ioctl_reg.reg_num, 566 ioctl_reg.reg_value); 567 break; 568 569 default: 570 D2CMN_ERR((CE_WARN, "%s: Invalid IOCTL cmd: %x", 571 unitp->name, cmd)); 572 err = EINVAL; 573 } 574 575 mutex_exit(&unitp->mutex); 576 return (err); 577 } 578 579 /* ARGSUSED */ 580 static int 581 ssc050_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 582 { 583 dev_t dev; 584 int instance; 585 586 if (infocmd == DDI_INFO_DEVT2INSTANCE) { 587 dev = (dev_t)arg; 588 instance = MINOR_TO_INST(getminor(dev)); 589 *result = (void *)(uintptr_t)instance; 590 return (DDI_SUCCESS); 591 } 592 return (DDI_FAILURE); 593 } 594 595 static int 596 ssc050_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 597 { 598 switch (cmd) { 599 case DDI_ATTACH: 600 return (ssc050_do_attach(dip)); 601 case DDI_RESUME: 602 return (DDI_SUCCESS); 603 default: 604 return (DDI_FAILURE); 605 } 606 } 607 608 static int 609 ssc050_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 610 { 611 switch (cmd) { 612 case DDI_DETACH: 613 return (ssc050_do_detach(dip)); 614 615 case DDI_SUSPEND: 616 return (DDI_SUCCESS); 617 618 default: 619 return (DDI_FAILURE); 620 } 621 } 622 623 static int 624 ssc050_do_attach(dev_info_t *dip) 625 { 626 struct ssc050_unit *unitp; 627 int instance; 628 char name[MAXNAMELEN]; 629 minor_t minor_number; 630 int i; 631 632 instance = ddi_get_instance(dip); 633 634 if (ddi_soft_state_zalloc(ssc050soft_statep, instance) != 0) { 635 return (DDI_FAILURE); 636 } 637 638 unitp = ddi_get_soft_state(ssc050soft_statep, instance); 639 640 (void) snprintf(unitp->name, sizeof (unitp->name), 641 "%s%d", ddi_node_name(dip), instance); 642 643 for (i = 0; i < SSC050_NUM_PORTS; i++) { 644 (void) sprintf(name, "port_%d", i); 645 646 minor_number = INST_TO_MINOR(instance) | 647 PORT_TO_MINOR(I2C_PORT(i)); 648 649 if (ddi_create_minor_node(dip, name, S_IFCHR, minor_number, 650 "ddi_i2c:ioexp", NULL) == DDI_FAILURE) { 651 cmn_err(CE_WARN, "%s: failed to create node for %s", 652 unitp->name, name); 653 ddi_soft_state_free(ssc050soft_statep, instance); 654 return (DDI_FAILURE); 655 } 656 } 657 658 if (i2c_client_register(dip, &unitp->hdl) != I2C_SUCCESS) { 659 ddi_remove_minor_node(dip, NULL); 660 ddi_soft_state_free(ssc050soft_statep, instance); 661 return (DDI_FAILURE); 662 } 663 664 mutex_init(&unitp->mutex, NULL, MUTEX_DRIVER, NULL); 665 666 return (DDI_SUCCESS); 667 } 668 669 static int 670 ssc050_do_detach(dev_info_t *dip) 671 { 672 struct ssc050_unit *unitp; 673 int instance; 674 675 instance = ddi_get_instance(dip); 676 unitp = ddi_get_soft_state(ssc050soft_statep, instance); 677 i2c_client_unregister(unitp->hdl); 678 ddi_remove_minor_node(dip, NULL); 679 mutex_destroy(&unitp->mutex); 680 ddi_soft_state_free(ssc050soft_statep, instance); 681 682 return (DDI_SUCCESS); 683 } 684 685 int 686 ssc050_get_port_bit(dev_info_t *dip, int port, int bit, uchar_t *rval, 687 int flags) 688 { 689 struct ssc050_unit *unitp; 690 int instance; 691 int reg = (uchar_t)SSC050_BIT_REG(port, bit); 692 693 if (rval == NULL || dip == NULL) 694 return (EINVAL); 695 696 instance = ddi_get_instance(dip); 697 unitp = ddi_get_soft_state(ssc050soft_statep, instance); 698 if (unitp == NULL) { 699 return (ENXIO); 700 } 701 return (ssc050_get(unitp, reg, rval, flags)); 702 } 703