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-2001 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 30 #include <sys/stat.h> /* ddi_create_minor_node S_IFCHR */ 31 #include <sys/modctl.h> /* for modldrv */ 32 #include <sys/open.h> /* for open params. */ 33 #include <sys/types.h> 34 #include <sys/kmem.h> 35 #include <sys/sunddi.h> 36 #include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */ 37 #include <sys/ddi.h> 38 #include <sys/file.h> 39 #include <sys/note.h> 40 41 #include <sys/i2c/clients/pcf8591.h> 42 #include <sys/i2c/clients/pcf8591_impl.h> 43 44 static void *pcf8591soft_statep; 45 46 static uint8_t ipmode = PCF8591_4SINGLE; 47 static int32_t current_value = 0; 48 static int current_set_flag = 0; 49 50 static int pcf8591_do_attach(dev_info_t *); 51 static int pcf8591_do_detach(dev_info_t *); 52 static int pcf8591_do_resume(void); 53 static int pcf8591_do_suspend(void); 54 55 /* 56 * cb ops (only need ioctl) 57 */ 58 static int pcf8591_open(dev_t *, int, int, cred_t *); 59 static int pcf8591_close(dev_t, int, int, cred_t *); 60 static int pcf8591_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 61 62 static struct cb_ops pcf8591_cbops = { 63 pcf8591_open, /* open */ 64 pcf8591_close, /* close */ 65 nodev, /* strategy */ 66 nodev, /* print */ 67 nodev, /* dump */ 68 nodev, /* read */ 69 nodev, /* write */ 70 pcf8591_ioctl, /* ioctl */ 71 nodev, /* devmap */ 72 nodev, /* mmap */ 73 nodev, /* segmap */ 74 nochpoll, /* poll */ 75 ddi_prop_op, /* cb_prop_op */ 76 NULL, /* streamtab */ 77 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 78 CB_REV, /* rev */ 79 nodev, /* int (*cb_aread)() */ 80 nodev /* int (*cb_awrite)() */ 81 }; 82 83 /* 84 * dev ops 85 */ 86 static int pcf8591_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 87 void **result); 88 static int pcf8591_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 89 static int pcf8591_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 90 91 static struct dev_ops pcf8591_ops = { 92 DEVO_REV, 93 0, 94 pcf8591_info, 95 nulldev, 96 nulldev, 97 pcf8591_attach, 98 pcf8591_detach, 99 nodev, 100 &pcf8591_cbops, 101 NULL 102 }; 103 104 extern struct mod_ops mod_driverops; 105 106 static struct modldrv pcf8591_modldrv = { 107 &mod_driverops, /* type of module - driver */ 108 "PCF8591 i2c device driver: v%I%", 109 &pcf8591_ops 110 }; 111 112 static struct modlinkage pcf8591_modlinkage = { 113 MODREV_1, 114 &pcf8591_modldrv, 115 0 116 }; 117 118 119 int 120 _init(void) 121 { 122 int error; 123 124 error = mod_install(&pcf8591_modlinkage); 125 if (!error) 126 (void) ddi_soft_state_init(&pcf8591soft_statep, 127 sizeof (struct pcf8591_unit), 1); 128 return (error); 129 } 130 131 int 132 _fini(void) 133 { 134 int error; 135 136 error = mod_remove(&pcf8591_modlinkage); 137 if (!error) 138 ddi_soft_state_fini(&pcf8591soft_statep); 139 140 return (error); 141 } 142 143 int 144 _info(struct modinfo *modinfop) 145 { 146 return (mod_info(&pcf8591_modlinkage, modinfop)); 147 } 148 149 static int 150 pcf8591_open(dev_t *devp, int flags, int otyp, cred_t *credp) 151 { 152 _NOTE(ARGUNUSED(credp)) 153 154 struct pcf8591_unit *unitp; 155 int instance; 156 int error = 0; 157 158 instance = MINOR_TO_INST(getminor(*devp)); 159 160 if (instance < 0) { 161 return (ENXIO); 162 } 163 164 unitp = (struct pcf8591_unit *) 165 ddi_get_soft_state(pcf8591soft_statep, instance); 166 167 if (unitp == NULL) { 168 return (ENXIO); 169 } 170 171 if (otyp != OTYP_CHR) { 172 return (EINVAL); 173 } 174 175 mutex_enter(&unitp->pcf8591_mutex); 176 177 if (flags & FEXCL) { 178 if (unitp->pcf8591_oflag != 0) { 179 error = EBUSY; 180 } else { 181 unitp->pcf8591_oflag = FEXCL; 182 } 183 } else { 184 if (unitp->pcf8591_oflag == FEXCL) { 185 error = EBUSY; 186 } else { 187 unitp->pcf8591_oflag = FOPEN; 188 } 189 } 190 191 mutex_exit(&unitp->pcf8591_mutex); 192 193 return (error); 194 } 195 196 static int 197 pcf8591_close(dev_t dev, int flags, int otyp, cred_t *credp) 198 { 199 _NOTE(ARGUNUSED(flags, otyp, credp)) 200 201 struct pcf8591_unit *unitp; 202 int instance; 203 204 instance = MINOR_TO_INST(getminor(dev)); 205 206 if (instance < 0) { 207 return (ENXIO); 208 } 209 210 unitp = (struct pcf8591_unit *) 211 ddi_get_soft_state(pcf8591soft_statep, instance); 212 213 if (unitp == NULL) { 214 return (ENXIO); 215 } 216 217 mutex_enter(&unitp->pcf8591_mutex); 218 219 unitp->pcf8591_oflag = 0; 220 221 mutex_exit(&unitp->pcf8591_mutex); 222 return (DDI_SUCCESS); 223 } 224 225 static int 226 pcf8591_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 227 cred_t *credp, int *rvalp) 228 { 229 _NOTE(ARGUNUSED(credp, rvalp)) 230 231 struct pcf8591_unit *unitp; 232 int err = 0; 233 i2c_transfer_t *i2c_tran_pointer; 234 int port = MINOR_TO_PORT(getminor(dev)); 235 int instance = MINOR_TO_INST(getminor(dev)); 236 int autoincr = 0; 237 uchar_t control, reg; 238 int32_t value; 239 uint8_t uvalue; 240 241 if (arg == NULL) { 242 D2CMN_ERR((CE_WARN, "PCF8591: ioctl: arg passed in to ioctl " 243 "= NULL\n")); 244 err = EINVAL; 245 return (err); 246 } 247 unitp = (struct pcf8591_unit *) 248 ddi_get_soft_state(pcf8591soft_statep, instance); 249 250 if (unitp == NULL) { 251 cmn_err(CE_WARN, "PCF8591: ioctl: unitp not filled\n"); 252 return (ENOMEM); 253 } 254 255 mutex_enter(&unitp->pcf8591_mutex); 256 257 D1CMN_ERR((CE_NOTE, "%s: ioctl: port = %d instance = %d\n", 258 unitp->pcf8591_name, port, instance)); 259 260 switch (cmd) { 261 case I2C_GET_INPUT: 262 (void) i2c_transfer_alloc(unitp->pcf8591_hdl, &i2c_tran_pointer, 263 1, 2, I2C_SLEEP); 264 if (i2c_tran_pointer == NULL) { 265 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_INPUT " 266 "i2c_tran_pointer not allocated\n", 267 unitp->pcf8591_name)); 268 err = ENOMEM; 269 break; 270 } 271 reg = (uchar_t)port; 272 if ((reg == 0x02) && (ipmode == PCF8591_2DIFF)) { 273 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_INPUT " 274 "cannot use port 2 when ipmode is " 275 "0x03\n", unitp->pcf8591_name)); 276 err = EIO; 277 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer); 278 break; 279 } 280 281 if ((reg == 0x03) && (ipmode != PCF8591_4SINGLE)) { 282 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_INPUT " 283 "cannot use port 3 when ipmode is not " 284 "0x00\n", unitp->pcf8591_name)); 285 err = EIO; 286 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer); 287 break; 288 } 289 control = ((0 << PCF8591_ANALOG_OUTPUT_SHIFT) | 290 (ipmode << PCF8591_ANALOG_INPUT_SHIFT) | 291 (autoincr << PCF8591_AUTOINCR_SHIFT) | reg); 292 293 i2c_tran_pointer->i2c_flags = I2C_WR_RD; 294 i2c_tran_pointer->i2c_wbuf[0] = control; 295 296 err = i2c_transfer(unitp->pcf8591_hdl, i2c_tran_pointer); 297 if (err) { 298 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_INPUT" 299 " i2c_transfer routine\n", 300 unitp->pcf8591_name)); 301 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer); 302 break; 303 } 304 i2c_tran_pointer->i2c_rbuf[0] = i2c_tran_pointer->i2c_rbuf[1]; 305 value = i2c_tran_pointer->i2c_rbuf[0]; 306 D1CMN_ERR((CE_NOTE, "%s: Back from transfer result is %x\n", 307 unitp->pcf8591_name, value)); 308 if (ddi_copyout((caddr_t)&value, 309 (caddr_t)arg, 310 sizeof (int32_t), mode) != DDI_SUCCESS) { 311 D2CMN_ERR((CE_WARN, "%s: Failed I2C_GET_INPUT" 312 " ddi_copyout routine\n", 313 unitp->pcf8591_name)); 314 err = EFAULT; 315 } 316 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer); 317 break; 318 319 case I2C_SET_OUTPUT: 320 reg = (uchar_t)port; 321 if (ipmode != PCF8591_4SINGLE) { 322 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_SET_OUTPUT " 323 "cannot set output when ipmode is not " 324 "0x00\n", unitp->pcf8591_name)); 325 err = EIO; 326 break; 327 } 328 329 (void) i2c_transfer_alloc(unitp->pcf8591_hdl, &i2c_tran_pointer, 330 2, 0, I2C_SLEEP); 331 if (i2c_tran_pointer == NULL) { 332 D2CMN_ERR((CE_WARN, "%s: Failed in " 333 "I2C_SET_OUTPUT " 334 "i2c_tran_pointer not allocated\n", 335 unitp->pcf8591_name)); 336 err = ENOMEM; 337 break; 338 } 339 if (ddi_copyin((caddr_t)arg, (caddr_t)&value, 340 sizeof (int32_t), mode) != DDI_SUCCESS) { 341 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_SET_OUTPUT" 342 " ddi_copyout routine\n", 343 unitp->pcf8591_name)); 344 err = EFAULT; 345 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer); 346 break; 347 } 348 control = ((1 << PCF8591_ANALOG_OUTPUT_SHIFT) | 349 (0 << PCF8591_ANALOG_INPUT_SHIFT) | 350 (autoincr << PCF8591_AUTOINCR_SHIFT) | reg); 351 352 i2c_tran_pointer->i2c_flags = I2C_WR; 353 i2c_tran_pointer->i2c_wbuf[0] = control; 354 i2c_tran_pointer->i2c_wbuf[1] = (uchar_t)value; 355 356 err = i2c_transfer(unitp->pcf8591_hdl, i2c_tran_pointer); 357 if (!err) { 358 current_value = value; 359 current_set_flag = 1; 360 } 361 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer); 362 break; 363 364 case I2C_GET_OUTPUT: 365 if (current_set_flag == 0) { 366 err = EIO; 367 break; 368 } else { 369 if (ddi_copyout((caddr_t)current_value, (caddr_t)arg, 370 sizeof (int32_t), mode) != DDI_SUCCESS) { 371 D2CMN_ERR((CE_WARN, "%s: Failed in " 372 "I2C_GET_OUTPUT ddi_copyout routine\n", 373 unitp->pcf8591_name)); 374 err = EFAULT; 375 break; 376 } 377 } 378 break; 379 380 case PCF8591_SET_IPMODE: 381 if (ddi_copyin((caddr_t)arg, (caddr_t)&uvalue, 382 sizeof (uint_t), mode) != DDI_SUCCESS) { 383 D2CMN_ERR((CE_WARN, "%s: Failed in PCF8591_SET_IPMODE" 384 " ddi_copyout routine\n", 385 unitp->pcf8591_name)); 386 err = EFAULT; 387 break; 388 } 389 390 if (uvalue > 0x03) { 391 D2CMN_ERR((CE_WARN, "%s: Failed in PCF8591_SET_IPMODE" 392 " value is not a valid mode\n", 393 unitp->pcf8591_name)); 394 err = EIO; 395 break; 396 } 397 398 ipmode = uvalue; 399 break; 400 401 default: 402 D2CMN_ERR((CE_WARN, "%s: Invalid IOCTL cmd: %x\n", 403 unitp->pcf8591_name, cmd)); 404 err = EINVAL; 405 } 406 407 mutex_exit(&unitp->pcf8591_mutex); 408 return (err); 409 410 } 411 412 /* ARGSUSED */ 413 static int 414 pcf8591_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 415 { 416 dev_t dev; 417 int instance; 418 419 if (infocmd == DDI_INFO_DEVT2INSTANCE) { 420 dev = (dev_t)arg; 421 instance = MINOR_TO_INST(getminor(dev)); 422 *result = (void *)(uintptr_t)instance; 423 return (DDI_SUCCESS); 424 } 425 return (DDI_FAILURE); 426 } 427 428 static int 429 pcf8591_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 430 { 431 switch (cmd) { 432 case DDI_ATTACH: 433 return (pcf8591_do_attach(dip)); 434 case DDI_RESUME: 435 return (pcf8591_do_resume()); 436 default: 437 return (DDI_FAILURE); 438 } 439 } 440 441 static int 442 pcf8591_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 443 { 444 switch (cmd) { 445 case DDI_DETACH: 446 return (pcf8591_do_detach(dip)); 447 case DDI_SUSPEND: 448 return (pcf8591_do_suspend()); 449 default: 450 return (DDI_FAILURE); 451 } 452 } 453 454 static int 455 pcf8591_do_attach(dev_info_t *dip) 456 { 457 struct pcf8591_unit *unitp; 458 int instance; 459 char name[MAXNAMELEN]; 460 minor_t minor_number; 461 int i; 462 463 instance = ddi_get_instance(dip); 464 465 if (ddi_soft_state_zalloc(pcf8591soft_statep, instance) != 0) { 466 cmn_err(CE_WARN, "%s%d: failed to zalloc softstate\n", 467 ddi_get_name(dip), instance); 468 return (DDI_FAILURE); 469 } 470 471 unitp = ddi_get_soft_state(pcf8591soft_statep, instance); 472 473 if (unitp == NULL) { 474 cmn_err(CE_WARN, "%s%d: unitp not filled\n", 475 ddi_get_name(dip), instance); 476 return (ENOMEM); 477 } 478 479 (void) snprintf(unitp->pcf8591_name, sizeof (unitp->pcf8591_name), 480 "%sd", ddi_node_name(dip), instance); 481 482 for (i = 0; i < 4; i++) { 483 (void) sprintf(name, "port_%d", i); 484 485 minor_number = INST_TO_MINOR(instance) | 486 PORT_TO_MINOR(I2C_PORT(i)); 487 488 if (ddi_create_minor_node(dip, name, S_IFCHR, minor_number, 489 "ddi_i2c:adio", NULL) == DDI_FAILURE) { 490 cmn_err(CE_WARN, "%s ddi_create_minor_node failed for " 491 "%s\n", unitp->pcf8591_name, name); 492 ddi_soft_state_free(pcf8591soft_statep, instance); 493 494 return (DDI_FAILURE); 495 } 496 } 497 498 if (i2c_client_register(dip, &unitp->pcf8591_hdl) != I2C_SUCCESS) { 499 ddi_remove_minor_node(dip, NULL); 500 ddi_soft_state_free(pcf8591soft_statep, instance); 501 502 return (DDI_FAILURE); 503 } 504 505 mutex_init(&unitp->pcf8591_mutex, NULL, MUTEX_DRIVER, NULL); 506 507 return (DDI_SUCCESS); 508 } 509 510 static int 511 pcf8591_do_resume() 512 { 513 int ret = DDI_SUCCESS; 514 515 return (ret); 516 } 517 518 static int 519 pcf8591_do_suspend() 520 { 521 int ret = DDI_SUCCESS; 522 523 return (ret); 524 } 525 526 static int 527 pcf8591_do_detach(dev_info_t *dip) 528 { 529 struct pcf8591_unit *unitp; 530 int instance; 531 532 instance = ddi_get_instance(dip); 533 534 unitp = ddi_get_soft_state(pcf8591soft_statep, instance); 535 536 if (unitp == NULL) { 537 cmn_err(CE_WARN, "%s%d: unitp not filled\n", 538 ddi_get_name(dip), instance); 539 return (ENOMEM); 540 } 541 542 i2c_client_unregister(unitp->pcf8591_hdl); 543 544 ddi_remove_minor_node(dip, NULL); 545 546 mutex_destroy(&unitp->pcf8591_mutex); 547 548 ddi_soft_state_free(pcf8591soft_statep, instance); 549 550 return (DDI_SUCCESS); 551 } 552