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 /* 27 * Memory target support for SDcard. 28 */ 29 30 #include <sys/types.h> 31 #include <sys/types.h> 32 #include <sys/conf.h> 33 #include <sys/scsi/adapters/blk2scsa.h> 34 #include <sys/ddi.h> 35 #include <sys/sunddi.h> 36 #include <sys/sdcard/sda.h> 37 #include <sys/sdcard/sda_impl.h> 38 39 static int sda_mem_attach(dev_info_t *, ddi_attach_cmd_t); 40 static int sda_mem_detach(dev_info_t *, ddi_detach_cmd_t); 41 static b2s_err_t sda_mem_b2s_errno(sda_err_t); 42 static boolean_t sda_mem_b2s_request(void *, b2s_request_t *); 43 static boolean_t sda_mem_b2s_rw(sda_slot_t *, b2s_request_t *); 44 static void sda_mem_b2s_done(sda_cmd_t *); 45 static void sda_mem_getstring(uint32_t *, char *, int, int); 46 static int sda_mem_parse_cid_csd(sda_slot_t *, dev_info_t *); 47 static int sda_mem_cmd(sda_slot_t *, uint8_t, uint32_t, uint8_t, uint32_t *); 48 49 50 /* 51 * To minimize complexity and reduce layering, we implement almost the 52 * entire memory card driver (sdcard) here. The memory card still 53 * needs to be a separate driver though, due to the requirement to 54 * have both SCSI HBA bus ops and SD bus ops. 55 */ 56 57 /* 58 * SCSA layer supplies a cb_ops, but we don't want it, because we 59 * don't want to expose a SCSI attachment point. (Our parent handles 60 * the attachment point, the SCSI one would be confusing.) We have to 61 * supply a stubbed out one, to prevent SCSA from trying to create minor 62 * nodes on our behalf. 63 * 64 * Perhaps at some future point we might want to expose a separate set 65 * of ioctls for these nodes, but for now we rely on our parent to do 66 * all that work. 67 */ 68 static struct cb_ops sda_mem_ops = { 69 nodev, /* cb_open */ 70 nodev, /* cb_close */ 71 nodev, /* cb_strategy */ 72 nodev, /* cb_print */ 73 nodev, /* cb_dump */ 74 nodev, /* cb_read */ 75 nodev, /* cb_write */ 76 nodev, /* cb_ioctl */ 77 nodev, /* cb_devmap */ 78 nodev, /* cb_mmap */ 79 nodev, /* cb_segmap */ 80 nochpoll, /* cb_chpoll */ 81 ddi_prop_op, /* cb_prop_op */ 82 NULL, /* cb_stream */ 83 D_MP /* cb_flag */ 84 }; 85 86 /* 87 * Here are the public functions. 88 */ 89 void 90 sda_mem_init(struct modlinkage *modlp) 91 { 92 struct dev_ops *devo; 93 94 devo = ((struct modldrv *)(modlp->ml_linkage[0]))->drv_dev_ops; 95 devo->devo_attach = sda_mem_attach; 96 devo->devo_detach = sda_mem_detach; 97 98 devo->devo_cb_ops = &sda_mem_ops; 99 100 /* it turns out that this can't ever really fail */ 101 (void) b2s_mod_init(modlp); 102 } 103 104 void 105 sda_mem_fini(struct modlinkage *modlp) 106 { 107 b2s_mod_fini(modlp); 108 } 109 110 /* 111 * Everything beyond this is private. 112 */ 113 114 int 115 sda_mem_cmd(sda_slot_t *slot, uint8_t cmd, uint32_t arg, uint8_t rtype, 116 uint32_t *resp) 117 { 118 sda_cmd_t *cmdp; 119 int errno; 120 121 cmdp = sda_cmd_alloc(slot, cmd, arg, rtype, NULL, KM_SLEEP); 122 if (cmdp == NULL) { 123 return (ENOMEM); 124 } 125 errno = sda_cmd_exec(slot, cmdp, resp); 126 sda_cmd_free(cmdp); 127 128 return (errno); 129 } 130 131 boolean_t 132 sda_mem_b2s_rw(sda_slot_t *slot, b2s_request_t *reqp) 133 { 134 sda_cmd_t *cmdp; 135 uint64_t nblks; 136 uint64_t blkno; 137 uint16_t rblen; 138 int rv; 139 uint8_t index; 140 uint16_t flags; 141 142 blkno = reqp->br_lba; 143 nblks = reqp->br_nblks; 144 145 switch (reqp->br_cmd) { 146 case B2S_CMD_READ: 147 if (nblks > 1) { 148 index = CMD_READ_MULTI; 149 flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_READ | 150 SDA_CMDF_AUTO_CMD12; 151 } else { 152 index = CMD_READ_SINGLE; 153 flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_READ; 154 } 155 break; 156 case B2S_CMD_WRITE: 157 if (nblks > 1) { 158 index = CMD_WRITE_MULTI; 159 flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_WRITE | 160 SDA_CMDF_AUTO_CMD12; 161 } else { 162 index = CMD_WRITE_SINGLE; 163 flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_WRITE; 164 } 165 break; 166 default: 167 ASSERT(0); 168 break; 169 } 170 171 cmdp = sda_cmd_alloc(slot, index, blkno << slot->s_bshift, 172 R1, reqp, KM_NOSLEEP); 173 if (cmdp == NULL) { 174 b2s_request_done(reqp, B2S_ENOMEM, 0); 175 return (B_TRUE); 176 } 177 178 if (slot->s_host->h_dma != NULL) { 179 b2s_request_dma(reqp, &cmdp->sc_ndmac, &cmdp->sc_dmacs); 180 cmdp->sc_kvaddr = 0; 181 } 182 if ((slot->s_caps & SLOT_CAP_NOPIO) == 0) { 183 size_t maplen; 184 b2s_request_mapin(reqp, &cmdp->sc_kvaddr, &maplen); 185 cmdp->sc_ndmac = 0; 186 } 187 188 if (nblks == 0) { 189 /* 190 * This is not strictly a failure, but no work to do. 191 * We have to do it late here because we don't want to 192 * by pass the above media readiness checks. 193 */ 194 rv = B2S_EOK; 195 goto failed; 196 } 197 if (nblks > 0xffff) { 198 rv = B2S_EINVAL; 199 goto failed; 200 } 201 202 rblen = slot->s_blksz; 203 204 if ((blkno + nblks) > slot->s_nblks) { 205 rv = B2S_EBLKADDR; 206 goto failed; 207 } 208 209 cmdp->sc_rtype = R1; 210 cmdp->sc_blksz = rblen; 211 cmdp->sc_nblks = (uint16_t)nblks; 212 cmdp->sc_index = index; 213 cmdp->sc_flags = flags; 214 215 sda_cmd_submit(slot, cmdp, sda_mem_b2s_done); 216 return (B_TRUE); 217 218 failed: 219 sda_cmd_free(cmdp); 220 b2s_request_done(reqp, rv, 0); 221 return (B_TRUE); 222 } 223 224 boolean_t 225 sda_mem_b2s_format(sda_slot_t *slot, b2s_request_t *reqp) 226 { 227 sda_cmd_t *cmdp; 228 int rv; 229 230 231 rv = sda_mem_cmd(slot, CMD_ERASE_START, 0, R1, NULL); 232 if (rv != 0) { 233 b2s_request_done(reqp, sda_mem_b2s_errno(rv), 0); 234 return (B_TRUE); 235 } 236 rv = sda_mem_cmd(slot, CMD_ERASE_END, slot->s_nblks - 1, R1, NULL); 237 if (rv != 0) { 238 b2s_request_done(reqp, sda_mem_b2s_errno(rv), 0); 239 return (B_TRUE); 240 } 241 242 cmdp = sda_cmd_alloc(slot, CMD_ERASE, 0, R1b, reqp, KM_NOSLEEP); 243 if (cmdp == NULL) { 244 b2s_request_done(reqp, B2S_ENOMEM, 0); 245 return (B_TRUE); 246 } 247 cmdp->sc_flags = SDA_CMDF_DAT | SDA_CMDF_MEM; 248 249 sda_cmd_submit(slot, cmdp, sda_mem_b2s_done); 250 return (B_TRUE); 251 } 252 253 b2s_err_t 254 sda_mem_b2s_errno(sda_err_t errno) 255 { 256 /* the hot path */ 257 if (errno == SDA_EOK) { 258 return (B2S_EOK); 259 } 260 261 switch (errno) { 262 case SDA_ENOMEM: 263 return (B2S_ENOMEM); 264 case SDA_ETIME: 265 return (B2S_ETIMEDOUT); 266 case SDA_EWPROTECT: 267 return (B2S_EWPROTECT); 268 case SDA_ESUSPENDED: 269 case SDA_ENODEV: 270 return (B2S_ENOMEDIA); 271 case SDA_EFAULT: 272 case SDA_ECRC7: 273 case SDA_EPROTO: 274 return (B2S_EHARDWARE); 275 case SDA_ERESET: 276 return (B2S_ERESET); 277 case SDA_EIO: 278 case SDA_ERESID: 279 default: 280 return (B2S_EIO); 281 } 282 } 283 284 void 285 sda_mem_b2s_done(sda_cmd_t *cmdp) 286 { 287 b2s_request_t *reqp = sda_cmd_data(cmdp); 288 int errno = sda_cmd_errno(cmdp); 289 290 b2s_request_done(reqp, sda_mem_b2s_errno(errno), cmdp->sc_resid); 291 sda_cmd_free(cmdp); 292 } 293 294 boolean_t 295 sda_mem_b2s_request(void *arg, b2s_request_t *reqp) 296 { 297 sda_slot_t *slot = arg; 298 int rv; 299 300 switch (reqp->br_cmd) { 301 case B2S_CMD_WRITE: 302 if ((slot->s_flags & SLOTF_WRITABLE) == 0) { 303 rv = B2S_EWPROTECT; 304 } else { 305 return (sda_mem_b2s_rw(slot, reqp)); 306 } 307 break; 308 309 case B2S_CMD_READ: 310 return (sda_mem_b2s_rw(slot, reqp)); 311 312 case B2S_CMD_INQUIRY: 313 reqp->br_inquiry.inq_vendor = "OSOL"; 314 reqp->br_inquiry.inq_product = 315 slot->s_flags & SLOTF_MMC ? "MultiMediaCard" : 316 slot->s_flags & SLOTF_SDHC ? "SDHC Memory Card" : 317 "SD Memory Card"; 318 reqp->br_inquiry.inq_revision = ""; 319 reqp->br_inquiry.inq_serial = ""; 320 rv = B2S_EOK; 321 break; 322 323 case B2S_CMD_GETMEDIA: 324 if (!slot->s_ready) { 325 rv = B2S_ENODEV; 326 } else { 327 reqp->br_media.media_blksz = slot->s_blksz; 328 reqp->br_media.media_nblks = slot->s_nblks; 329 /* detect read-only cards */ 330 if (slot->s_flags & SLOTF_WRITABLE) { 331 reqp->br_media.media_flags = 0; 332 } else { 333 reqp->br_media.media_flags = 334 B2S_MEDIA_FLAG_READ_ONLY; 335 } 336 rv = B2S_EOK; 337 } 338 break; 339 340 case B2S_CMD_FORMAT: 341 return (sda_mem_b2s_format(slot, reqp)); 342 343 case B2S_CMD_ABORT: 344 sda_slot_mem_reset(slot, SDA_EABORT); 345 rv = B2S_EOK; 346 break; 347 348 case B2S_CMD_RESET: 349 sda_slot_mem_reset(slot, SDA_ERESET); 350 rv = B2S_EOK; 351 break; 352 353 case B2S_CMD_START: 354 case B2S_CMD_STOP: 355 case B2S_CMD_SYNC: 356 rv = B2S_EOK; 357 break; 358 359 case B2S_CMD_LOCK: 360 case B2S_CMD_UNLOCK: 361 default: 362 rv = B2S_ENOTSUP; 363 break; 364 } 365 366 b2s_request_done(reqp, rv, 0); 367 return (B_TRUE); 368 } 369 370 int 371 sda_mem_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 372 { 373 sda_slot_t *slot; 374 b2s_nexus_t *nexus; 375 b2s_nexus_info_t nexinfo; 376 b2s_leaf_info_t leafinfo; 377 378 switch (cmd) { 379 case DDI_ATTACH: 380 if ((slot = ddi_get_parent_data(dip)) == NULL) { 381 return (DDI_FAILURE); 382 } 383 384 if (sda_mem_parse_cid_csd(slot, dip) != DDI_SUCCESS) { 385 return (DDI_FAILURE); 386 } 387 388 nexinfo.nexus_version = B2S_VERSION_0; 389 nexinfo.nexus_private = slot; 390 nexinfo.nexus_dip = dip; 391 nexinfo.nexus_dma_attr = slot->s_host->h_dma; 392 nexinfo.nexus_request = sda_mem_b2s_request; 393 394 nexus = b2s_alloc_nexus(&nexinfo); 395 if (nexus == NULL) { 396 return (DDI_FAILURE); 397 } 398 399 leafinfo.leaf_target = 0; 400 leafinfo.leaf_lun = 0; 401 leafinfo.leaf_flags = 402 B2S_LEAF_REMOVABLE | B2S_LEAF_HOTPLUGGABLE; 403 leafinfo.leaf_unique_id = slot->s_uuid; 404 405 slot->s_leaf = b2s_attach_leaf(nexus, &leafinfo); 406 if (slot->s_leaf == NULL) { 407 b2s_free_nexus(nexus); 408 return (DDI_FAILURE); 409 } 410 411 slot->s_nexus = nexus; 412 if (b2s_attach_nexus(nexus) != DDI_SUCCESS) { 413 slot->s_nexus = NULL; 414 b2s_free_nexus(nexus); 415 return (DDI_FAILURE); 416 } 417 slot->s_nexus = nexus; 418 419 return (DDI_SUCCESS); 420 421 422 case DDI_RESUME: 423 return (DDI_SUCCESS); 424 425 default: 426 return (DDI_FAILURE); 427 } 428 } 429 430 int 431 sda_mem_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 432 { 433 sda_slot_t *slot; 434 b2s_nexus_t *nexus; 435 436 switch (cmd) { 437 case DDI_DETACH: 438 if ((slot = ddi_get_parent_data(dip)) == NULL) { 439 return (DDI_FAILURE); 440 } 441 if ((nexus = slot->s_nexus) == NULL) { 442 /* nothing to do */ 443 return (DDI_SUCCESS); 444 } 445 if (b2s_detach_nexus(nexus) != DDI_SUCCESS) { 446 return (DDI_FAILURE); 447 } 448 slot->s_nexus = NULL; 449 b2s_free_nexus(nexus); 450 return (DDI_SUCCESS); 451 452 case DDI_SUSPEND: 453 return (DDI_SUCCESS); 454 455 default: 456 return (DDI_FAILURE); 457 } 458 } 459 460 uint32_t 461 sda_mem_getbits(uint32_t *resp, int hibit, int len) 462 { 463 uint32_t val = 0; 464 uint32_t bit; 465 466 for (bit = hibit; len--; bit--) { 467 val <<= 1; 468 val |= ((resp[bit / 32]) >> (bit % 32)) & 1; 469 } 470 return (val); 471 } 472 473 void 474 sda_mem_getstring(uint32_t *resp, char *s, int hibit, int len) 475 { 476 while (len--) { 477 *s++ = sda_mem_getbits(resp, hibit, 8); 478 hibit -= 8; 479 } 480 *s = 0; 481 } 482 483 uint32_t 484 sda_mem_maxclk(sda_slot_t *slot) 485 { 486 static const uint32_t mult[16] = { 487 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 488 }; 489 490 static const uint32_t units[8] = { 491 10000, 100000, 1000000, 10000000, 0, 0, 0, 0, 492 }; 493 uint8_t ts; 494 495 ts = sda_mem_getbits(slot->s_rcsd, 103, 8); 496 497 return ((units[ts & 0x7]) * (mult[(ts >> 3) & 0xf])); 498 } 499 500 int 501 sda_mem_parse_cid_csd(sda_slot_t *slot, dev_info_t *dip) 502 { 503 uint32_t *rcid; 504 uint32_t *rcsd; 505 int csdver; 506 uint16_t rblen; 507 uint16_t bshift; 508 uint32_t cmult; 509 uint32_t csize; 510 char date[16]; 511 char *dtype; 512 513 rcid = slot->s_rcid; 514 rcsd = slot->s_rcsd; 515 516 csdver = sda_mem_getbits(rcsd, 127, 2); 517 518 if (slot->s_flags & SLOTF_SDMEM) { 519 switch (csdver) { 520 case 0: 521 csize = sda_mem_getbits(rcsd, 73, 12); 522 /* see comment above */ 523 rblen = (1 << sda_mem_getbits(rcsd, 83, 4)); 524 cmult = (4 << sda_mem_getbits(rcsd, 49, 3)); 525 bshift = 9; 526 break; 527 case 1: 528 rblen = 512; 529 csize = sda_mem_getbits(rcsd, 69, 22); 530 cmult = 1024; 531 bshift = 0; 532 break; 533 default: 534 sda_slot_err(slot, "Unknown SD CSD version (%d)", 535 csdver); 536 return (DDI_FAILURE); 537 } 538 539 dtype = slot->s_flags & SLOTF_SDHC ? "sdhc" : "sdcard"; 540 slot->s_mfg = sda_mem_getbits(rcid, 127, 8); 541 sda_mem_getstring(rcid, slot->s_oem, 119, 2); 542 sda_mem_getstring(rcid, slot->s_prod, 103, 5); 543 slot->s_majver = sda_mem_getbits(rcid, 63, 4); 544 slot->s_minver = sda_mem_getbits(rcid, 59, 4); 545 slot->s_serial = sda_mem_getbits(rcid, 55, 32); 546 slot->s_year = sda_mem_getbits(rcid, 19, 8) + 2000; 547 slot->s_month = sda_mem_getbits(rcid, 11, 4); 548 549 } else if (slot->s_flags & SLOTF_MMC) { 550 if ((csdver < 1) || (csdver > 2)) { 551 sda_slot_err(slot, "Unknown MMC CSD version (%d)", 552 csdver); 553 return (DDI_FAILURE); 554 } 555 556 dtype = "mmc"; 557 558 switch (sda_mem_getbits(rcsd, 125, 4)) { 559 case 0: /* MMC 1.0 - 1.2 */ 560 case 1: /* MMC 1.4 */ 561 slot->s_mfg = sda_mem_getbits(rcid, 127, 24); 562 slot->s_oem[0] = 0; 563 sda_mem_getstring(rcid, slot->s_prod, 103, 7); 564 slot->s_majver = sda_mem_getbits(rcid, 47, 4); 565 slot->s_minver = sda_mem_getbits(rcid, 43, 4); 566 slot->s_serial = sda_mem_getbits(rcid, 39, 24); 567 break; 568 569 case 2: /* MMC 2.0 - 2.2 */ 570 case 3: /* MMC 3.1 - 3.3 */ 571 case 4: /* MMC 4.x */ 572 slot->s_mfg = sda_mem_getbits(rcid, 127, 8); 573 sda_mem_getstring(rcid, slot->s_oem, 119, 2); 574 sda_mem_getstring(rcid, slot->s_prod, 103, 6); 575 slot->s_majver = sda_mem_getbits(rcid, 55, 4); 576 slot->s_minver = sda_mem_getbits(rcid, 51, 4); 577 slot->s_serial = sda_mem_getbits(rcid, 47, 32); 578 break; 579 580 default: 581 /* this error isn't fatal to us */ 582 sda_slot_err(slot, "Unknown MMCA version (%d)", 583 sda_mem_getbits(rcsd, 125, 4)); 584 break; 585 } 586 587 slot->s_year = sda_mem_getbits(rcid, 11, 4) + 1997; 588 slot->s_month = sda_mem_getbits(rcid, 15, 4); 589 590 csize = sda_mem_getbits(rcsd, 73, 12); 591 rblen = (1 << sda_mem_getbits(rcsd, 83, 4)); 592 cmult = (4 << sda_mem_getbits(rcsd, 49, 3)); 593 bshift = 9; 594 595 } else { 596 597 sda_slot_err(slot, "Card type unknown"); 598 return (DDI_FAILURE); 599 } 600 601 /* 602 * These fields are common to all known MMC/SDcard memory cards. 603 * 604 * The spec requires that block size 512 be supported. 605 * The media may have a different native size, but 512 606 * byte blocks will always work. This is true for SDcard, 607 * and apparently for MMC as well. 608 */ 609 rblen = max(rblen, 512); /* paranoia */ 610 slot->s_nblks = (csize + 1) * cmult * (rblen / 512); 611 slot->s_bshift = bshift; 612 slot->s_blksz = 512; 613 614 slot->s_r2w = (1 << sda_mem_getbits(rcsd, 28, 3)); 615 slot->s_ccc = sda_mem_getbits(rcsd, 95, 12); 616 slot->s_perm_wp = sda_mem_getbits(rcsd, 13, 1); 617 slot->s_temp_wp = sda_mem_getbits(rcsd, 12, 1); 618 slot->s_dsr = sda_mem_getbits(rcsd, 76, 1); 619 620 if (((slot->s_ccc & (1 << 4)) == 0) || 621 (slot->s_perm_wp != 0) || (slot->s_temp_wp != 0)) { 622 slot->s_flags &= ~SLOTF_WRITABLE; 623 } 624 (void) snprintf(date, sizeof (date), "%02d-%04d", 625 slot->s_month, slot->s_year); 626 627 #define prop_set_int(name, val) \ 628 (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, name, val) 629 #define prop_set_str(name, val) \ 630 (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, name, val) 631 #define prop_set_bool(name, val) \ 632 if (val) (void) ddi_prop_create(DDI_DEV_T_NONE, dip, 0, name, NULL, 0) 633 634 prop_set_str("device-type", dtype); 635 prop_set_int("mfg-id", slot->s_mfg); 636 prop_set_str("product-id", slot->s_prod); 637 prop_set_str("oem-id", slot->s_oem); 638 prop_set_str("mfg-date", date); 639 640 prop_set_int("block-size", slot->s_blksz); 641 prop_set_int("num-blocks", slot->s_nblks); 642 prop_set_int("max-freq", slot->s_maxclk); 643 prop_set_bool("dsr-implemented", slot->s_dsr); 644 prop_set_int("ccc", slot->s_ccc); 645 prop_set_bool("perm-wp", slot->s_perm_wp); 646 prop_set_bool("temp-wp", slot->s_temp_wp); 647 648 #undef prop_set_int 649 #undef prop_set_str 650 #undef prop_set_bool 651 652 return (DDI_SUCCESS); 653 } 654