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 /*
224bb7efa7SGarrett D'Amore  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
234bb7efa7SGarrett D'Amore  * Use is subject to license terms.
244bb7efa7SGarrett D'Amore  */
254bb7efa7SGarrett D'Amore 
264bb7efa7SGarrett D'Amore /*
274bb7efa7SGarrett D'Amore  * SD card initialization support.
284bb7efa7SGarrett D'Amore  */
294bb7efa7SGarrett D'Amore 
304bb7efa7SGarrett D'Amore #include <sys/types.h>
314bb7efa7SGarrett D'Amore #include <sys/ddi.h>
324bb7efa7SGarrett D'Amore #include <sys/sunddi.h>
334bb7efa7SGarrett D'Amore #include <sys/sdcard/sda.h>
344bb7efa7SGarrett D'Amore #include <sys/sdcard/sda_impl.h>
354bb7efa7SGarrett D'Amore 
364bb7efa7SGarrett D'Amore 
374bb7efa7SGarrett D'Amore /*
384bb7efa7SGarrett D'Amore  * Local Prototypes.
394bb7efa7SGarrett D'Amore  */
404bb7efa7SGarrett D'Amore 
414bb7efa7SGarrett D'Amore static sda_err_t sda_init_mmc(sda_slot_t *);
424bb7efa7SGarrett D'Amore static sda_err_t sda_init_sdio(sda_slot_t *);
434bb7efa7SGarrett D'Amore static sda_err_t sda_init_sdmem(sda_slot_t *);
444bb7efa7SGarrett D'Amore static sda_err_t sda_init_cmd(sda_slot_t *, sda_index_t, uint32_t,
454bb7efa7SGarrett D'Amore     sda_rtype_t, uint32_t *);
464bb7efa7SGarrett D'Amore static sda_err_t sda_init_acmd(sda_slot_t *, sda_index_t, uint32_t,
474bb7efa7SGarrett D'Amore     sda_rtype_t, uint32_t *);
484bb7efa7SGarrett D'Amore static sda_err_t sda_init_blocklen(sda_slot_t *);
494bb7efa7SGarrett D'Amore static sda_err_t sda_init_width(sda_slot_t *);
504bb7efa7SGarrett D'Amore static sda_err_t sda_init_rca(sda_slot_t *);
514bb7efa7SGarrett D'Amore static sda_err_t sda_init_ifcond(sda_slot_t *);
524bb7efa7SGarrett D'Amore static sda_err_t sda_init_highspeed(sda_slot_t *);
534bb7efa7SGarrett D'Amore static sda_err_t sda_init_switch(sda_slot_t *, uint8_t, uint8_t, uint8_t,
544bb7efa7SGarrett D'Amore     uint8_t *);
554bb7efa7SGarrett D'Amore static void sda_init_clock(sda_slot_t *, uint32_t);
564bb7efa7SGarrett D'Amore 
574bb7efa7SGarrett D'Amore /*
584bb7efa7SGarrett D'Amore  * Implementation.
594bb7efa7SGarrett D'Amore  */
604bb7efa7SGarrett D'Amore sda_err_t
sda_init_cmd(sda_slot_t * slot,sda_index_t cmd,uint32_t arg,sda_rtype_t rtype,uint32_t * resp)614bb7efa7SGarrett D'Amore sda_init_cmd(sda_slot_t *slot, sda_index_t cmd, uint32_t arg,
624bb7efa7SGarrett D'Amore     sda_rtype_t rtype, uint32_t *resp)
634bb7efa7SGarrett D'Amore {
644bb7efa7SGarrett D'Amore 	sda_cmd_t	*cmdp;
654bb7efa7SGarrett D'Amore 	sda_err_t	errno;
664bb7efa7SGarrett D'Amore 
674bb7efa7SGarrett D'Amore 	cmdp = sda_cmd_alloc(slot, cmd, arg, rtype, NULL, KM_SLEEP);
684bb7efa7SGarrett D'Amore 
694bb7efa7SGarrett D'Amore 	cmdp->sc_flags |= SDA_CMDF_INIT;
704bb7efa7SGarrett D'Amore 
714bb7efa7SGarrett D'Amore 	errno = sda_cmd_exec(slot, cmdp, resp);
724bb7efa7SGarrett D'Amore 
734bb7efa7SGarrett D'Amore 	sda_cmd_free(cmdp);
744bb7efa7SGarrett D'Amore 
754bb7efa7SGarrett D'Amore 	return (errno);
764bb7efa7SGarrett D'Amore }
774bb7efa7SGarrett D'Amore 
784bb7efa7SGarrett D'Amore sda_err_t
sda_init_acmd(sda_slot_t * slot,sda_index_t cmd,uint32_t arg,sda_rtype_t rtype,uint32_t * resp)794bb7efa7SGarrett D'Amore sda_init_acmd(sda_slot_t *slot, sda_index_t cmd, uint32_t arg,
804bb7efa7SGarrett D'Amore     sda_rtype_t rtype, uint32_t *resp)
814bb7efa7SGarrett D'Amore {
824bb7efa7SGarrett D'Amore 	sda_cmd_t	*cmdp;
834bb7efa7SGarrett D'Amore 	sda_err_t	errno;
844bb7efa7SGarrett D'Amore 
854bb7efa7SGarrett D'Amore 	cmdp = sda_cmd_alloc_acmd(slot, cmd, arg, rtype, NULL, KM_SLEEP);
864bb7efa7SGarrett D'Amore 
874bb7efa7SGarrett D'Amore 	cmdp->sc_flags |= SDA_CMDF_INIT;
884bb7efa7SGarrett D'Amore 
894bb7efa7SGarrett D'Amore 	errno = sda_cmd_exec(slot, cmdp, resp);
904bb7efa7SGarrett D'Amore 
914bb7efa7SGarrett D'Amore 	sda_cmd_free(cmdp);
924bb7efa7SGarrett D'Amore 
934bb7efa7SGarrett D'Amore 	return (errno);
944bb7efa7SGarrett D'Amore }
954bb7efa7SGarrett D'Amore 
964bb7efa7SGarrett D'Amore sda_err_t
sda_init_sdio(sda_slot_t * slot)974bb7efa7SGarrett D'Amore sda_init_sdio(sda_slot_t *slot)
984bb7efa7SGarrett D'Amore {
994bb7efa7SGarrett D'Amore 	slot->s_num_io = 0;
1004bb7efa7SGarrett D'Amore 
1014bb7efa7SGarrett D'Amore 	/*
1024bb7efa7SGarrett D'Amore 	 * TODO: SDIO: We need to initialize the SDIO OCR register using
1034bb7efa7SGarrett D'Amore 	 * the special CMD_IO_SEND_OCR (CMD5) command.
1044bb7efa7SGarrett D'Amore 	 */
1054bb7efa7SGarrett D'Amore 	return (SDA_EOK);
1064bb7efa7SGarrett D'Amore }
1074bb7efa7SGarrett D'Amore 
1084bb7efa7SGarrett D'Amore sda_err_t
sda_init_sdmem(sda_slot_t * slot)1094bb7efa7SGarrett D'Amore sda_init_sdmem(sda_slot_t *slot)
1104bb7efa7SGarrett D'Amore {
1114bb7efa7SGarrett D'Amore 	uint32_t	ocr;
1124bb7efa7SGarrett D'Amore 	int		count;
1134bb7efa7SGarrett D'Amore 
1144bb7efa7SGarrett D'Amore 	slot->s_flags &= ~SLOTF_SDMEM;
1154bb7efa7SGarrett D'Amore 
1164bb7efa7SGarrett D'Amore 	/*
1174bb7efa7SGarrett D'Amore 	 * Try sending the ACMD41 to query the OCR (Op Cond Register).
1184bb7efa7SGarrett D'Amore 	 */
1194bb7efa7SGarrett D'Amore 	if (sda_init_acmd(slot, ACMD_SD_SEND_OCR, 0, R3, &ocr) != SDA_EOK) {
1204bb7efa7SGarrett D'Amore 		/*
1214bb7efa7SGarrett D'Amore 		 * Card failed to respond to query, not an SD card?
1224bb7efa7SGarrett D'Amore 		 * We send GO_IDLE to clear any error status on the
1234bb7efa7SGarrett D'Amore 		 * card.
1244bb7efa7SGarrett D'Amore 		 */
1254bb7efa7SGarrett D'Amore 		(void) sda_init_cmd(slot, CMD_GO_IDLE, 0, R0, NULL);
1264bb7efa7SGarrett D'Amore 		return (SDA_EOK);
1274bb7efa7SGarrett D'Amore 	}
1284bb7efa7SGarrett D'Amore 
1294bb7efa7SGarrett D'Amore 	/*
1304bb7efa7SGarrett D'Amore 	 * Now we have to send our OCR value, along with the HCS (High
1314bb7efa7SGarrett D'Amore 	 * Capacity Support) bit.  The HCS bit is required, to
1324bb7efa7SGarrett D'Amore 	 * activate high capacity cards.  We only set the HCS bit if
1334bb7efa7SGarrett D'Amore 	 * the card responded to CMD8 (SEND_IFCOND), indicating that
1344bb7efa7SGarrett D'Amore 	 * it supports the new protocol.
1354bb7efa7SGarrett D'Amore 	 *
1364bb7efa7SGarrett D'Amore 	 * Note that the HCS bit occupies the same location as the CCS bit
1374bb7efa7SGarrett D'Amore 	 * in the response.
1384bb7efa7SGarrett D'Amore 	 */
1394bb7efa7SGarrett D'Amore 	if ((ocr & slot->s_cur_ocr) == 0) {
1404bb7efa7SGarrett D'Amore 		sda_slot_err(slot, "SD card not compatible with host");
1414bb7efa7SGarrett D'Amore 		return (SDA_ENOTSUP);
1424bb7efa7SGarrett D'Amore 	}
1434bb7efa7SGarrett D'Amore 	/* set the HCS bit if its a ver 2.00 card */
1444bb7efa7SGarrett D'Amore 	if (slot->s_flags & SLOTF_IFCOND) {
1454bb7efa7SGarrett D'Amore 		ocr |= OCR_CCS;
1464bb7efa7SGarrett D'Amore 	}
1474bb7efa7SGarrett D'Amore 
1484bb7efa7SGarrett D'Amore 	/* make sure card is powered up */
1494bb7efa7SGarrett D'Amore 	for (count = 1000000; count != 0; count -= 10000) {
1504bb7efa7SGarrett D'Amore 		uint32_t	r3;
1514bb7efa7SGarrett D'Amore 
1524bb7efa7SGarrett D'Amore 		if (sda_init_acmd(slot, ACMD_SD_SEND_OCR, ocr, R3, &r3) != 0) {
1534bb7efa7SGarrett D'Amore 			sda_slot_err(slot, "SD card failed to power up");
1544bb7efa7SGarrett D'Amore 			return (SDA_ENOTSUP);
1554bb7efa7SGarrett D'Amore 		}
1564bb7efa7SGarrett D'Amore 
1574bb7efa7SGarrett D'Amore 		/* Now check the busy bit */
1584bb7efa7SGarrett D'Amore 		if (r3 & OCR_POWER_UP) {
1594bb7efa7SGarrett D'Amore 			slot->s_flags |= SLOTF_SDMEM;
1604bb7efa7SGarrett D'Amore 			if ((slot->s_flags & SLOTF_IFCOND) &&
1614bb7efa7SGarrett D'Amore 			    (r3 & OCR_CCS)) {
1624bb7efa7SGarrett D'Amore 				slot->s_flags |= SLOTF_SDHC;
1634bb7efa7SGarrett D'Amore 			} else {
1644bb7efa7SGarrett D'Amore 				slot->s_flags &= ~SLOTF_SDHC;
1654bb7efa7SGarrett D'Amore 			}
1664bb7efa7SGarrett D'Amore 			return (0);
1674bb7efa7SGarrett D'Amore 		}
1684bb7efa7SGarrett D'Amore 
1694bb7efa7SGarrett D'Amore 		drv_usecwait(10000);
1704bb7efa7SGarrett D'Amore 	}
1714bb7efa7SGarrett D'Amore 
1724bb7efa7SGarrett D'Amore 	sda_slot_err(slot, "SD card timed out during power up");
1734bb7efa7SGarrett D'Amore 	return (SDA_ETIME);
1744bb7efa7SGarrett D'Amore }
1754bb7efa7SGarrett D'Amore 
1764bb7efa7SGarrett D'Amore sda_err_t
sda_init_mmc(sda_slot_t * slot)1774bb7efa7SGarrett D'Amore sda_init_mmc(sda_slot_t *slot)
1784bb7efa7SGarrett D'Amore {
1794bb7efa7SGarrett D'Amore 	uint32_t	ocr;
1804bb7efa7SGarrett D'Amore 	int		count;
1814bb7efa7SGarrett D'Amore 
1824bb7efa7SGarrett D'Amore 	slot->s_flags &= ~SLOTF_MMC;
1834bb7efa7SGarrett D'Amore 
1844bb7efa7SGarrett D'Amore 	/*
1854bb7efa7SGarrett D'Amore 	 * If the card has already been identified as an SD card, then
1864bb7efa7SGarrett D'Amore 	 * cannot also be an MMC card, so don't probe it as such.
1874bb7efa7SGarrett D'Amore 	 */
1884bb7efa7SGarrett D'Amore 	if (slot->s_flags & SLOTF_SD) {
1894bb7efa7SGarrett D'Amore 		return (SDA_EOK);
1904bb7efa7SGarrett D'Amore 	}
1914bb7efa7SGarrett D'Amore 
1924bb7efa7SGarrett D'Amore 	/*
1934bb7efa7SGarrett D'Amore 	 * Try sending the CMD1 to query the OCR.
1944bb7efa7SGarrett D'Amore 	 */
1954bb7efa7SGarrett D'Amore 	if (sda_init_cmd(slot, CMD_SEND_OCR, 0, R3, &ocr) != 0) {
1964bb7efa7SGarrett D'Amore 		/*
1974bb7efa7SGarrett D'Amore 		 * Card failed to respond to query, not an MMC card?
1984bb7efa7SGarrett D'Amore 		 * We send GO_IDLE to clear any error status on the
1994bb7efa7SGarrett D'Amore 		 * card.
2004bb7efa7SGarrett D'Amore 		 */
2014bb7efa7SGarrett D'Amore 		(void) sda_init_cmd(slot, CMD_GO_IDLE, 0, R0, NULL);
2024bb7efa7SGarrett D'Amore 		return (SDA_EOK);
2034bb7efa7SGarrett D'Amore 	}
2044bb7efa7SGarrett D'Amore 
2054bb7efa7SGarrett D'Amore 	if ((ocr & slot->s_cur_ocr) == 0) {
2064bb7efa7SGarrett D'Amore 		sda_slot_err(slot, "MMC card not compatible with host");
2074bb7efa7SGarrett D'Amore 		return (SDA_ENOTSUP);
2084bb7efa7SGarrett D'Amore 	}
2094bb7efa7SGarrett D'Amore 
2104bb7efa7SGarrett D'Amore 	/* make sure card is powered up */
2114bb7efa7SGarrett D'Amore 	for (count = 1000000; count != 0; count -= 10000) {
2124bb7efa7SGarrett D'Amore 		uint32_t	r3;
2134bb7efa7SGarrett D'Amore 
2144bb7efa7SGarrett D'Amore 		if (sda_init_cmd(slot, CMD_SEND_OCR, ocr, R3, &r3) != 0) {
2154bb7efa7SGarrett D'Amore 			sda_slot_err(slot, "MMC card failed to power up");
2164bb7efa7SGarrett D'Amore 			return (SDA_ENOTSUP);
2174bb7efa7SGarrett D'Amore 		}
2184bb7efa7SGarrett D'Amore 
2194bb7efa7SGarrett D'Amore 		/* Now check the busy bit */
2204bb7efa7SGarrett D'Amore 		if (r3 & OCR_POWER_UP) {
2214bb7efa7SGarrett D'Amore 			slot->s_flags |= SLOTF_MMC;
2224bb7efa7SGarrett D'Amore 			return (SDA_EOK);
2234bb7efa7SGarrett D'Amore 		}
2244bb7efa7SGarrett D'Amore 
2254bb7efa7SGarrett D'Amore 		drv_usecwait(10000);
2264bb7efa7SGarrett D'Amore 	}
2274bb7efa7SGarrett D'Amore 
2284bb7efa7SGarrett D'Amore 	sda_slot_err(slot, "MMC card timed out during power up");
2294bb7efa7SGarrett D'Amore 	return (SDA_ETIME);
2304bb7efa7SGarrett D'Amore }
2314bb7efa7SGarrett D'Amore 
2324bb7efa7SGarrett D'Amore sda_err_t
sda_init_card(sda_slot_t * slot)2334bb7efa7SGarrett D'Amore sda_init_card(sda_slot_t *slot)
2344bb7efa7SGarrett D'Amore {
2354bb7efa7SGarrett D'Amore 	int		rv;
2364bb7efa7SGarrett D'Amore 	uint32_t	resp;
2374bb7efa7SGarrett D'Amore 	uint32_t	val;
2384bb7efa7SGarrett D'Amore 
2394bb7efa7SGarrett D'Amore 	/*
2404bb7efa7SGarrett D'Amore 	 * Power off slot/card initially.
2414bb7efa7SGarrett D'Amore 	 */
2424bb7efa7SGarrett D'Amore 	sda_slot_power_off(slot);
2434bb7efa7SGarrett D'Amore 
2444bb7efa7SGarrett D'Amore 	/*
2454bb7efa7SGarrett D'Amore 	 * Apply initial power to the slot.
2464bb7efa7SGarrett D'Amore 	 */
2474bb7efa7SGarrett D'Amore 	if ((rv = sda_slot_power_on(slot)) != 0) {
2484bb7efa7SGarrett D'Amore 		return (rv);
2494bb7efa7SGarrett D'Amore 	}
2504bb7efa7SGarrett D'Amore 
2514bb7efa7SGarrett D'Amore 	/*
2524bb7efa7SGarrett D'Amore 	 * First enable the clock, but only at 400 kHz.  All cards are
2534bb7efa7SGarrett D'Amore 	 * supposed to be able to operate between this speed and 100
2544bb7efa7SGarrett D'Amore 	 * kHz, and all hosts must be able to pick a speed between 100
2554bb7efa7SGarrett D'Amore 	 * kHz and 400 kHz.
2564bb7efa7SGarrett D'Amore 	 *
2574bb7efa7SGarrett D'Amore 	 * Once we know what the device can support, then we speed up.
2584bb7efa7SGarrett D'Amore 	 */
2594bb7efa7SGarrett D'Amore 	sda_init_clock(slot, 400000);
2604bb7efa7SGarrett D'Amore 
2614bb7efa7SGarrett D'Amore 	if ((rv = sda_init_ifcond(slot)) != SDA_EOK) {
2624bb7efa7SGarrett D'Amore 		goto done;
2634bb7efa7SGarrett D'Amore 	}
2644bb7efa7SGarrett D'Amore 
2654bb7efa7SGarrett D'Amore 	if (((rv = sda_init_sdio(slot)) != SDA_EOK) ||
2664bb7efa7SGarrett D'Amore 	    ((rv = sda_init_sdmem(slot)) != SDA_EOK) ||
2674bb7efa7SGarrett D'Amore 	    ((rv = sda_init_mmc(slot)) != SDA_EOK)) {
2684bb7efa7SGarrett D'Amore 
2694bb7efa7SGarrett D'Amore 		/* message will already have been logged */
2704bb7efa7SGarrett D'Amore 		goto done;
2714bb7efa7SGarrett D'Amore 	}
2724bb7efa7SGarrett D'Amore 
2734bb7efa7SGarrett D'Amore 	if ((slot->s_flags & (SLOTF_MEMORY | SLOTF_SDIO)) == 0) {
2744bb7efa7SGarrett D'Amore 		sda_slot_err(slot, "Unidentified card type");
2754bb7efa7SGarrett D'Amore 		rv = SDA_ENOTSUP;
2764bb7efa7SGarrett D'Amore 		goto done;
2774bb7efa7SGarrett D'Amore 	}
2784bb7efa7SGarrett D'Amore 
2794bb7efa7SGarrett D'Amore 	/*
2804bb7efa7SGarrett D'Amore 	 * Memory cards need to obtain their CID before getting their RCA.
2814bb7efa7SGarrett D'Amore 	 * This is a requirement for the state transitions... they go thru
2824bb7efa7SGarrett D'Amore 	 * the ident state, unlike SDIO cards.
2834bb7efa7SGarrett D'Amore 	 */
2844bb7efa7SGarrett D'Amore 	if (slot->s_flags & SLOTF_MEMORY) {
2854bb7efa7SGarrett D'Amore 		rv = sda_init_cmd(slot, CMD_BCAST_CID, 0, R2, slot->s_rcid);
2864bb7efa7SGarrett D'Amore 		if (rv != SDA_EOK) {
2874bb7efa7SGarrett D'Amore 			sda_slot_err(slot, "Failed getting card CID (%d)", rv);
2884bb7efa7SGarrett D'Amore 			goto done;
2894bb7efa7SGarrett D'Amore 		}
2904bb7efa7SGarrett D'Amore 	}
2914bb7efa7SGarrett D'Amore 
2924bb7efa7SGarrett D'Amore 	if ((rv = sda_init_rca(slot)) != SDA_EOK) {
2934bb7efa7SGarrett D'Amore 		goto done;
2944bb7efa7SGarrett D'Amore 	}
2954bb7efa7SGarrett D'Amore 
2964bb7efa7SGarrett D'Amore 	slot->s_maxclk = 0xffffffffU;	/* special sentinel */
2974bb7efa7SGarrett D'Amore 
2984bb7efa7SGarrett D'Amore 	/*
2994bb7efa7SGarrett D'Amore 	 * Figure out card supported bus width and speed.
3004bb7efa7SGarrett D'Amore 	 *
3014bb7efa7SGarrett D'Amore 	 * TODO: SDIO: For IO cards, we have to check what speed the card
3024bb7efa7SGarrett D'Amore 	 * supports by looking in the CCCR_CAPAB register.  (SDIO cards
3034bb7efa7SGarrett D'Amore 	 * can go low-speed only, full-speed, or high-speed.)
3044bb7efa7SGarrett D'Amore 	 */
3054bb7efa7SGarrett D'Amore 	if (slot->s_flags & SLOTF_MEMORY) {
3064bb7efa7SGarrett D'Amore 
3074bb7efa7SGarrett D'Amore 		/*
3084bb7efa7SGarrett D'Amore 		 * We need to obtain the CSD.
3094bb7efa7SGarrett D'Amore 		 */
3104bb7efa7SGarrett D'Amore 		rv = sda_init_cmd(slot, CMD_SEND_CSD, slot->s_rca << 16, R2,
3114bb7efa7SGarrett D'Amore 		    slot->s_rcsd);
3124bb7efa7SGarrett D'Amore 		if (rv != 0) {
3134bb7efa7SGarrett D'Amore 			sda_slot_err(slot, "Failed getting card CSD (%d)", rv);
3144bb7efa7SGarrett D'Amore 			goto done;
3154bb7efa7SGarrett D'Amore 		}
3164bb7efa7SGarrett D'Amore 
3174bb7efa7SGarrett D'Amore 		/*
3184bb7efa7SGarrett D'Amore 		 * Calculate the maxclock.
3194bb7efa7SGarrett D'Amore 		 */
3204bb7efa7SGarrett D'Amore 		slot->s_maxclk = sda_mem_maxclk(slot);
3214bb7efa7SGarrett D'Amore 	}
3224bb7efa7SGarrett D'Amore 	if (((slot->s_flags & SLOTF_SDMEM) != 0) &&
3234bb7efa7SGarrett D'Amore 	    ((slot->s_caps & SLOT_CAP_4BITS) != 0)) {
3244bb7efa7SGarrett D'Amore 		slot->s_flags |= SLOTF_4BITS;
3254bb7efa7SGarrett D'Amore 	}
3264bb7efa7SGarrett D'Amore 	if (slot->s_flags & SLOTF_SDIO) {
3274bb7efa7SGarrett D'Amore 		sda_slot_debug(slot, "Wide SDIO bus not yet supported");
3284bb7efa7SGarrett D'Amore 		slot->s_flags &= ~SLOTF_4BITS;
3294bb7efa7SGarrett D'Amore 	}
3304bb7efa7SGarrett D'Amore 
3314bb7efa7SGarrett D'Amore 	/*
3324bb7efa7SGarrett D'Amore 	 * Now select the card.
3334bb7efa7SGarrett D'Amore 	 */
3344bb7efa7SGarrett D'Amore 	if ((rv = sda_init_cmd(slot, CMD_SELECT_CARD, slot->s_rca << 16,
3354bb7efa7SGarrett D'Amore 	    R1b, &resp)) != SDA_EOK) {
3364bb7efa7SGarrett D'Amore 		sda_slot_err(slot, "Failed selecting card (%d, %x)", rv, resp);
3374bb7efa7SGarrett D'Amore 		goto done;
3384bb7efa7SGarrett D'Amore 	}
3394bb7efa7SGarrett D'Amore 
3404bb7efa7SGarrett D'Amore 	if ((rv = sda_init_highspeed(slot)) != SDA_EOK) {
3414bb7efa7SGarrett D'Amore 		goto done;
3424bb7efa7SGarrett D'Amore 	}
3434bb7efa7SGarrett D'Amore 
3444bb7efa7SGarrett D'Amore 	sda_init_clock(slot, slot->s_maxclk);
3454bb7efa7SGarrett D'Amore 
3464bb7efa7SGarrett D'Amore 	/*
3474bb7efa7SGarrett D'Amore 	 * Lets go to 4-bit bus mode, if possible.
3484bb7efa7SGarrett D'Amore 	 */
3494bb7efa7SGarrett D'Amore 	if ((rv = sda_init_width(slot)) != SDA_EOK) {
3504bb7efa7SGarrett D'Amore 		goto done;
3514bb7efa7SGarrett D'Amore 	}
3524bb7efa7SGarrett D'Amore 
3534bb7efa7SGarrett D'Amore 	if ((rv = sda_init_blocklen(slot)) != SDA_EOK) {
3544bb7efa7SGarrett D'Amore 		goto done;
3554bb7efa7SGarrett D'Amore 	}
3564bb7efa7SGarrett D'Amore 
3574bb7efa7SGarrett D'Amore 	/* note if a card is writable */
3584bb7efa7SGarrett D'Amore 	if ((sda_getprop(slot, SDA_PROP_WPROTECT, &val) == SDA_EOK) &&
3594bb7efa7SGarrett D'Amore 	    (val == 0)) {
3604bb7efa7SGarrett D'Amore 		slot->s_flags |= SLOTF_WRITABLE;
3614bb7efa7SGarrett D'Amore 	}
3624bb7efa7SGarrett D'Amore 
3634bb7efa7SGarrett D'Amore 	rv = SDA_EOK;
3644bb7efa7SGarrett D'Amore 
3654bb7efa7SGarrett D'Amore done:
3664bb7efa7SGarrett D'Amore 
3674bb7efa7SGarrett D'Amore 	sda_slot_enter(slot);
3684bb7efa7SGarrett D'Amore 	slot->s_init = B_FALSE;
3694bb7efa7SGarrett D'Amore 	sda_slot_exit(slot);
3704bb7efa7SGarrett D'Amore 
3714bb7efa7SGarrett D'Amore 	sda_slot_wakeup(slot);
3724bb7efa7SGarrett D'Amore 
3734bb7efa7SGarrett D'Amore 	return (rv);
3744bb7efa7SGarrett D'Amore }
3754bb7efa7SGarrett D'Amore 
3764bb7efa7SGarrett D'Amore sda_err_t
sda_init_blocklen(sda_slot_t * slot)3774bb7efa7SGarrett D'Amore sda_init_blocklen(sda_slot_t *slot)
3784bb7efa7SGarrett D'Amore {
3794bb7efa7SGarrett D'Amore 	int		rv;
3804bb7efa7SGarrett D'Amore 	uint32_t	resp;
3814bb7efa7SGarrett D'Amore 
3824bb7efa7SGarrett D'Amore 	if ((slot->s_flags & SLOTF_MEMORY) == 0)  {
3834bb7efa7SGarrett D'Amore 		return (SDA_EOK);
3844bb7efa7SGarrett D'Amore 	}
3854bb7efa7SGarrett D'Amore 
3864bb7efa7SGarrett D'Amore 	/*
3874bb7efa7SGarrett D'Amore 	 * All memory cards support block sizes of 512.  Full stop.
3884bb7efa7SGarrett D'Amore 	 */
3894bb7efa7SGarrett D'Amore 	rv = sda_init_cmd(slot, CMD_SET_BLOCKLEN, 512, R1, &resp);
3904bb7efa7SGarrett D'Amore 	if (rv != SDA_EOK) {
3914bb7efa7SGarrett D'Amore 		sda_slot_err(slot, "Unable to set block length (%d, %x)",
3924bb7efa7SGarrett D'Amore 		    rv, resp);
3934bb7efa7SGarrett D'Amore 	}
3944bb7efa7SGarrett D'Amore 	return (rv);
3954bb7efa7SGarrett D'Amore }
3964bb7efa7SGarrett D'Amore 
3974bb7efa7SGarrett D'Amore void
sda_init_clock(sda_slot_t * slot,uint32_t hz)3984bb7efa7SGarrett D'Amore sda_init_clock(sda_slot_t *slot, uint32_t hz)
3994bb7efa7SGarrett D'Amore {
4004bb7efa7SGarrett D'Amore 	int		rv;
4014bb7efa7SGarrett D'Amore 	uint32_t	act;
4024bb7efa7SGarrett D'Amore 
4034bb7efa7SGarrett D'Amore 	/*
4044bb7efa7SGarrett D'Amore 	 * Note that at no time is a failure programming the clock
4054bb7efa7SGarrett D'Amore 	 * itself necessarily a fatal error.  Although if the clock
4064bb7efa7SGarrett D'Amore 	 * wasn't programmed, other things will probably not work during
4074bb7efa7SGarrett D'Amore 	 * initialization.
4084bb7efa7SGarrett D'Amore 	 */
4094bb7efa7SGarrett D'Amore 
4104bb7efa7SGarrett D'Amore 	if ((rv = sda_setprop(slot, SDA_PROP_CLOCK, hz)) != SDA_EOK) {
4114bb7efa7SGarrett D'Amore 		sda_slot_err(slot, "Failed setting clock to %u Hz (%d)",
4124bb7efa7SGarrett D'Amore 		    hz, rv);
4134bb7efa7SGarrett D'Amore 		/* XXX: FMA fail the slot */
4144bb7efa7SGarrett D'Amore 		return;
4154bb7efa7SGarrett D'Amore 	}
4164bb7efa7SGarrett D'Amore 
417*f2b90c3cSGarrett D'Amore 	rv = sda_getprop(slot, SDA_PROP_CLOCK, &act);
418*f2b90c3cSGarrett D'Amore 	sda_slot_debug(slot,
419*f2b90c3cSGarrett D'Amore 	    rv == SDA_EOK ? "Clock set to %u Hz (requested %u Hz)" :
420*f2b90c3cSGarrett D'Amore 	    "Clock frequency unknown (good luck).", act, hz);
4214bb7efa7SGarrett D'Amore 
4224bb7efa7SGarrett D'Amore 	/*
4234bb7efa7SGarrett D'Amore 	 * For now, just wait 10msec for clocks to stabilize to the
4244bb7efa7SGarrett D'Amore 	 * card.  (Is this really necessary?)
4254bb7efa7SGarrett D'Amore 	 */
4264bb7efa7SGarrett D'Amore 	delay(drv_usectohz(10000));
4274bb7efa7SGarrett D'Amore }
4284bb7efa7SGarrett D'Amore 
4294bb7efa7SGarrett D'Amore sda_err_t
sda_init_width(sda_slot_t * slot)4304bb7efa7SGarrett D'Amore sda_init_width(sda_slot_t *slot)
4314bb7efa7SGarrett D'Amore {
4324bb7efa7SGarrett D'Amore 	int		rv;
4334bb7efa7SGarrett D'Amore 	uint32_t	resp;
4344bb7efa7SGarrett D'Amore 
4354bb7efa7SGarrett D'Amore 	/*
4364bb7efa7SGarrett D'Amore 	 * Spec says we should command the card first.
4374bb7efa7SGarrett D'Amore 	 */
4384bb7efa7SGarrett D'Amore 
4394bb7efa7SGarrett D'Amore 	rv = sda_setprop(slot, SDA_PROP_BUSWIDTH, 1);
4404bb7efa7SGarrett D'Amore 	if (rv != SDA_EOK) {
4414bb7efa7SGarrett D'Amore 		sda_slot_err(slot, "Unable to set slot 1-bit mode (%d)", rv);
4424bb7efa7SGarrett D'Amore 		return (rv);
4434bb7efa7SGarrett D'Amore 	}
4444bb7efa7SGarrett D'Amore 
4454bb7efa7SGarrett D'Amore 	if ((slot->s_flags & SLOTF_4BITS) == 0) {
4464bb7efa7SGarrett D'Amore 		return (SDA_EOK);
4474bb7efa7SGarrett D'Amore 	}
4484bb7efa7SGarrett D'Amore 
4494bb7efa7SGarrett D'Amore 	/*
4504bb7efa7SGarrett D'Amore 	 * TODO: SDIO: SDIO cards set the CCCR_BUS_WIDTH
4514bb7efa7SGarrett D'Amore 	 * and CCCR_CD_DISABLE bits here.
4524bb7efa7SGarrett D'Amore 	 */
4534bb7efa7SGarrett D'Amore 
4544bb7efa7SGarrett D'Amore 	/*
4554bb7efa7SGarrett D'Amore 	 * If we're going to use all 4 pins, we really need to disconnect
4564bb7efa7SGarrett D'Amore 	 * the card pullup resistor.   A consquence of this, is that hosts
4574bb7efa7SGarrett D'Amore 	 * which use that resistor for detection must not claim to support
4584bb7efa7SGarrett D'Amore 	 * 4-bit bus mode.  This is a limitation of our implementation.
4594bb7efa7SGarrett D'Amore 	 */
4604bb7efa7SGarrett D'Amore 	rv = sda_init_acmd(slot, ACMD_SET_CLR_CARD_DETECT, 1, R1, &resp);
4614bb7efa7SGarrett D'Amore 	if (rv != SDA_EOK) {
4624bb7efa7SGarrett D'Amore 		sda_slot_err(slot,
4634bb7efa7SGarrett D'Amore 		    "Unable disconnect DAT3 resistor on card (%d, %x)",
4644bb7efa7SGarrett D'Amore 		    rv, resp);
4654bb7efa7SGarrett D'Amore 		/* non-fatal error, muddle on */
4664bb7efa7SGarrett D'Amore 		return (SDA_EOK);
4674bb7efa7SGarrett D'Amore 	}
4684bb7efa7SGarrett D'Amore 
4694bb7efa7SGarrett D'Amore 	rv = sda_init_acmd(slot, ACMD_SET_BUS_WIDTH, 2, R1, &resp);
4704bb7efa7SGarrett D'Amore 	if (rv != SDA_EOK) {
4714bb7efa7SGarrett D'Amore 		sda_slot_err(slot, "Unable to set card 4-bit mode (%d, %x)",
4724bb7efa7SGarrett D'Amore 		    rv, resp);
4734bb7efa7SGarrett D'Amore 		/* non-fatal error, muddle on */
4744bb7efa7SGarrett D'Amore 		return (SDA_EOK);
4754bb7efa7SGarrett D'Amore 	}
4764bb7efa7SGarrett D'Amore 
4774bb7efa7SGarrett D'Amore 	rv = sda_setprop(slot, SDA_PROP_BUSWIDTH, 4);
4784bb7efa7SGarrett D'Amore 	if (rv != SDA_EOK) {
4794bb7efa7SGarrett D'Amore 		/*
4804bb7efa7SGarrett D'Amore 		 * This is bad news.  We've already asked for the card to
4814bb7efa7SGarrett D'Amore 		 * to use 4-bit mode, but the host is not complying.  It
4824bb7efa7SGarrett D'Amore 		 * shouldn't ever happen, so we just error out.
4834bb7efa7SGarrett D'Amore 		 */
4844bb7efa7SGarrett D'Amore 		sda_slot_err(slot, "Unable to set slot 4-bit mode (%d)", rv);
4854bb7efa7SGarrett D'Amore 	}
4864bb7efa7SGarrett D'Amore 
4874bb7efa7SGarrett D'Amore 	return (rv);
4884bb7efa7SGarrett D'Amore }
4894bb7efa7SGarrett D'Amore 
4904bb7efa7SGarrett D'Amore sda_err_t
sda_init_ifcond(sda_slot_t * slot)4914bb7efa7SGarrett D'Amore sda_init_ifcond(sda_slot_t *slot)
4924bb7efa7SGarrett D'Amore {
4934bb7efa7SGarrett D'Amore 	int		rv;
4944bb7efa7SGarrett D'Amore 	int		tries;
4954bb7efa7SGarrett D'Amore 	uint32_t	vchk;
4964bb7efa7SGarrett D'Amore 	uint32_t	resp;
4974bb7efa7SGarrett D'Amore 
4984bb7efa7SGarrett D'Amore 	/*
4994bb7efa7SGarrett D'Amore 	 * Try SEND_IF_COND.  Note that this assumes that the host is
5004bb7efa7SGarrett D'Amore 	 * supplying 2.7 - 3.6 voltage range.  The standard is not
5014bb7efa7SGarrett D'Amore 	 * defined for any other ranges.
5024bb7efa7SGarrett D'Amore 	 */
5034bb7efa7SGarrett D'Amore 	vchk = R7_VHS_27_36V | R7_PATTERN;
5044bb7efa7SGarrett D'Amore 
5054bb7efa7SGarrett D'Amore 	/* we try this a few times, just to be sure */
5064bb7efa7SGarrett D'Amore 	for (tries = 0; tries < 5; tries++) {
5074bb7efa7SGarrett D'Amore 		rv = sda_init_cmd(slot, CMD_GO_IDLE, 0, R0, NULL);
5084bb7efa7SGarrett D'Amore 		if (rv != SDA_EOK) {
5094bb7efa7SGarrett D'Amore 			sda_slot_err(slot, "Failed to IDLE card");
5104bb7efa7SGarrett D'Amore 			return (rv);
5114bb7efa7SGarrett D'Amore 		}
5124bb7efa7SGarrett D'Amore 
5134bb7efa7SGarrett D'Amore 		rv = sda_init_cmd(slot, CMD_SEND_IF_COND, vchk, R7, &resp);
5144bb7efa7SGarrett D'Amore 		if (rv == SDA_EOK) {
5154bb7efa7SGarrett D'Amore 			break;
5164bb7efa7SGarrett D'Amore 		}
5174bb7efa7SGarrett D'Amore 		delay(drv_usectohz(10000));
5184bb7efa7SGarrett D'Amore 	}
5194bb7efa7SGarrett D'Amore 
5204bb7efa7SGarrett D'Amore 	if (rv != SDA_EOK) {
5214bb7efa7SGarrett D'Amore 		(void) sda_init_cmd(slot, CMD_GO_IDLE, 0, R0, NULL);
5224bb7efa7SGarrett D'Amore 		slot->s_flags &= ~SLOTF_IFCOND;
5234bb7efa7SGarrett D'Amore 
5244bb7efa7SGarrett D'Amore 	} else if (resp != vchk) {
5254bb7efa7SGarrett D'Amore 		sda_slot_err(slot, "Card voltages incompatible! (%x)", resp);
5264bb7efa7SGarrett D'Amore 		return (SDA_ENOTSUP);
5274bb7efa7SGarrett D'Amore 
5284bb7efa7SGarrett D'Amore 	} else {
5294bb7efa7SGarrett D'Amore 		/* SDHC compliant */
5304bb7efa7SGarrett D'Amore 		slot->s_flags |= SLOTF_IFCOND;
5314bb7efa7SGarrett D'Amore 	}
5324bb7efa7SGarrett D'Amore 
5334bb7efa7SGarrett D'Amore 	return (SDA_EOK);
5344bb7efa7SGarrett D'Amore }
5354bb7efa7SGarrett D'Amore 
5364bb7efa7SGarrett D'Amore sda_err_t
sda_init_rca(sda_slot_t * slot)5374bb7efa7SGarrett D'Amore sda_init_rca(sda_slot_t *slot)
5384bb7efa7SGarrett D'Amore {
5394bb7efa7SGarrett D'Amore 	int		rv;
5404bb7efa7SGarrett D'Amore 	int		tries;
5414bb7efa7SGarrett D'Amore 	uint32_t	resp;
5424bb7efa7SGarrett D'Amore 
5434bb7efa7SGarrett D'Amore 	/*
5444bb7efa7SGarrett D'Amore 	 * Program the RCA.  Note that MMC has a different mechanism
5454bb7efa7SGarrett D'Amore 	 * for this.
5464bb7efa7SGarrett D'Amore 	 */
5474bb7efa7SGarrett D'Amore 	for (tries = 0; tries < 10; tries++) {
5484bb7efa7SGarrett D'Amore 
5494bb7efa7SGarrett D'Amore 		if (slot->s_flags & SLOTF_MMC) {
5504bb7efa7SGarrett D'Amore 			/*
5514bb7efa7SGarrett D'Amore 			 * For MMC, we push the RCA to the MMC.  We
5524bb7efa7SGarrett D'Amore 			 * arbitrarily start at 0x100, and add from
5534bb7efa7SGarrett D'Amore 			 * there.
5544bb7efa7SGarrett D'Amore 			 */
5554bb7efa7SGarrett D'Amore 			rv = sda_init_cmd(slot, CMD_SEND_RCA,
5564bb7efa7SGarrett D'Amore 			    (0x100 + tries) << 16, R1, NULL);
5574bb7efa7SGarrett D'Amore 			if (rv == SDA_EOK)
5584bb7efa7SGarrett D'Amore 				slot->s_rca = 0x100 + tries;
5594bb7efa7SGarrett D'Amore 		} else {
5604bb7efa7SGarrett D'Amore 			/*
5614bb7efa7SGarrett D'Amore 			 * For SDcard, we are basically asking the
5624bb7efa7SGarrett D'Amore 			 * card to propose a value.  It *may* propose
5634bb7efa7SGarrett D'Amore 			 * a value of zero, in which case we will have
5644bb7efa7SGarrett D'Amore 			 * to ask again.
5654bb7efa7SGarrett D'Amore 			 */
5664bb7efa7SGarrett D'Amore 			rv = sda_init_cmd(slot, CMD_SEND_RCA, 0, R6, &resp);
5674bb7efa7SGarrett D'Amore 			if (rv == SDA_EOK)
5684bb7efa7SGarrett D'Amore 				slot->s_rca = resp >> 16;
5694bb7efa7SGarrett D'Amore 		}
5704bb7efa7SGarrett D'Amore 		if ((rv == SDA_EOK) && (slot->s_rca != 0)) {
5714bb7efa7SGarrett D'Amore 			sda_slot_debug(slot, "Relative address (RCA) = %d",
5724bb7efa7SGarrett D'Amore 			    slot->s_rca);
5734bb7efa7SGarrett D'Amore 			return (SDA_EOK);
5744bb7efa7SGarrett D'Amore 		}
5754bb7efa7SGarrett D'Amore 	}
5764bb7efa7SGarrett D'Amore 
5774bb7efa7SGarrett D'Amore 	sda_slot_err(slot, "Unable to negotiate a suitable RCA (%d)", rv);
5784bb7efa7SGarrett D'Amore 	return ((rv != SDA_EOK) ? rv : SDA_EINVAL);
5794bb7efa7SGarrett D'Amore }
5804bb7efa7SGarrett D'Amore 
5814bb7efa7SGarrett D'Amore sda_err_t
sda_init_switch(sda_slot_t * slot,uint8_t mode,uint8_t grp,uint8_t val,uint8_t * data)5824bb7efa7SGarrett D'Amore sda_init_switch(sda_slot_t *slot, uint8_t mode, uint8_t grp, uint8_t val,
5834bb7efa7SGarrett D'Amore     uint8_t *data)
5844bb7efa7SGarrett D'Amore {
5854bb7efa7SGarrett D'Amore 	sda_cmd_t	*cmdp;
5864bb7efa7SGarrett D'Amore 	sda_err_t	errno;
5874bb7efa7SGarrett D'Amore 	uint32_t	arg;
5884bb7efa7SGarrett D'Amore 
5894bb7efa7SGarrett D'Amore 	/*
5904bb7efa7SGarrett D'Amore 	 * The spec says we should leave unselected groups set to 0xf,
5914bb7efa7SGarrett D'Amore 	 * to prevent inadvertent changes.
5924bb7efa7SGarrett D'Amore 	 */
5934bb7efa7SGarrett D'Amore 	arg = (mode << 31) | 0xffffff;
5944bb7efa7SGarrett D'Amore 	arg &= ~(0xf << (grp << 2));
5954bb7efa7SGarrett D'Amore 	arg |= (val << (grp << 2));
5964bb7efa7SGarrett D'Amore 
5974bb7efa7SGarrett D'Amore 	cmdp = sda_cmd_alloc(slot, CMD_SWITCH_FUNC, arg, R1, NULL, KM_SLEEP);
5984bb7efa7SGarrett D'Amore 
5994bb7efa7SGarrett D'Amore 	cmdp->sc_flags |= SDA_CMDF_INIT | SDA_CMDF_DAT | SDA_CMDF_READ;
6004bb7efa7SGarrett D'Amore 	cmdp->sc_blksz = 64;
6014bb7efa7SGarrett D'Amore 	cmdp->sc_nblks = 1;
6024bb7efa7SGarrett D'Amore 	cmdp->sc_kvaddr = (void *)data;
6034bb7efa7SGarrett D'Amore 
6044bb7efa7SGarrett D'Amore 	errno = sda_cmd_exec(slot, cmdp, NULL);
6054bb7efa7SGarrett D'Amore 
6064bb7efa7SGarrett D'Amore 	sda_cmd_free(cmdp);
6074bb7efa7SGarrett D'Amore 
6084bb7efa7SGarrett D'Amore 	return (errno);
6094bb7efa7SGarrett D'Amore 
6104bb7efa7SGarrett D'Amore }
6114bb7efa7SGarrett D'Amore 
6124bb7efa7SGarrett D'Amore sda_err_t
sda_init_highspeed(sda_slot_t * slot)6134bb7efa7SGarrett D'Amore sda_init_highspeed(sda_slot_t *slot)
6144bb7efa7SGarrett D'Amore {
6154bb7efa7SGarrett D'Amore 	uint32_t	ccc;
6164bb7efa7SGarrett D'Amore 	uint8_t		data[64];
6174bb7efa7SGarrett D'Amore 	sda_err_t	rv;
6184bb7efa7SGarrett D'Amore 
6194bb7efa7SGarrett D'Amore 	if ((slot->s_caps & SLOT_CAP_HISPEED) == 0) {
6204bb7efa7SGarrett D'Amore 		return (SDA_EOK);
6214bb7efa7SGarrett D'Amore 	}
6224bb7efa7SGarrett D'Amore 	if ((slot->s_flags & SLOTF_SDMEM) == 0) {
6234bb7efa7SGarrett D'Amore 		return (SDA_EOK);
6244bb7efa7SGarrett D'Amore 	}
6254bb7efa7SGarrett D'Amore 	ccc = sda_mem_getbits(slot->s_rcsd, 95, 12);
6264bb7efa7SGarrett D'Amore 	if ((ccc & (1 << 10)) == 0) {
6274bb7efa7SGarrett D'Amore 		return (SDA_EOK);
6284bb7efa7SGarrett D'Amore 	}
6294bb7efa7SGarrett D'Amore 
6304bb7efa7SGarrett D'Amore 	rv = sda_init_switch(slot, 0, 0, 1, data);
6314bb7efa7SGarrett D'Amore 
6324bb7efa7SGarrett D'Amore 	/* these are big-endian bits, bit 401 */
6334bb7efa7SGarrett D'Amore 	if ((rv != SDA_EOK) || ((data[13] & (1 << 1)) == 0)) {
6344bb7efa7SGarrett D'Amore 		return (SDA_EOK);
6354bb7efa7SGarrett D'Amore 	}
6364bb7efa7SGarrett D'Amore 
6374bb7efa7SGarrett D'Amore 	rv = sda_init_switch(slot, 1, 0, 1, data);
6384bb7efa7SGarrett D'Amore 	if (rv != SDA_EOK) {
6394bb7efa7SGarrett D'Amore 		return (SDA_EOK);
6404bb7efa7SGarrett D'Amore 	}
6414bb7efa7SGarrett D'Amore 
6424bb7efa7SGarrett D'Amore 	/* now program the card */
6434bb7efa7SGarrett D'Amore 	rv = sda_setprop(slot, SDA_PROP_HISPEED, 1);
6444bb7efa7SGarrett D'Amore 	if (rv != SDA_EOK) {
6454bb7efa7SGarrett D'Amore 		sda_slot_err(slot, "Failed setting slot to high speed mode");
6464bb7efa7SGarrett D'Amore 	} else {
6474bb7efa7SGarrett D'Amore 		/* the card should now support 50 MHz */
6484bb7efa7SGarrett D'Amore 		slot->s_maxclk = 50000000;
6494bb7efa7SGarrett D'Amore 	}
6504bb7efa7SGarrett D'Amore 
6514bb7efa7SGarrett D'Amore 	return (rv);
6524bb7efa7SGarrett D'Amore }