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/modctl.h> 31 #include <sys/open.h> 32 #include <sys/types.h> 33 #include <sys/kmem.h> 34 #include <sys/ddi.h> 35 #include <sys/sunddi.h> 36 #include <sys/conf.h> 37 #include <sys/file.h> 38 #include <sys/note.h> 39 #include <sys/i2c/misc/i2c_svc.h> 40 #include <sys/i2c/clients/seeprom_impl.h> 41 42 /* 43 * cb ops 44 */ 45 static int seeprom_open(dev_t *, int, int, cred_t *); 46 static int seeprom_close(dev_t, int, int, cred_t *); 47 static int seeprom_read(dev_t, struct uio *, cred_t *); 48 static int seeprom_write(dev_t, struct uio *, cred_t *); 49 static int seeprom_io(dev_t, struct uio *, int); 50 51 /* 52 * dev ops 53 */ 54 static int seeprom_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 55 static int seeprom_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 56 static int seeprom_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 57 58 static struct cb_ops seeprom_cbops = { 59 seeprom_open, /* open */ 60 seeprom_close, /* close */ 61 nodev, /* strategy */ 62 nodev, /* print */ 63 nodev, /* dump */ 64 seeprom_read, /* read */ 65 seeprom_write, /* write */ 66 nodev, /* ioctl */ 67 nodev, /* devmap */ 68 nodev, /* mmap */ 69 nodev, /* segmap */ 70 nochpoll, /* poll */ 71 ddi_prop_op, /* cb_prop_op */ 72 NULL, /* streamtab */ 73 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 74 CB_REV, /* rev */ 75 nodev, /* int (*cb_aread)() */ 76 nodev /* int (*cb_awrite)() */ 77 }; 78 79 static struct dev_ops seeprom_ops = { 80 DEVO_REV, 81 0, 82 seeprom_info, 83 nulldev, 84 nulldev, 85 seeprom_attach, 86 seeprom_detach, 87 nodev, 88 &seeprom_cbops, 89 NULL, 90 nulldev 91 }; 92 93 static struct modldrv seeprom_modldrv = { 94 &mod_driverops, /* type of module - driver */ 95 "I2C serial EEPROM device driver v%I%", 96 &seeprom_ops, 97 }; 98 99 static struct modlinkage seeprom_modlinkage = { 100 MODREV_1, 101 &seeprom_modldrv, 102 0 103 }; 104 105 /* 106 * globals 107 */ 108 109 static void *seepromsoft_statep; 110 111 int 112 _init(void) 113 { 114 int error; 115 116 if ((error = ddi_soft_state_init(&seepromsoft_statep, 117 sizeof (struct seepromunit), 1)) != 0) 118 return (error); 119 120 if ((error = mod_install(&seeprom_modlinkage)) != 0) { 121 ddi_soft_state_fini(&seepromsoft_statep); 122 return (error); 123 } 124 125 return (error); 126 } 127 128 int 129 _fini(void) 130 { 131 int error; 132 133 error = mod_remove(&seeprom_modlinkage); 134 if (error == 0) { 135 ddi_soft_state_fini(&seepromsoft_statep); 136 } 137 138 return (error); 139 } 140 141 int 142 _info(struct modinfo *modinfop) 143 { 144 return (mod_info(&seeprom_modlinkage, modinfop)); 145 } 146 147 static int 148 seeprom_do_attach(dev_info_t *dip) 149 { 150 struct seepromunit *unitp; 151 int instance; 152 dev_t dev; 153 154 instance = ddi_get_instance(dip); 155 156 if (ddi_soft_state_zalloc(seepromsoft_statep, instance) != 0) { 157 cmn_err(CE_WARN, "%s_%d: failed to zalloc softstate", 158 ddi_node_name(dip), instance); 159 160 return (DDI_FAILURE); 161 } 162 163 unitp = ddi_get_soft_state(seepromsoft_statep, instance); 164 165 unitp->seeprom_dip = dip; 166 167 (void) snprintf(unitp->seeprom_name, sizeof (unitp->seeprom_name), 168 "%s%d", ddi_driver_name(dip), instance); 169 170 if (ddi_create_minor_node(dip, ddi_node_name(dip), S_IFCHR, 171 instance, SEEPROM_NODE_TYPE, NULL) == DDI_FAILURE) { 172 cmn_err(CE_WARN, "%s ddi_create_minor_node failed for '%s'", 173 unitp->seeprom_name, ddi_node_name(dip)); 174 ddi_soft_state_free(seepromsoft_statep, instance); 175 176 return (DDI_FAILURE); 177 } 178 179 if (i2c_client_register(dip, &unitp->seeprom_hdl) != I2C_SUCCESS) { 180 cmn_err(CE_WARN, "i2c_client_register failed\n"); 181 ddi_remove_minor_node(dip, NULL); 182 ddi_soft_state_free(seepromsoft_statep, instance); 183 184 return (DDI_FAILURE); 185 } 186 187 if (strcmp(ddi_binding_name(dip), "i2c-at34c02") == 0) { 188 unitp->seeprom_addrsize = AT34C02_ADDRSIZE; 189 unitp->seeprom_memsize = AT34C02_MEMSIZE; 190 unitp->seeprom_pagesize = AT34C02_PAGESIZE; 191 unitp->seeprom_pagemask = AT34C02_PAGEMASK; 192 } else { 193 /* 194 * Default is i2c-at24c64 195 */ 196 unitp->seeprom_addrsize = AT24C64_ADDRSIZE; 197 unitp->seeprom_memsize = AT24C64_MEMSIZE; 198 unitp->seeprom_pagesize = AT24C64_PAGESIZE; 199 unitp->seeprom_pagemask = AT24C64_PAGEMASK; 200 } 201 dev = makedevice(DDI_MAJOR_T_UNKNOWN, instance); 202 203 (void) ddi_prop_create(dev, dip, DDI_PROP_CANSLEEP, "size", 204 (caddr_t)&unitp->seeprom_memsize, sizeof (unitp->seeprom_memsize)); 205 206 mutex_init(&unitp->seeprom_mutex, NULL, MUTEX_DRIVER, NULL); 207 cv_init(&unitp->seeprom_cv, NULL, CV_DRIVER, NULL); 208 209 return (DDI_SUCCESS); 210 } 211 212 static int 213 seeprom_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 214 { 215 switch (cmd) { 216 case DDI_ATTACH: 217 218 return (seeprom_do_attach(dip)); 219 case DDI_RESUME: 220 /* 221 * No state to restore. 222 */ 223 return (DDI_SUCCESS); 224 default: 225 226 return (DDI_FAILURE); 227 } 228 } 229 230 static int 231 seeprom_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 232 { 233 _NOTE(ARGUNUSED(dip)) 234 struct seepromunit *unitp; 235 236 switch (infocmd) { 237 case DDI_INFO_DEVT2DEVINFO: 238 unitp = ddi_get_soft_state(seepromsoft_statep, 239 getminor((dev_t)arg)); 240 if (unitp == NULL) { 241 242 return (DDI_FAILURE); 243 } 244 *result = (void *)unitp->seeprom_dip; 245 246 return (DDI_SUCCESS); 247 case DDI_INFO_DEVT2INSTANCE: 248 *result = (void *)getminor((dev_t)arg); 249 250 return (DDI_SUCCESS); 251 default: 252 return (DDI_FAILURE); 253 } 254 255 } 256 257 static int 258 seeprom_do_detach(dev_info_t *dip) 259 { 260 struct seepromunit *unitp; 261 int instance; 262 263 instance = ddi_get_instance(dip); 264 unitp = ddi_get_soft_state(seepromsoft_statep, instance); 265 i2c_client_unregister(unitp->seeprom_hdl); 266 ddi_remove_minor_node(dip, NULL); 267 mutex_destroy(&unitp->seeprom_mutex); 268 cv_destroy(&unitp->seeprom_cv); 269 270 ddi_soft_state_free(seepromsoft_statep, instance); 271 272 return (DDI_SUCCESS); 273 } 274 275 static int 276 seeprom_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 277 { 278 switch (cmd) { 279 case DDI_DETACH: 280 281 return (seeprom_do_detach(dip)); 282 case DDI_SUSPEND: 283 /* 284 * No state to save. IO will be blocked by nexus. 285 */ 286 return (DDI_SUCCESS); 287 default: 288 289 return (DDI_FAILURE); 290 } 291 } 292 293 static int 294 seeprom_open(dev_t *devp, int flags, int otyp, cred_t *credp) 295 { 296 _NOTE(ARGUNUSED(credp)) 297 struct seepromunit *unitp; 298 int instance; 299 int err = 0; 300 301 if (otyp != OTYP_CHR) { 302 303 return (EINVAL); 304 } 305 306 instance = getminor(*devp); 307 308 unitp = (struct seepromunit *) 309 ddi_get_soft_state(seepromsoft_statep, instance); 310 311 if (unitp == NULL) { 312 313 return (ENXIO); 314 } 315 316 mutex_enter(&unitp->seeprom_mutex); 317 318 if (flags & FEXCL) { 319 if (unitp->seeprom_oflag != 0) { 320 err = EBUSY; 321 } else { 322 unitp->seeprom_oflag = FEXCL; 323 } 324 } else { 325 if (unitp->seeprom_oflag == FEXCL) { 326 err = EBUSY; 327 } else { 328 unitp->seeprom_oflag = FOPEN; 329 } 330 } 331 332 mutex_exit(&unitp->seeprom_mutex); 333 334 return (err); 335 } 336 337 static int 338 seeprom_close(dev_t dev, int flags, int otyp, cred_t *credp) 339 { 340 _NOTE(ARGUNUSED(flags, otyp, credp)) 341 struct seepromunit *unitp; 342 int instance; 343 344 instance = getminor(dev); 345 346 unitp = (struct seepromunit *) 347 ddi_get_soft_state(seepromsoft_statep, instance); 348 349 if (unitp == NULL) { 350 351 return (ENXIO); 352 } 353 354 mutex_enter(&unitp->seeprom_mutex); 355 356 unitp->seeprom_oflag = 0; 357 358 mutex_exit(&unitp->seeprom_mutex); 359 360 return (DDI_SUCCESS); 361 } 362 363 static int 364 seeprom_read(dev_t dev, struct uio *uiop, cred_t *cred_p) 365 { 366 _NOTE(ARGUNUSED(cred_p)) 367 return (seeprom_io(dev, uiop, B_READ)); 368 } 369 370 static int 371 seeprom_write(dev_t dev, struct uio *uiop, cred_t *cred_p) 372 { 373 _NOTE(ARGUNUSED(cred_p)) 374 return (seeprom_io(dev, uiop, B_WRITE)); 375 } 376 377 static int 378 seeprom_io(dev_t dev, struct uio *uiop, int rw) 379 { 380 struct seepromunit *unitp; 381 int instance = getminor(dev); 382 int seeprom_addr; 383 int bytes_to_rw; 384 int err = 0; 385 int current_xfer_len; 386 int actual_data_xfer; 387 i2c_transfer_t *i2ctp = NULL; 388 389 unitp = (struct seepromunit *) 390 ddi_get_soft_state(seepromsoft_statep, instance); 391 392 393 if (unitp == NULL) { 394 return (ENXIO); 395 } 396 397 if (uiop->uio_offset >= unitp->seeprom_memsize) { 398 /* 399 * Exceeded seeprom size. 400 */ 401 402 return (ENXIO); 403 } 404 405 seeprom_addr = uiop->uio_offset; 406 407 if (uiop->uio_resid == 0) { 408 return (0); 409 } 410 411 bytes_to_rw = min(uiop->uio_resid, 412 unitp->seeprom_memsize - uiop->uio_offset); 413 /* 414 * Serialize access here to prevent a transaction starting 415 * until after 20 ms delay if last operation was a write. 416 */ 417 mutex_enter(&unitp->seeprom_mutex); 418 while ((unitp->seeprom_flags & SEEPROM_BUSY) == SEEPROM_BUSY) { 419 if (cv_wait_sig(&unitp->seeprom_cv, 420 &unitp->seeprom_mutex) <= 0) { 421 mutex_exit(&unitp->seeprom_mutex); 422 423 return (EINTR); 424 } 425 } 426 unitp->seeprom_flags |= SEEPROM_BUSY; 427 mutex_exit(&unitp->seeprom_mutex); 428 429 while ((bytes_to_rw != 0) && (err == 0)) { 430 current_xfer_len = min(bytes_to_rw, unitp->seeprom_pagesize - 431 (seeprom_addr & unitp->seeprom_pagemask)); 432 433 if (rw == B_WRITE) { 434 if (i2ctp == NULL) { 435 (void) i2c_transfer_alloc(unitp->seeprom_hdl, 436 &i2ctp, 437 unitp->seeprom_addrsize + current_xfer_len, 438 0, 439 I2C_SLEEP); 440 441 if ((err = uiomove(&i2ctp->i2c_wbuf[ 442 unitp->seeprom_addrsize], 443 current_xfer_len, UIO_WRITE, uiop)) != 0) { 444 i2c_transfer_free(unitp->seeprom_hdl, 445 i2ctp); 446 break; 447 } 448 i2ctp->i2c_version = I2C_XFER_REV; 449 i2ctp->i2c_flags = I2C_WR; 450 } else { 451 452 /* 453 * not all bytes were sent in previous attempt. 454 * Adjust the write pointer to the unsent data. 455 */ 456 /*LINTED*/ 457 i2ctp->i2c_wbuf += actual_data_xfer; 458 /*LINTED*/ 459 i2ctp->i2c_wlen -= actual_data_xfer; 460 } 461 462 if (unitp->seeprom_addrsize == 2) { 463 i2ctp->i2c_wbuf[0] = (seeprom_addr >> 8); 464 i2ctp->i2c_wbuf[1] = (uchar_t)seeprom_addr; 465 } else { 466 i2ctp->i2c_wbuf[0] = (uchar_t)seeprom_addr; 467 } 468 469 if ((err = i2c_transfer(unitp->seeprom_hdl, i2ctp)) != 470 I2C_SUCCESS) { 471 i2c_transfer_free(unitp->seeprom_hdl, i2ctp); 472 break; 473 } 474 475 actual_data_xfer = i2ctp->i2c_wlen - 476 i2ctp->i2c_w_resid - unitp->seeprom_addrsize; 477 478 if (i2ctp->i2c_w_resid == 0) { 479 i2c_transfer_free(unitp->seeprom_hdl, i2ctp); 480 i2ctp = NULL; 481 } 482 /* 483 * 20 ms(20000 Microsec) delay is required before 484 * issuing another transaction. This enforces that 485 * wait. 486 */ 487 delay(drv_usectohz(20000)); 488 } else { 489 /* 490 * SEEPROM read. First write out the address to read. 491 */ 492 (void) i2c_transfer_alloc(unitp->seeprom_hdl, &i2ctp, 493 unitp->seeprom_addrsize, current_xfer_len, 494 I2C_SLEEP); 495 i2ctp->i2c_version = I2C_XFER_REV; 496 497 if (unitp->seeprom_addrsize == 2) { 498 i2ctp->i2c_wbuf[0] = (seeprom_addr >> 8); 499 i2ctp->i2c_wbuf[1] = (uchar_t)seeprom_addr; 500 } else { 501 i2ctp->i2c_wbuf[0] = (uchar_t)seeprom_addr; 502 } 503 504 i2ctp->i2c_flags = I2C_WR_RD; 505 506 if ((err = i2c_transfer(unitp->seeprom_hdl, i2ctp)) != 507 I2C_SUCCESS) { 508 i2c_transfer_free(unitp->seeprom_hdl, i2ctp); 509 break; 510 } 511 512 actual_data_xfer = i2ctp->i2c_rlen - i2ctp->i2c_r_resid; 513 514 err = uiomove(i2ctp->i2c_rbuf, actual_data_xfer, 515 UIO_READ, uiop); 516 i2c_transfer_free(unitp->seeprom_hdl, i2ctp); 517 } 518 519 bytes_to_rw -= actual_data_xfer; 520 seeprom_addr += actual_data_xfer; 521 } 522 523 mutex_enter(&unitp->seeprom_mutex); 524 unitp->seeprom_flags = 0; 525 cv_signal(&unitp->seeprom_cv); 526 mutex_exit(&unitp->seeprom_mutex); 527 528 return (err); 529 } 530