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