xref: /onnv-gate/usr/src/uts/common/io/sdcard/impl/sda_mem.c (revision 12426:cdff5d2ea989)
17302Sgdamore@opensolaris.org /*
27302Sgdamore@opensolaris.org  * CDDL HEADER START
37302Sgdamore@opensolaris.org  *
47302Sgdamore@opensolaris.org  * The contents of this file are subject to the terms of the
57302Sgdamore@opensolaris.org  * Common Development and Distribution License (the "License").
67302Sgdamore@opensolaris.org  * You may not use this file except in compliance with the License.
77302Sgdamore@opensolaris.org  *
87302Sgdamore@opensolaris.org  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97302Sgdamore@opensolaris.org  * or http://www.opensolaris.org/os/licensing.
107302Sgdamore@opensolaris.org  * See the License for the specific language governing permissions
117302Sgdamore@opensolaris.org  * and limitations under the License.
127302Sgdamore@opensolaris.org  *
137302Sgdamore@opensolaris.org  * When distributing Covered Code, include this CDDL HEADER in each
147302Sgdamore@opensolaris.org  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157302Sgdamore@opensolaris.org  * If applicable, add the following below this CDDL HEADER, with the
167302Sgdamore@opensolaris.org  * fields enclosed by brackets "[]" replaced with your own identifying
177302Sgdamore@opensolaris.org  * information: Portions Copyright [yyyy] [name of copyright owner]
187302Sgdamore@opensolaris.org  *
197302Sgdamore@opensolaris.org  * CDDL HEADER END
207302Sgdamore@opensolaris.org  */
217302Sgdamore@opensolaris.org /*
22*12426Sgdamore@opensolaris.org  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
237302Sgdamore@opensolaris.org  */
247302Sgdamore@opensolaris.org 
257302Sgdamore@opensolaris.org /*
267302Sgdamore@opensolaris.org  * Memory target support for SDcard.
277302Sgdamore@opensolaris.org  */
287302Sgdamore@opensolaris.org 
297302Sgdamore@opensolaris.org #include <sys/types.h>
308292Sgdamore@opensolaris.org #include <sys/note.h>
317302Sgdamore@opensolaris.org #include <sys/conf.h>
32*12426Sgdamore@opensolaris.org #include <sys/blkdev.h>
337302Sgdamore@opensolaris.org #include <sys/ddi.h>
347302Sgdamore@opensolaris.org #include <sys/sunddi.h>
357302Sgdamore@opensolaris.org #include <sys/sdcard/sda.h>
367302Sgdamore@opensolaris.org #include <sys/sdcard/sda_impl.h>
377302Sgdamore@opensolaris.org 
38*12426Sgdamore@opensolaris.org static int sda_mem_errno(sda_err_t);
39*12426Sgdamore@opensolaris.org static int sda_mem_rw(sda_slot_t *, bd_xfer_t *, uint8_t, uint16_t);
40*12426Sgdamore@opensolaris.org static void sda_mem_done(sda_cmd_t *);
417302Sgdamore@opensolaris.org static void sda_mem_getstring(uint32_t *, char *, int, int);
427302Sgdamore@opensolaris.org 
437302Sgdamore@opensolaris.org /*
447302Sgdamore@opensolaris.org  * To minimize complexity and reduce layering, we implement almost the
457302Sgdamore@opensolaris.org  * entire memory card driver (sdcard) here.  The memory card still
467302Sgdamore@opensolaris.org  * needs to be a separate driver though, due to the requirement to
477302Sgdamore@opensolaris.org  * have both SCSI HBA bus ops and SD bus ops.
487302Sgdamore@opensolaris.org  */
497302Sgdamore@opensolaris.org 
507302Sgdamore@opensolaris.org /*
517302Sgdamore@opensolaris.org  * Everything beyond this is private.
527302Sgdamore@opensolaris.org  */
537302Sgdamore@opensolaris.org 
547302Sgdamore@opensolaris.org int
sda_mem_errno(sda_err_t errno)55*12426Sgdamore@opensolaris.org sda_mem_errno(sda_err_t errno)
567302Sgdamore@opensolaris.org {
57*12426Sgdamore@opensolaris.org 	/* the hot path */
58*12426Sgdamore@opensolaris.org 	if (errno == SDA_EOK) {
59*12426Sgdamore@opensolaris.org 		return (0);
60*12426Sgdamore@opensolaris.org 	}
617302Sgdamore@opensolaris.org 
62*12426Sgdamore@opensolaris.org 	switch (errno) {
63*12426Sgdamore@opensolaris.org 	case SDA_ENOMEM:
647302Sgdamore@opensolaris.org 		return (ENOMEM);
65*12426Sgdamore@opensolaris.org 	case SDA_ETIME:
66*12426Sgdamore@opensolaris.org 		return (ETIMEDOUT);
67*12426Sgdamore@opensolaris.org 	case SDA_EWPROTECT:
68*12426Sgdamore@opensolaris.org 		return (EROFS);
69*12426Sgdamore@opensolaris.org 	case SDA_ESUSPENDED:
70*12426Sgdamore@opensolaris.org 	case SDA_ENODEV:
71*12426Sgdamore@opensolaris.org 		return (ENODEV);
72*12426Sgdamore@opensolaris.org 	case SDA_EFAULT:
73*12426Sgdamore@opensolaris.org 	case SDA_ECRC7:
74*12426Sgdamore@opensolaris.org 	case SDA_EPROTO:
75*12426Sgdamore@opensolaris.org 	case SDA_ERESET:
76*12426Sgdamore@opensolaris.org 	case SDA_EIO:
77*12426Sgdamore@opensolaris.org 	case SDA_ERESID:
78*12426Sgdamore@opensolaris.org 	default:
79*12426Sgdamore@opensolaris.org 		return (EIO);
807302Sgdamore@opensolaris.org 	}
817302Sgdamore@opensolaris.org }
827302Sgdamore@opensolaris.org 
83*12426Sgdamore@opensolaris.org void
sda_mem_done(sda_cmd_t * cmdp)84*12426Sgdamore@opensolaris.org sda_mem_done(sda_cmd_t *cmdp)
85*12426Sgdamore@opensolaris.org {
86*12426Sgdamore@opensolaris.org 	bd_xfer_t	*xfer = sda_cmd_data(cmdp);
87*12426Sgdamore@opensolaris.org 	int		errno = sda_cmd_errno(cmdp);
88*12426Sgdamore@opensolaris.org 
89*12426Sgdamore@opensolaris.org 	bd_xfer_done(xfer, sda_mem_errno(errno));
90*12426Sgdamore@opensolaris.org 	sda_cmd_free(cmdp);
91*12426Sgdamore@opensolaris.org }
92*12426Sgdamore@opensolaris.org 
93*12426Sgdamore@opensolaris.org int
sda_mem_rw(sda_slot_t * slot,bd_xfer_t * xfer,uint8_t cmd,uint16_t flags)94*12426Sgdamore@opensolaris.org sda_mem_rw(sda_slot_t *slot, bd_xfer_t *xfer, uint8_t cmd, uint16_t flags)
957302Sgdamore@opensolaris.org {
967302Sgdamore@opensolaris.org 	sda_cmd_t	*cmdp;
977302Sgdamore@opensolaris.org 	uint64_t	nblks;
987302Sgdamore@opensolaris.org 	uint64_t	blkno;
997302Sgdamore@opensolaris.org 	uint16_t	rblen;
1007302Sgdamore@opensolaris.org 
101*12426Sgdamore@opensolaris.org 	blkno = xfer->x_blkno;
102*12426Sgdamore@opensolaris.org 	nblks = xfer->x_nblks;
1037302Sgdamore@opensolaris.org 
104*12426Sgdamore@opensolaris.org 	ASSERT(nblks != 0);
105*12426Sgdamore@opensolaris.org 
106*12426Sgdamore@opensolaris.org 	if ((blkno + nblks) > slot->s_nblks) {
107*12426Sgdamore@opensolaris.org 		return (EINVAL);
1087302Sgdamore@opensolaris.org 	}
1097302Sgdamore@opensolaris.org 
110*12426Sgdamore@opensolaris.org 	cmdp = sda_cmd_alloc(slot, cmd, blkno << slot->s_bshift,
111*12426Sgdamore@opensolaris.org 	    R1, xfer, KM_NOSLEEP);
1127302Sgdamore@opensolaris.org 	if (cmdp == NULL) {
113*12426Sgdamore@opensolaris.org 		return (ENOMEM);
1147302Sgdamore@opensolaris.org 	}
1157302Sgdamore@opensolaris.org 
1167686Sgdamore@opensolaris.org 	if (slot->s_hostp->h_dma != NULL) {
117*12426Sgdamore@opensolaris.org 		cmdp->sc_dmah = xfer->x_dmah;
118*12426Sgdamore@opensolaris.org 		cmdp->sc_ndmac = xfer->x_ndmac;
119*12426Sgdamore@opensolaris.org 		cmdp->sc_dmac = xfer->x_dmac;
1207302Sgdamore@opensolaris.org 		cmdp->sc_kvaddr = 0;
1218708Sgdamore@opensolaris.org 	} else {
1228708Sgdamore@opensolaris.org 		cmdp->sc_ndmac = 0;
123*12426Sgdamore@opensolaris.org 		cmdp->sc_kvaddr = xfer->x_kaddr;
1247302Sgdamore@opensolaris.org 	}
1257302Sgdamore@opensolaris.org 
1267302Sgdamore@opensolaris.org 	rblen = slot->s_blksz;
1277302Sgdamore@opensolaris.org 
128*12426Sgdamore@opensolaris.org 	/* other fields are set by sda_cmd_alloc */
1297302Sgdamore@opensolaris.org 	cmdp->sc_blksz = rblen;
1307302Sgdamore@opensolaris.org 	cmdp->sc_nblks = (uint16_t)nblks;
1317302Sgdamore@opensolaris.org 	cmdp->sc_flags = flags;
1327302Sgdamore@opensolaris.org 
133*12426Sgdamore@opensolaris.org 	sda_cmd_submit(slot, cmdp, sda_mem_done);
134*12426Sgdamore@opensolaris.org 	return (0);
1357302Sgdamore@opensolaris.org }
1367302Sgdamore@opensolaris.org 
137*12426Sgdamore@opensolaris.org int
sda_mem_bd_read(void * arg,bd_xfer_t * xfer)138*12426Sgdamore@opensolaris.org sda_mem_bd_read(void *arg, bd_xfer_t *xfer)
1397302Sgdamore@opensolaris.org {
140*12426Sgdamore@opensolaris.org 	sda_slot_t	*slot = arg;
141*12426Sgdamore@opensolaris.org 	uint8_t		cmd;
142*12426Sgdamore@opensolaris.org 	uint16_t	flags;
1437302Sgdamore@opensolaris.org 
144*12426Sgdamore@opensolaris.org 	if (xfer->x_nblks > 1) {
145*12426Sgdamore@opensolaris.org 		cmd = CMD_READ_MULTI;
146*12426Sgdamore@opensolaris.org 		flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_READ |
147*12426Sgdamore@opensolaris.org 		    SDA_CMDF_AUTO_CMD12;
148*12426Sgdamore@opensolaris.org 	} else {
149*12426Sgdamore@opensolaris.org 		cmd = CMD_READ_SINGLE;
150*12426Sgdamore@opensolaris.org 		flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_READ;
1517302Sgdamore@opensolaris.org 	}
1527302Sgdamore@opensolaris.org 
153*12426Sgdamore@opensolaris.org 	return (sda_mem_rw(slot, xfer, cmd, flags));
1547302Sgdamore@opensolaris.org }
1557302Sgdamore@opensolaris.org 
156*12426Sgdamore@opensolaris.org int
sda_mem_bd_write(void * arg,bd_xfer_t * xfer)157*12426Sgdamore@opensolaris.org sda_mem_bd_write(void *arg, bd_xfer_t *xfer)
1587302Sgdamore@opensolaris.org {
159*12426Sgdamore@opensolaris.org 	sda_slot_t	*slot = arg;
160*12426Sgdamore@opensolaris.org 	uint8_t		cmd;
161*12426Sgdamore@opensolaris.org 	uint16_t	flags;
162*12426Sgdamore@opensolaris.org 
163*12426Sgdamore@opensolaris.org 	if ((slot->s_flags & SLOTF_WRITABLE) == 0) {
164*12426Sgdamore@opensolaris.org 		return (EROFS);
165*12426Sgdamore@opensolaris.org 	}
166*12426Sgdamore@opensolaris.org 	if (xfer->x_nblks > 1) {
167*12426Sgdamore@opensolaris.org 		cmd = CMD_WRITE_MULTI;
168*12426Sgdamore@opensolaris.org 		flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_WRITE |
169*12426Sgdamore@opensolaris.org 		    SDA_CMDF_AUTO_CMD12;
170*12426Sgdamore@opensolaris.org 	} else {
171*12426Sgdamore@opensolaris.org 		cmd = CMD_WRITE_SINGLE;
172*12426Sgdamore@opensolaris.org 		flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_WRITE;
1737302Sgdamore@opensolaris.org 	}
1747302Sgdamore@opensolaris.org 
175*12426Sgdamore@opensolaris.org 	return (sda_mem_rw(slot, xfer, cmd, flags));
1767302Sgdamore@opensolaris.org }
1777302Sgdamore@opensolaris.org 
1787302Sgdamore@opensolaris.org void
sda_mem_bd_driveinfo(void * arg,bd_drive_t * drive)179*12426Sgdamore@opensolaris.org sda_mem_bd_driveinfo(void *arg, bd_drive_t *drive)
1807302Sgdamore@opensolaris.org {
1817302Sgdamore@opensolaris.org 	sda_slot_t	*slot = arg;
1827302Sgdamore@opensolaris.org 
183*12426Sgdamore@opensolaris.org 	drive->d_qsize = 4;	/* we queue up internally, 4 is enough */
184*12426Sgdamore@opensolaris.org 	drive->d_maxxfer = 65536;
185*12426Sgdamore@opensolaris.org 	drive->d_removable = B_TRUE;
186*12426Sgdamore@opensolaris.org 	drive->d_hotpluggable = B_FALSE;
187*12426Sgdamore@opensolaris.org 	drive->d_target = slot->s_slot_num;
1887302Sgdamore@opensolaris.org }
1897302Sgdamore@opensolaris.org 
1907302Sgdamore@opensolaris.org int
sda_mem_bd_mediainfo(void * arg,bd_media_t * media)191*12426Sgdamore@opensolaris.org sda_mem_bd_mediainfo(void *arg, bd_media_t *media)
1927302Sgdamore@opensolaris.org {
193*12426Sgdamore@opensolaris.org 	sda_slot_t	*slot = arg;
1947302Sgdamore@opensolaris.org 
195*12426Sgdamore@opensolaris.org 	sda_slot_enter(slot);
196*12426Sgdamore@opensolaris.org 	if (!slot->s_ready) {
197*12426Sgdamore@opensolaris.org 		sda_slot_exit(slot);
198*12426Sgdamore@opensolaris.org 		return (ENXIO);
1997302Sgdamore@opensolaris.org 	}
200*12426Sgdamore@opensolaris.org 	media->m_nblks = slot->s_nblks;
201*12426Sgdamore@opensolaris.org 	media->m_blksize = slot->s_blksz;
202*12426Sgdamore@opensolaris.org 	media->m_readonly = slot->s_flags & SLOTF_WRITABLE ? B_FALSE : B_TRUE;
203*12426Sgdamore@opensolaris.org 	sda_slot_exit(slot);
204*12426Sgdamore@opensolaris.org 	return (0);
2058289Sgdamore@opensolaris.org }
2068289Sgdamore@opensolaris.org 
2077302Sgdamore@opensolaris.org uint32_t
sda_mem_getbits(uint32_t * resp,int hibit,int len)2087302Sgdamore@opensolaris.org sda_mem_getbits(uint32_t *resp, int hibit, int len)
2097302Sgdamore@opensolaris.org {
2107302Sgdamore@opensolaris.org 	uint32_t	val = 0;
2117302Sgdamore@opensolaris.org 	uint32_t	bit;
2127302Sgdamore@opensolaris.org 
2137302Sgdamore@opensolaris.org 	for (bit = hibit; len--; bit--) {
2147302Sgdamore@opensolaris.org 		val <<= 1;
2157302Sgdamore@opensolaris.org 		val |= ((resp[bit / 32]) >> (bit % 32)) & 1;
2167302Sgdamore@opensolaris.org 	}
2177302Sgdamore@opensolaris.org 	return (val);
2187302Sgdamore@opensolaris.org }
2197302Sgdamore@opensolaris.org 
2207302Sgdamore@opensolaris.org void
sda_mem_getstring(uint32_t * resp,char * s,int hibit,int len)2217302Sgdamore@opensolaris.org sda_mem_getstring(uint32_t *resp, char *s, int hibit, int len)
2227302Sgdamore@opensolaris.org {
2237302Sgdamore@opensolaris.org 	while (len--) {
2247302Sgdamore@opensolaris.org 		*s++ = sda_mem_getbits(resp, hibit, 8);
2257302Sgdamore@opensolaris.org 		hibit -= 8;
2267302Sgdamore@opensolaris.org 	}
2277302Sgdamore@opensolaris.org 	*s = 0;
2287302Sgdamore@opensolaris.org }
2297302Sgdamore@opensolaris.org 
2307302Sgdamore@opensolaris.org uint32_t
sda_mem_maxclk(sda_slot_t * slot)2317302Sgdamore@opensolaris.org sda_mem_maxclk(sda_slot_t *slot)
2327302Sgdamore@opensolaris.org {
2337302Sgdamore@opensolaris.org 	static const uint32_t	mult[16] = {
2347302Sgdamore@opensolaris.org 		0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
2357302Sgdamore@opensolaris.org 	};
2367302Sgdamore@opensolaris.org 
2377302Sgdamore@opensolaris.org 	static const uint32_t	units[8] = {
2387302Sgdamore@opensolaris.org 		10000, 100000, 1000000, 10000000, 0, 0, 0, 0,
2397302Sgdamore@opensolaris.org 	};
2407302Sgdamore@opensolaris.org 	uint8_t			ts;
2417302Sgdamore@opensolaris.org 
2427302Sgdamore@opensolaris.org 	ts = sda_mem_getbits(slot->s_rcsd, 103, 8);
2437302Sgdamore@opensolaris.org 
2447302Sgdamore@opensolaris.org 	return ((units[ts & 0x7]) * (mult[(ts >> 3) & 0xf]));
2457302Sgdamore@opensolaris.org }
2467302Sgdamore@opensolaris.org 
2477302Sgdamore@opensolaris.org int
sda_mem_parse_cid_csd(sda_slot_t * slot)248*12426Sgdamore@opensolaris.org sda_mem_parse_cid_csd(sda_slot_t *slot)
2497302Sgdamore@opensolaris.org {
2507302Sgdamore@opensolaris.org 	uint32_t	*rcid;
2517302Sgdamore@opensolaris.org 	uint32_t	*rcsd;
2527302Sgdamore@opensolaris.org 	int		csdver;
2537302Sgdamore@opensolaris.org 	uint16_t	rblen;
2547302Sgdamore@opensolaris.org 	uint16_t	bshift;
2557302Sgdamore@opensolaris.org 	uint32_t	cmult;
2567302Sgdamore@opensolaris.org 	uint32_t	csize;
2577302Sgdamore@opensolaris.org 
2587302Sgdamore@opensolaris.org 	rcid = slot->s_rcid;
2597302Sgdamore@opensolaris.org 	rcsd = slot->s_rcsd;
2607302Sgdamore@opensolaris.org 
2617302Sgdamore@opensolaris.org 	csdver = sda_mem_getbits(rcsd, 127, 2);
2627302Sgdamore@opensolaris.org 
2637302Sgdamore@opensolaris.org 	if (slot->s_flags & SLOTF_SDMEM) {
2647302Sgdamore@opensolaris.org 		switch (csdver) {
2657302Sgdamore@opensolaris.org 		case 0:
2667302Sgdamore@opensolaris.org 			csize = sda_mem_getbits(rcsd, 73, 12);
2677302Sgdamore@opensolaris.org 			rblen = (1 << sda_mem_getbits(rcsd, 83, 4));
2687302Sgdamore@opensolaris.org 			cmult = (4 << sda_mem_getbits(rcsd, 49, 3));
2697302Sgdamore@opensolaris.org 			bshift = 9;
2707302Sgdamore@opensolaris.org 			break;
2717302Sgdamore@opensolaris.org 		case 1:
2727302Sgdamore@opensolaris.org 			rblen = 512;
2737302Sgdamore@opensolaris.org 			csize = sda_mem_getbits(rcsd, 69, 22);
2747302Sgdamore@opensolaris.org 			cmult = 1024;
2757302Sgdamore@opensolaris.org 			bshift = 0;
2767302Sgdamore@opensolaris.org 			break;
2777302Sgdamore@opensolaris.org 		default:
2787302Sgdamore@opensolaris.org 			sda_slot_err(slot, "Unknown SD CSD version (%d)",
2797302Sgdamore@opensolaris.org 			    csdver);
2807302Sgdamore@opensolaris.org 			return (DDI_FAILURE);
2817302Sgdamore@opensolaris.org 		}
2827302Sgdamore@opensolaris.org 
2837302Sgdamore@opensolaris.org 		slot->s_mfg = sda_mem_getbits(rcid, 127, 8);
2847302Sgdamore@opensolaris.org 		sda_mem_getstring(rcid, slot->s_oem, 119, 2);
2857302Sgdamore@opensolaris.org 		sda_mem_getstring(rcid, slot->s_prod, 103, 5);
2867302Sgdamore@opensolaris.org 		slot->s_majver = sda_mem_getbits(rcid, 63, 4);
2877302Sgdamore@opensolaris.org 		slot->s_minver = sda_mem_getbits(rcid, 59, 4);
2887302Sgdamore@opensolaris.org 		slot->s_serial =  sda_mem_getbits(rcid, 55, 32);
2897302Sgdamore@opensolaris.org 		slot->s_year = sda_mem_getbits(rcid, 19, 8) + 2000;
2907302Sgdamore@opensolaris.org 		slot->s_month = sda_mem_getbits(rcid, 11, 4);
2917302Sgdamore@opensolaris.org 
2927302Sgdamore@opensolaris.org 	} else if (slot->s_flags & SLOTF_MMC) {
2937302Sgdamore@opensolaris.org 		if ((csdver < 1) || (csdver > 2)) {
2947302Sgdamore@opensolaris.org 			sda_slot_err(slot, "Unknown MMC CSD version (%d)",
2957302Sgdamore@opensolaris.org 			    csdver);
2967302Sgdamore@opensolaris.org 			return (DDI_FAILURE);
2977302Sgdamore@opensolaris.org 		}
2987302Sgdamore@opensolaris.org 
2997302Sgdamore@opensolaris.org 		switch (sda_mem_getbits(rcsd, 125, 4)) {
3007302Sgdamore@opensolaris.org 		case 0:	/* MMC 1.0 - 1.2 */
3017302Sgdamore@opensolaris.org 		case 1:	/* MMC 1.4 */
3027302Sgdamore@opensolaris.org 			slot->s_mfg = sda_mem_getbits(rcid, 127, 24);
3037302Sgdamore@opensolaris.org 			slot->s_oem[0] = 0;
3047302Sgdamore@opensolaris.org 			sda_mem_getstring(rcid, slot->s_prod, 103, 7);
3057302Sgdamore@opensolaris.org 			slot->s_majver = sda_mem_getbits(rcid, 47, 4);
3067302Sgdamore@opensolaris.org 			slot->s_minver = sda_mem_getbits(rcid, 43, 4);
3077302Sgdamore@opensolaris.org 			slot->s_serial =  sda_mem_getbits(rcid, 39, 24);
3087302Sgdamore@opensolaris.org 			break;
3097302Sgdamore@opensolaris.org 
3107302Sgdamore@opensolaris.org 		case 2:	/* MMC 2.0 - 2.2 */
3117302Sgdamore@opensolaris.org 		case 3:	/* MMC 3.1 - 3.3 */
3127302Sgdamore@opensolaris.org 		case 4:	/* MMC 4.x */
3137302Sgdamore@opensolaris.org 			slot->s_mfg = sda_mem_getbits(rcid, 127, 8);
3147302Sgdamore@opensolaris.org 			sda_mem_getstring(rcid, slot->s_oem, 119, 2);
3157302Sgdamore@opensolaris.org 			sda_mem_getstring(rcid, slot->s_prod, 103, 6);
3167302Sgdamore@opensolaris.org 			slot->s_majver = sda_mem_getbits(rcid, 55, 4);
3177302Sgdamore@opensolaris.org 			slot->s_minver = sda_mem_getbits(rcid, 51, 4);
3187302Sgdamore@opensolaris.org 			slot->s_serial =  sda_mem_getbits(rcid, 47, 32);
3197302Sgdamore@opensolaris.org 			break;
3207302Sgdamore@opensolaris.org 
3217302Sgdamore@opensolaris.org 		default:
3227302Sgdamore@opensolaris.org 			/* this error isn't fatal to us */
3237302Sgdamore@opensolaris.org 			sda_slot_err(slot, "Unknown MMCA version (%d)",
3247302Sgdamore@opensolaris.org 			    sda_mem_getbits(rcsd, 125, 4));
3257302Sgdamore@opensolaris.org 			break;
3267302Sgdamore@opensolaris.org 		}
3277302Sgdamore@opensolaris.org 
3287302Sgdamore@opensolaris.org 		slot->s_year = sda_mem_getbits(rcid, 11, 4) + 1997;
3297302Sgdamore@opensolaris.org 		slot->s_month = sda_mem_getbits(rcid, 15, 4);
3307302Sgdamore@opensolaris.org 
3317302Sgdamore@opensolaris.org 		csize = sda_mem_getbits(rcsd, 73, 12);
3327302Sgdamore@opensolaris.org 		rblen = (1 << sda_mem_getbits(rcsd, 83, 4));
3337302Sgdamore@opensolaris.org 		cmult = (4 << sda_mem_getbits(rcsd, 49, 3));
3347302Sgdamore@opensolaris.org 		bshift = 9;
3357302Sgdamore@opensolaris.org 
3367302Sgdamore@opensolaris.org 	} else {
3377302Sgdamore@opensolaris.org 
3387302Sgdamore@opensolaris.org 		sda_slot_err(slot, "Card type unknown");
3397302Sgdamore@opensolaris.org 		return (DDI_FAILURE);
3407302Sgdamore@opensolaris.org 	}
3417302Sgdamore@opensolaris.org 
3427302Sgdamore@opensolaris.org 	/*
3437302Sgdamore@opensolaris.org 	 * These fields are common to all known MMC/SDcard memory cards.
3447302Sgdamore@opensolaris.org 	 *
3457302Sgdamore@opensolaris.org 	 * The spec requires that block size 512 be supported.
3467302Sgdamore@opensolaris.org 	 * The media may have a different native size, but 512
3477302Sgdamore@opensolaris.org 	 * byte blocks will always work.  This is true for SDcard,
3487302Sgdamore@opensolaris.org 	 * and apparently for MMC as well.
3497302Sgdamore@opensolaris.org 	 */
3507302Sgdamore@opensolaris.org 	rblen = max(rblen, 512);	/* paranoia */
3517302Sgdamore@opensolaris.org 	slot->s_nblks = (csize + 1) * cmult * (rblen / 512);
3527302Sgdamore@opensolaris.org 	slot->s_bshift = bshift;
3537302Sgdamore@opensolaris.org 	slot->s_blksz = 512;
3547302Sgdamore@opensolaris.org 
3557302Sgdamore@opensolaris.org 	slot->s_r2w = (1 << sda_mem_getbits(rcsd, 28, 3));
3567302Sgdamore@opensolaris.org 	slot->s_ccc = sda_mem_getbits(rcsd, 95, 12);
3577302Sgdamore@opensolaris.org 	slot->s_perm_wp = sda_mem_getbits(rcsd, 13, 1);
3587302Sgdamore@opensolaris.org 	slot->s_temp_wp = sda_mem_getbits(rcsd, 12, 1);
3597302Sgdamore@opensolaris.org 	slot->s_dsr = sda_mem_getbits(rcsd, 76, 1);
3607302Sgdamore@opensolaris.org 
3617302Sgdamore@opensolaris.org 	if (((slot->s_ccc & (1 << 4)) == 0) ||
3627302Sgdamore@opensolaris.org 	    (slot->s_perm_wp != 0) || (slot->s_temp_wp != 0)) {
3637302Sgdamore@opensolaris.org 		slot->s_flags &= ~SLOTF_WRITABLE;
3647302Sgdamore@opensolaris.org 	}
3657302Sgdamore@opensolaris.org 
3667302Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
3677302Sgdamore@opensolaris.org }
368