14bb7efa7SGarrett D'Amore /*
24bb7efa7SGarrett D'Amore  * CDDL HEADER START
34bb7efa7SGarrett D'Amore  *
44bb7efa7SGarrett D'Amore  * The contents of this file are subject to the terms of the
54bb7efa7SGarrett D'Amore  * Common Development and Distribution License (the "License").
64bb7efa7SGarrett D'Amore  * You may not use this file except in compliance with the License.
74bb7efa7SGarrett D'Amore  *
84bb7efa7SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94bb7efa7SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
104bb7efa7SGarrett D'Amore  * See the License for the specific language governing permissions
114bb7efa7SGarrett D'Amore  * and limitations under the License.
124bb7efa7SGarrett D'Amore  *
134bb7efa7SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
144bb7efa7SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154bb7efa7SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
164bb7efa7SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
174bb7efa7SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
184bb7efa7SGarrett D'Amore  *
194bb7efa7SGarrett D'Amore  * CDDL HEADER END
204bb7efa7SGarrett D'Amore  */
214bb7efa7SGarrett D'Amore /*
223f7d54a6SGarrett D'Amore  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
2386e3bca6SGarrett D'Amore  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
24*59d8f100SGarrett D'Amore  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
254bb7efa7SGarrett D'Amore  */
264bb7efa7SGarrett D'Amore 
274bb7efa7SGarrett D'Amore /*
284bb7efa7SGarrett D'Amore  * Memory target support for SDcard.
294bb7efa7SGarrett D'Amore  */
304bb7efa7SGarrett D'Amore 
314bb7efa7SGarrett D'Amore #include <sys/types.h>
32b721891fSGarrett D'Amore #include <sys/note.h>
334bb7efa7SGarrett D'Amore #include <sys/conf.h>
343f7d54a6SGarrett D'Amore #include <sys/blkdev.h>
354bb7efa7SGarrett D'Amore #include <sys/ddi.h>
364bb7efa7SGarrett D'Amore #include <sys/sunddi.h>
374bb7efa7SGarrett D'Amore #include <sys/sdcard/sda.h>
384bb7efa7SGarrett D'Amore #include <sys/sdcard/sda_impl.h>
394bb7efa7SGarrett D'Amore 
403f7d54a6SGarrett D'Amore static int sda_mem_errno(sda_err_t);
413f7d54a6SGarrett D'Amore static int sda_mem_rw(sda_slot_t *, bd_xfer_t *, uint8_t, uint16_t);
423f7d54a6SGarrett D'Amore static void sda_mem_done(sda_cmd_t *);
434bb7efa7SGarrett D'Amore static void sda_mem_getstring(uint32_t *, char *, int, int);
444bb7efa7SGarrett D'Amore 
454bb7efa7SGarrett D'Amore /*
464bb7efa7SGarrett D'Amore  * To minimize complexity and reduce layering, we implement almost the
474bb7efa7SGarrett D'Amore  * entire memory card driver (sdcard) here.  The memory card still
484bb7efa7SGarrett D'Amore  * needs to be a separate driver though, due to the requirement to
494bb7efa7SGarrett D'Amore  * have both SCSI HBA bus ops and SD bus ops.
504bb7efa7SGarrett D'Amore  */
514bb7efa7SGarrett D'Amore 
524bb7efa7SGarrett D'Amore /*
533f7d54a6SGarrett D'Amore  * Everything beyond this is private.
544bb7efa7SGarrett D'Amore  */
554bb7efa7SGarrett D'Amore 
563f7d54a6SGarrett D'Amore int
sda_mem_errno(sda_err_t errno)573f7d54a6SGarrett D'Amore sda_mem_errno(sda_err_t errno)
584bb7efa7SGarrett D'Amore {
593f7d54a6SGarrett D'Amore 	/* the hot path */
603f7d54a6SGarrett D'Amore 	if (errno == SDA_EOK) {
613f7d54a6SGarrett D'Amore 		return (0);
623f7d54a6SGarrett D'Amore 	}
634bb7efa7SGarrett D'Amore 
643f7d54a6SGarrett D'Amore 	switch (errno) {
653f7d54a6SGarrett D'Amore 	case SDA_ENOMEM:
663f7d54a6SGarrett D'Amore 		return (ENOMEM);
673f7d54a6SGarrett D'Amore 	case SDA_ETIME:
683f7d54a6SGarrett D'Amore 		return (ETIMEDOUT);
693f7d54a6SGarrett D'Amore 	case SDA_EWPROTECT:
703f7d54a6SGarrett D'Amore 		return (EROFS);
713f7d54a6SGarrett D'Amore 	case SDA_ESUSPENDED:
723f7d54a6SGarrett D'Amore 	case SDA_ENODEV:
733f7d54a6SGarrett D'Amore 		return (ENODEV);
743f7d54a6SGarrett D'Amore 	case SDA_EFAULT:
753f7d54a6SGarrett D'Amore 	case SDA_ECRC7:
763f7d54a6SGarrett D'Amore 	case SDA_EPROTO:
773f7d54a6SGarrett D'Amore 	case SDA_ERESET:
783f7d54a6SGarrett D'Amore 	case SDA_EIO:
793f7d54a6SGarrett D'Amore 	case SDA_ERESID:
803f7d54a6SGarrett D'Amore 	default:
813f7d54a6SGarrett D'Amore 		return (EIO);
823f7d54a6SGarrett D'Amore 	}
834bb7efa7SGarrett D'Amore }
844bb7efa7SGarrett D'Amore 
854bb7efa7SGarrett D'Amore void
sda_mem_done(sda_cmd_t * cmdp)863f7d54a6SGarrett D'Amore sda_mem_done(sda_cmd_t *cmdp)
874bb7efa7SGarrett D'Amore {
883f7d54a6SGarrett D'Amore 	bd_xfer_t	*xfer = sda_cmd_data(cmdp);
893f7d54a6SGarrett D'Amore 	int		errno = sda_cmd_errno(cmdp);
904bb7efa7SGarrett D'Amore 
913f7d54a6SGarrett D'Amore 	bd_xfer_done(xfer, sda_mem_errno(errno));
924bb7efa7SGarrett D'Amore 	sda_cmd_free(cmdp);
934bb7efa7SGarrett D'Amore }
944bb7efa7SGarrett D'Amore 
953f7d54a6SGarrett D'Amore int
sda_mem_rw(sda_slot_t * slot,bd_xfer_t * xfer,uint8_t cmd,uint16_t flags)963f7d54a6SGarrett D'Amore sda_mem_rw(sda_slot_t *slot, bd_xfer_t *xfer, uint8_t cmd, uint16_t flags)
974bb7efa7SGarrett D'Amore {
984bb7efa7SGarrett D'Amore 	sda_cmd_t	*cmdp;
994bb7efa7SGarrett D'Amore 	uint64_t	nblks;
1004bb7efa7SGarrett D'Amore 	uint64_t	blkno;
1014bb7efa7SGarrett D'Amore 	uint16_t	rblen;
1024bb7efa7SGarrett D'Amore 
1033f7d54a6SGarrett D'Amore 	blkno = xfer->x_blkno;
1043f7d54a6SGarrett D'Amore 	nblks = xfer->x_nblks;
1053f7d54a6SGarrett D'Amore 
1063f7d54a6SGarrett D'Amore 	ASSERT(nblks != 0);
1073f7d54a6SGarrett D'Amore 
1083f7d54a6SGarrett D'Amore 	if ((blkno + nblks) > slot->s_nblks) {
1093f7d54a6SGarrett D'Amore 		return (EINVAL);
1104bb7efa7SGarrett D'Amore 	}
1114bb7efa7SGarrett D'Amore 
1123f7d54a6SGarrett D'Amore 	cmdp = sda_cmd_alloc(slot, cmd, blkno << slot->s_bshift,
1133f7d54a6SGarrett D'Amore 	    R1, xfer, KM_NOSLEEP);
1144bb7efa7SGarrett D'Amore 	if (cmdp == NULL) {
1153f7d54a6SGarrett D'Amore 		return (ENOMEM);
1164bb7efa7SGarrett D'Amore 	}
1174bb7efa7SGarrett D'Amore 
1181cb84fbfSGarrett D'Amore 	if (slot->s_hostp->h_dma != NULL) {
1193f7d54a6SGarrett D'Amore 		cmdp->sc_dmah = xfer->x_dmah;
1203f7d54a6SGarrett D'Amore 		cmdp->sc_ndmac = xfer->x_ndmac;
1213f7d54a6SGarrett D'Amore 		cmdp->sc_dmac = xfer->x_dmac;
1224bb7efa7SGarrett D'Amore 		cmdp->sc_kvaddr = 0;
1239a5113a6SGarrett D'Amore 	} else {
1249a5113a6SGarrett D'Amore 		cmdp->sc_ndmac = 0;
1253f7d54a6SGarrett D'Amore 		cmdp->sc_kvaddr = xfer->x_kaddr;
1264bb7efa7SGarrett D'Amore 	}
1274bb7efa7SGarrett D'Amore 
1284bb7efa7SGarrett D'Amore 	rblen = slot->s_blksz;
1294bb7efa7SGarrett D'Amore 
1303f7d54a6SGarrett D'Amore 	/* other fields are set by sda_cmd_alloc */
1314bb7efa7SGarrett D'Amore 	cmdp->sc_blksz = rblen;
1324bb7efa7SGarrett D'Amore 	cmdp->sc_nblks = (uint16_t)nblks;
1334bb7efa7SGarrett D'Amore 	cmdp->sc_flags = flags;
1344bb7efa7SGarrett D'Amore 
1353f7d54a6SGarrett D'Amore 	sda_cmd_submit(slot, cmdp, sda_mem_done);
1363f7d54a6SGarrett D'Amore 	return (0);
1374bb7efa7SGarrett D'Amore }
1384bb7efa7SGarrett D'Amore 
1393f7d54a6SGarrett D'Amore int
sda_mem_bd_read(void * arg,bd_xfer_t * xfer)1403f7d54a6SGarrett D'Amore sda_mem_bd_read(void *arg, bd_xfer_t *xfer)
1414bb7efa7SGarrett D'Amore {
1423f7d54a6SGarrett D'Amore 	sda_slot_t	*slot = arg;
1433f7d54a6SGarrett D'Amore 	uint8_t		cmd;
1443f7d54a6SGarrett D'Amore 	uint16_t	flags;
1454bb7efa7SGarrett D'Amore 
14686e3bca6SGarrett D'Amore 	if (xfer->x_flags & BD_XFER_POLL) {
14786e3bca6SGarrett D'Amore 		return (EIO);
14886e3bca6SGarrett D'Amore 	}
1493f7d54a6SGarrett D'Amore 	if (xfer->x_nblks > 1) {
1503f7d54a6SGarrett D'Amore 		cmd = CMD_READ_MULTI;
1513f7d54a6SGarrett D'Amore 		flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_READ |
1523f7d54a6SGarrett D'Amore 		    SDA_CMDF_AUTO_CMD12;
1533f7d54a6SGarrett D'Amore 	} else {
1543f7d54a6SGarrett D'Amore 		cmd = CMD_READ_SINGLE;
1553f7d54a6SGarrett D'Amore 		flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_READ;
1564bb7efa7SGarrett D'Amore 	}
1574bb7efa7SGarrett D'Amore 
1583f7d54a6SGarrett D'Amore 	return (sda_mem_rw(slot, xfer, cmd, flags));
1594bb7efa7SGarrett D'Amore }
1604bb7efa7SGarrett D'Amore 
1613f7d54a6SGarrett D'Amore int
sda_mem_bd_write(void * arg,bd_xfer_t * xfer)1623f7d54a6SGarrett D'Amore sda_mem_bd_write(void *arg, bd_xfer_t *xfer)
1634bb7efa7SGarrett D'Amore {
1644bb7efa7SGarrett D'Amore 	sda_slot_t	*slot = arg;
1653f7d54a6SGarrett D'Amore 	uint8_t		cmd;
1663f7d54a6SGarrett D'Amore 	uint16_t	flags;
1674bb7efa7SGarrett D'Amore 
16886e3bca6SGarrett D'Amore 	if (xfer->x_flags & BD_XFER_POLL) {
16986e3bca6SGarrett D'Amore 		return (EIO);
17086e3bca6SGarrett D'Amore 	}
1713f7d54a6SGarrett D'Amore 	if ((slot->s_flags & SLOTF_WRITABLE) == 0) {
1723f7d54a6SGarrett D'Amore 		return (EROFS);
1733f7d54a6SGarrett D'Amore 	}
1743f7d54a6SGarrett D'Amore 	if (xfer->x_nblks > 1) {
1753f7d54a6SGarrett D'Amore 		cmd = CMD_WRITE_MULTI;
1763f7d54a6SGarrett D'Amore 		flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_WRITE |
1773f7d54a6SGarrett D'Amore 		    SDA_CMDF_AUTO_CMD12;
1783f7d54a6SGarrett D'Amore 	} else {
1793f7d54a6SGarrett D'Amore 		cmd = CMD_WRITE_SINGLE;
1803f7d54a6SGarrett D'Amore 		flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_WRITE;
1814bb7efa7SGarrett D'Amore 	}
1824bb7efa7SGarrett D'Amore 
1833f7d54a6SGarrett D'Amore 	return (sda_mem_rw(slot, xfer, cmd, flags));
1844bb7efa7SGarrett D'Amore }
1854bb7efa7SGarrett D'Amore 
1863f7d54a6SGarrett D'Amore void
sda_mem_bd_driveinfo(void * arg,bd_drive_t * drive)1873f7d54a6SGarrett D'Amore sda_mem_bd_driveinfo(void *arg, bd_drive_t *drive)
1884bb7efa7SGarrett D'Amore {
1893f7d54a6SGarrett D'Amore 	sda_slot_t	*slot = arg;
1904bb7efa7SGarrett D'Amore 
1913f7d54a6SGarrett D'Amore 	drive->d_qsize = 4;	/* we queue up internally, 4 is enough */
1923f7d54a6SGarrett D'Amore 	drive->d_maxxfer = 65536;
1933f7d54a6SGarrett D'Amore 	drive->d_removable = B_TRUE;
1943f7d54a6SGarrett D'Amore 	drive->d_hotpluggable = B_FALSE;
1953f7d54a6SGarrett D'Amore 	drive->d_target = slot->s_slot_num;
1964bb7efa7SGarrett D'Amore }
1974bb7efa7SGarrett D'Amore 
1984bb7efa7SGarrett D'Amore int
sda_mem_bd_mediainfo(void * arg,bd_media_t * media)1993f7d54a6SGarrett D'Amore sda_mem_bd_mediainfo(void *arg, bd_media_t *media)
2004bb7efa7SGarrett D'Amore {
2013f7d54a6SGarrett D'Amore 	sda_slot_t	*slot = arg;
2024bb7efa7SGarrett D'Amore 
2033f7d54a6SGarrett D'Amore 	sda_slot_enter(slot);
2043f7d54a6SGarrett D'Amore 	if (!slot->s_ready) {
2053f7d54a6SGarrett D'Amore 		sda_slot_exit(slot);
2063f7d54a6SGarrett D'Amore 		return (ENXIO);
2074bb7efa7SGarrett D'Amore 	}
2083f7d54a6SGarrett D'Amore 	media->m_nblks = slot->s_nblks;
2093f7d54a6SGarrett D'Amore 	media->m_blksize = slot->s_blksz;
2103f7d54a6SGarrett D'Amore 	media->m_readonly = slot->s_flags & SLOTF_WRITABLE ? B_FALSE : B_TRUE;
211*59d8f100SGarrett D'Amore 	media->m_solidstate = B_TRUE;
2123f7d54a6SGarrett D'Amore 	sda_slot_exit(slot);
2133f7d54a6SGarrett D'Amore 	return (0);
214f2b90c3cSGarrett D'Amore }
215f2b90c3cSGarrett D'Amore 
2164bb7efa7SGarrett D'Amore uint32_t
sda_mem_getbits(uint32_t * resp,int hibit,int len)2174bb7efa7SGarrett D'Amore sda_mem_getbits(uint32_t *resp, int hibit, int len)
2184bb7efa7SGarrett D'Amore {
2194bb7efa7SGarrett D'Amore 	uint32_t	val = 0;
2204bb7efa7SGarrett D'Amore 	uint32_t	bit;
2214bb7efa7SGarrett D'Amore 
2224bb7efa7SGarrett D'Amore 	for (bit = hibit; len--; bit--) {
2234bb7efa7SGarrett D'Amore 		val <<= 1;
2244bb7efa7SGarrett D'Amore 		val |= ((resp[bit / 32]) >> (bit % 32)) & 1;
2254bb7efa7SGarrett D'Amore 	}
2264bb7efa7SGarrett D'Amore 	return (val);
2274bb7efa7SGarrett D'Amore }
2284bb7efa7SGarrett D'Amore 
2294bb7efa7SGarrett D'Amore void
sda_mem_getstring(uint32_t * resp,char * s,int hibit,int len)2304bb7efa7SGarrett D'Amore sda_mem_getstring(uint32_t *resp, char *s, int hibit, int len)
2314bb7efa7SGarrett D'Amore {
2324bb7efa7SGarrett D'Amore 	while (len--) {
2334bb7efa7SGarrett D'Amore 		*s++ = sda_mem_getbits(resp, hibit, 8);
2344bb7efa7SGarrett D'Amore 		hibit -= 8;
2354bb7efa7SGarrett D'Amore 	}
2364bb7efa7SGarrett D'Amore 	*s = 0;
2374bb7efa7SGarrett D'Amore }
2384bb7efa7SGarrett D'Amore 
2394bb7efa7SGarrett D'Amore uint32_t
sda_mem_maxclk(sda_slot_t * slot)2404bb7efa7SGarrett D'Amore sda_mem_maxclk(sda_slot_t *slot)
2414bb7efa7SGarrett D'Amore {
2424bb7efa7SGarrett D'Amore 	static const uint32_t	mult[16] = {
2434bb7efa7SGarrett D'Amore 		0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
2444bb7efa7SGarrett D'Amore 	};
2454bb7efa7SGarrett D'Amore 
2464bb7efa7SGarrett D'Amore 	static const uint32_t	units[8] = {
2474bb7efa7SGarrett D'Amore 		10000, 100000, 1000000, 10000000, 0, 0, 0, 0,
2484bb7efa7SGarrett D'Amore 	};
2494bb7efa7SGarrett D'Amore 	uint8_t			ts;
2504bb7efa7SGarrett D'Amore 
2514bb7efa7SGarrett D'Amore 	ts = sda_mem_getbits(slot->s_rcsd, 103, 8);
2524bb7efa7SGarrett D'Amore 
2534bb7efa7SGarrett D'Amore 	return ((units[ts & 0x7]) * (mult[(ts >> 3) & 0xf]));
2544bb7efa7SGarrett D'Amore }
2554bb7efa7SGarrett D'Amore 
2564bb7efa7SGarrett D'Amore int
sda_mem_parse_cid_csd(sda_slot_t * slot)2573f7d54a6SGarrett D'Amore sda_mem_parse_cid_csd(sda_slot_t *slot)
2584bb7efa7SGarrett D'Amore {
2594bb7efa7SGarrett D'Amore 	uint32_t	*rcid;
2604bb7efa7SGarrett D'Amore 	uint32_t	*rcsd;
2614bb7efa7SGarrett D'Amore 	int		csdver;
2624bb7efa7SGarrett D'Amore 	uint16_t	rblen;
2634bb7efa7SGarrett D'Amore 	uint16_t	bshift;
2644bb7efa7SGarrett D'Amore 	uint32_t	cmult;
2654bb7efa7SGarrett D'Amore 	uint32_t	csize;
2664bb7efa7SGarrett D'Amore 
2674bb7efa7SGarrett D'Amore 	rcid = slot->s_rcid;
2684bb7efa7SGarrett D'Amore 	rcsd = slot->s_rcsd;
2694bb7efa7SGarrett D'Amore 
2704bb7efa7SGarrett D'Amore 	csdver = sda_mem_getbits(rcsd, 127, 2);
2714bb7efa7SGarrett D'Amore 
2724bb7efa7SGarrett D'Amore 	if (slot->s_flags & SLOTF_SDMEM) {
2734bb7efa7SGarrett D'Amore 		switch (csdver) {
2744bb7efa7SGarrett D'Amore 		case 0:
2754bb7efa7SGarrett D'Amore 			csize = sda_mem_getbits(rcsd, 73, 12);
2764bb7efa7SGarrett D'Amore 			rblen = (1 << sda_mem_getbits(rcsd, 83, 4));
2774bb7efa7SGarrett D'Amore 			cmult = (4 << sda_mem_getbits(rcsd, 49, 3));
2784bb7efa7SGarrett D'Amore 			bshift = 9;
2794bb7efa7SGarrett D'Amore 			break;
2804bb7efa7SGarrett D'Amore 		case 1:
2814bb7efa7SGarrett D'Amore 			rblen = 512;
2824bb7efa7SGarrett D'Amore 			csize = sda_mem_getbits(rcsd, 69, 22);
2834bb7efa7SGarrett D'Amore 			cmult = 1024;
2844bb7efa7SGarrett D'Amore 			bshift = 0;
2854bb7efa7SGarrett D'Amore 			break;
2864bb7efa7SGarrett D'Amore 		default:
2874bb7efa7SGarrett D'Amore 			sda_slot_err(slot, "Unknown SD CSD version (%d)",
2884bb7efa7SGarrett D'Amore 			    csdver);
2894bb7efa7SGarrett D'Amore 			return (DDI_FAILURE);
2904bb7efa7SGarrett D'Amore 		}
2914bb7efa7SGarrett D'Amore 
2924bb7efa7SGarrett D'Amore 		slot->s_mfg = sda_mem_getbits(rcid, 127, 8);
2934bb7efa7SGarrett D'Amore 		sda_mem_getstring(rcid, slot->s_oem, 119, 2);
2944bb7efa7SGarrett D'Amore 		sda_mem_getstring(rcid, slot->s_prod, 103, 5);
2954bb7efa7SGarrett D'Amore 		slot->s_majver = sda_mem_getbits(rcid, 63, 4);
2964bb7efa7SGarrett D'Amore 		slot->s_minver = sda_mem_getbits(rcid, 59, 4);
2974bb7efa7SGarrett D'Amore 		slot->s_serial =  sda_mem_getbits(rcid, 55, 32);
2984bb7efa7SGarrett D'Amore 		slot->s_year = sda_mem_getbits(rcid, 19, 8) + 2000;
2994bb7efa7SGarrett D'Amore 		slot->s_month = sda_mem_getbits(rcid, 11, 4);
3004bb7efa7SGarrett D'Amore 
3014bb7efa7SGarrett D'Amore 	} else if (slot->s_flags & SLOTF_MMC) {
3024bb7efa7SGarrett D'Amore 		if ((csdver < 1) || (csdver > 2)) {
3034bb7efa7SGarrett D'Amore 			sda_slot_err(slot, "Unknown MMC CSD version (%d)",
3044bb7efa7SGarrett D'Amore 			    csdver);
3054bb7efa7SGarrett D'Amore 			return (DDI_FAILURE);
3064bb7efa7SGarrett D'Amore 		}
3074bb7efa7SGarrett D'Amore 
3084bb7efa7SGarrett D'Amore 		switch (sda_mem_getbits(rcsd, 125, 4)) {
3094bb7efa7SGarrett D'Amore 		case 0:	/* MMC 1.0 - 1.2 */
3104bb7efa7SGarrett D'Amore 		case 1:	/* MMC 1.4 */
3114bb7efa7SGarrett D'Amore 			slot->s_mfg = sda_mem_getbits(rcid, 127, 24);
3124bb7efa7SGarrett D'Amore 			slot->s_oem[0] = 0;
3134bb7efa7SGarrett D'Amore 			sda_mem_getstring(rcid, slot->s_prod, 103, 7);
3144bb7efa7SGarrett D'Amore 			slot->s_majver = sda_mem_getbits(rcid, 47, 4);
3154bb7efa7SGarrett D'Amore 			slot->s_minver = sda_mem_getbits(rcid, 43, 4);
3164bb7efa7SGarrett D'Amore 			slot->s_serial =  sda_mem_getbits(rcid, 39, 24);
3174bb7efa7SGarrett D'Amore 			break;
3184bb7efa7SGarrett D'Amore 
3194bb7efa7SGarrett D'Amore 		case 2:	/* MMC 2.0 - 2.2 */
3204bb7efa7SGarrett D'Amore 		case 3:	/* MMC 3.1 - 3.3 */
3214bb7efa7SGarrett D'Amore 		case 4:	/* MMC 4.x */
3224bb7efa7SGarrett D'Amore 			slot->s_mfg = sda_mem_getbits(rcid, 127, 8);
3234bb7efa7SGarrett D'Amore 			sda_mem_getstring(rcid, slot->s_oem, 119, 2);
3244bb7efa7SGarrett D'Amore 			sda_mem_getstring(rcid, slot->s_prod, 103, 6);
3254bb7efa7SGarrett D'Amore 			slot->s_majver = sda_mem_getbits(rcid, 55, 4);
3264bb7efa7SGarrett D'Amore 			slot->s_minver = sda_mem_getbits(rcid, 51, 4);
3274bb7efa7SGarrett D'Amore 			slot->s_serial =  sda_mem_getbits(rcid, 47, 32);
3284bb7efa7SGarrett D'Amore 			break;
3294bb7efa7SGarrett D'Amore 
3304bb7efa7SGarrett D'Amore 		default:
3314bb7efa7SGarrett D'Amore 			/* this error isn't fatal to us */
3324bb7efa7SGarrett D'Amore 			sda_slot_err(slot, "Unknown MMCA version (%d)",
3334bb7efa7SGarrett D'Amore 			    sda_mem_getbits(rcsd, 125, 4));
3344bb7efa7SGarrett D'Amore 			break;
3354bb7efa7SGarrett D'Amore 		}
3364bb7efa7SGarrett D'Amore 
3374bb7efa7SGarrett D'Amore 		slot->s_year = sda_mem_getbits(rcid, 11, 4) + 1997;
3384bb7efa7SGarrett D'Amore 		slot->s_month = sda_mem_getbits(rcid, 15, 4);
3394bb7efa7SGarrett D'Amore 
3404bb7efa7SGarrett D'Amore 		csize = sda_mem_getbits(rcsd, 73, 12);
3414bb7efa7SGarrett D'Amore 		rblen = (1 << sda_mem_getbits(rcsd, 83, 4));
3424bb7efa7SGarrett D'Amore 		cmult = (4 << sda_mem_getbits(rcsd, 49, 3));
3434bb7efa7SGarrett D'Amore 		bshift = 9;
3444bb7efa7SGarrett D'Amore 
3454bb7efa7SGarrett D'Amore 	} else {
3464bb7efa7SGarrett D'Amore 
3474bb7efa7SGarrett D'Amore 		sda_slot_err(slot, "Card type unknown");
3484bb7efa7SGarrett D'Amore 		return (DDI_FAILURE);
3494bb7efa7SGarrett D'Amore 	}
3504bb7efa7SGarrett D'Amore 
3514bb7efa7SGarrett D'Amore 	/*
3524bb7efa7SGarrett D'Amore 	 * These fields are common to all known MMC/SDcard memory cards.
3534bb7efa7SGarrett D'Amore 	 *
3544bb7efa7SGarrett D'Amore 	 * The spec requires that block size 512 be supported.
3554bb7efa7SGarrett D'Amore 	 * The media may have a different native size, but 512
3564bb7efa7SGarrett D'Amore 	 * byte blocks will always work.  This is true for SDcard,
3574bb7efa7SGarrett D'Amore 	 * and apparently for MMC as well.
3584bb7efa7SGarrett D'Amore 	 */
3594bb7efa7SGarrett D'Amore 	rblen = max(rblen, 512);	/* paranoia */
3604bb7efa7SGarrett D'Amore 	slot->s_nblks = (csize + 1) * cmult * (rblen / 512);
3614bb7efa7SGarrett D'Amore 	slot->s_bshift = bshift;
3624bb7efa7SGarrett D'Amore 	slot->s_blksz = 512;
3634bb7efa7SGarrett D'Amore 
3644bb7efa7SGarrett D'Amore 	slot->s_r2w = (1 << sda_mem_getbits(rcsd, 28, 3));
3654bb7efa7SGarrett D'Amore 	slot->s_ccc = sda_mem_getbits(rcsd, 95, 12);
3664bb7efa7SGarrett D'Amore 	slot->s_perm_wp = sda_mem_getbits(rcsd, 13, 1);
3674bb7efa7SGarrett D'Amore 	slot->s_temp_wp = sda_mem_getbits(rcsd, 12, 1);
3684bb7efa7SGarrett D'Amore 	slot->s_dsr = sda_mem_getbits(rcsd, 76, 1);
3694bb7efa7SGarrett D'Amore 
3704bb7efa7SGarrett D'Amore 	if (((slot->s_ccc & (1 << 4)) == 0) ||
3714bb7efa7SGarrett D'Amore 	    (slot->s_perm_wp != 0) || (slot->s_temp_wp != 0)) {
3724bb7efa7SGarrett D'Amore 		slot->s_flags &= ~SLOTF_WRITABLE;
3734bb7efa7SGarrett D'Amore 	}
3744bb7efa7SGarrett D'Amore 
3754bb7efa7SGarrett D'Amore 	return (DDI_SUCCESS);
3764bb7efa7SGarrett D'Amore }
377