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