4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore/*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * CDDL HEADER START
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore *
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * The contents of this file are subject to the terms of the
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Common Development and Distribution License (the "License").
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * You may not use this file except in compliance with the License.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore *
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * or http://www.opensolaris.org/os/licensing.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * See the License for the specific language governing permissions
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * and limitations under the License.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore *
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * When distributing Covered Code, include this CDDL HEADER in each
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * If applicable, add the following below this CDDL HEADER, with the
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * fields enclosed by brackets "[]" replaced with your own identifying
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * information: Portions Copyright [yyyy] [name of copyright owner]
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore *
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * CDDL HEADER END
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore/*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Use is subject to license terms.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore/*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * SD card initialization support.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore#include <sys/types.h>
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore#include <sys/ddi.h>
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore#include <sys/sunddi.h>
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore#include <sys/sdcard/sda.h>
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore#include <sys/sdcard/sda_impl.h>
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore/*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Local Prototypes.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amorestatic sda_err_t sda_init_mmc(sda_slot_t *);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amorestatic sda_err_t sda_init_sdio(sda_slot_t *);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amorestatic sda_err_t sda_init_sdmem(sda_slot_t *);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amorestatic sda_err_t sda_init_cmd(sda_slot_t *, sda_index_t, uint32_t,
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_rtype_t, uint32_t *);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amorestatic sda_err_t sda_init_acmd(sda_slot_t *, sda_index_t, uint32_t,
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_rtype_t, uint32_t *);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amorestatic sda_err_t sda_init_blocklen(sda_slot_t *);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amorestatic sda_err_t sda_init_width(sda_slot_t *);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amorestatic sda_err_t sda_init_rca(sda_slot_t *);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amorestatic sda_err_t sda_init_ifcond(sda_slot_t *);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amorestatic sda_err_t sda_init_highspeed(sda_slot_t *);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amorestatic sda_err_t sda_init_switch(sda_slot_t *, uint8_t, uint8_t, uint8_t,
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint8_t *);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amorestatic void sda_init_clock(sda_slot_t *, uint32_t);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore/*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Implementation.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_err_t
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_init_cmd(sda_slot_t *slot, sda_index_t cmd, uint32_t arg,
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_rtype_t rtype, uint32_t *resp)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore{
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_cmd_t *cmdp;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_err_t errno;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore cmdp = sda_cmd_alloc(slot, cmd, arg, rtype, NULL, KM_SLEEP);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore cmdp->sc_flags |= SDA_CMDF_INIT;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore errno = sda_cmd_exec(slot, cmdp, resp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_cmd_free(cmdp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (errno);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore}
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_err_t
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_init_acmd(sda_slot_t *slot, sda_index_t cmd, uint32_t arg,
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_rtype_t rtype, uint32_t *resp)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore{
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_cmd_t *cmdp;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_err_t errno;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore cmdp = sda_cmd_alloc_acmd(slot, cmd, arg, rtype, NULL, KM_SLEEP);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore cmdp->sc_flags |= SDA_CMDF_INIT;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore errno = sda_cmd_exec(slot, cmdp, resp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_cmd_free(cmdp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (errno);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore}
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_err_t
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_init_sdio(sda_slot_t *slot)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore{
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_num_io = 0;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * TODO: SDIO: We need to initialize the SDIO OCR register using
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * the special CMD_IO_SEND_OCR (CMD5) command.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore}
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_err_t
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_init_sdmem(sda_slot_t *slot)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore{
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint32_t ocr;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore int count;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_flags &= ~SLOTF_SDMEM;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Try sending the ACMD41 to query the OCR (Op Cond Register).
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (sda_init_acmd(slot, ACMD_SD_SEND_OCR, 0, R3, &ocr) != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Card failed to respond to query, not an SD card?
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * We send GO_IDLE to clear any error status on the
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * card.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore (void) sda_init_cmd(slot, CMD_GO_IDLE, 0, R0, NULL);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Now we have to send our OCR value, along with the HCS (High
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Capacity Support) bit. The HCS bit is required, to
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * activate high capacity cards. We only set the HCS bit if
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * the card responded to CMD8 (SEND_IFCOND), indicating that
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * it supports the new protocol.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore *
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Note that the HCS bit occupies the same location as the CCS bit
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * in the response.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((ocr & slot->s_cur_ocr) == 0) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "SD card not compatible with host");
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_ENOTSUP);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* set the HCS bit if its a ver 2.00 card */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (slot->s_flags & SLOTF_IFCOND) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore ocr |= OCR_CCS;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* make sure card is powered up */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore for (count = 1000000; count != 0; count -= 10000) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint32_t r3;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (sda_init_acmd(slot, ACMD_SD_SEND_OCR, ocr, R3, &r3) != 0) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "SD card failed to power up");
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_ENOTSUP);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* Now check the busy bit */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (r3 & OCR_POWER_UP) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_flags |= SLOTF_SDMEM;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((slot->s_flags & SLOTF_IFCOND) &&
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore (r3 & OCR_CCS)) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_flags |= SLOTF_SDHC;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore } else {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_flags &= ~SLOTF_SDHC;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (0);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore drv_usecwait(10000);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "SD card timed out during power up");
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_ETIME);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore}
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_err_t
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_init_mmc(sda_slot_t *slot)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore{
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint32_t ocr;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore int count;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_flags &= ~SLOTF_MMC;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * If the card has already been identified as an SD card, then
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * cannot also be an MMC card, so don't probe it as such.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (slot->s_flags & SLOTF_SD) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Try sending the CMD1 to query the OCR.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (sda_init_cmd(slot, CMD_SEND_OCR, 0, R3, &ocr) != 0) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Card failed to respond to query, not an MMC card?
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * We send GO_IDLE to clear any error status on the
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * card.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore (void) sda_init_cmd(slot, CMD_GO_IDLE, 0, R0, NULL);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((ocr & slot->s_cur_ocr) == 0) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "MMC card not compatible with host");
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_ENOTSUP);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* make sure card is powered up */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore for (count = 1000000; count != 0; count -= 10000) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint32_t r3;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (sda_init_cmd(slot, CMD_SEND_OCR, ocr, R3, &r3) != 0) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "MMC card failed to power up");
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_ENOTSUP);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* Now check the busy bit */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (r3 & OCR_POWER_UP) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_flags |= SLOTF_MMC;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore drv_usecwait(10000);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "MMC card timed out during power up");
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_ETIME);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore}
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_err_t
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_init_card(sda_slot_t *slot)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore{
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore int rv;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint32_t resp;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint32_t val;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Power off slot/card initially.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_power_off(slot);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Apply initial power to the slot.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((rv = sda_slot_power_on(slot)) != 0) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (rv);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * First enable the clock, but only at 400 kHz. All cards are
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * supposed to be able to operate between this speed and 100
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * kHz, and all hosts must be able to pick a speed between 100
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * kHz and 400 kHz.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore *
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Once we know what the device can support, then we speed up.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_init_clock(slot, 400000);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((rv = sda_init_ifcond(slot)) != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore goto done;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (((rv = sda_init_sdio(slot)) != SDA_EOK) ||
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore ((rv = sda_init_sdmem(slot)) != SDA_EOK) ||
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore ((rv = sda_init_mmc(slot)) != SDA_EOK)) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* message will already have been logged */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore goto done;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((slot->s_flags & (SLOTF_MEMORY | SLOTF_SDIO)) == 0) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "Unidentified card type");
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = SDA_ENOTSUP;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore goto done;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Memory cards need to obtain their CID before getting their RCA.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * This is a requirement for the state transitions... they go thru
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * the ident state, unlike SDIO cards.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (slot->s_flags & SLOTF_MEMORY) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = sda_init_cmd(slot, CMD_BCAST_CID, 0, R2, slot->s_rcid);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (rv != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "Failed getting card CID (%d)", rv);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore goto done;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((rv = sda_init_rca(slot)) != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore goto done;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_maxclk = 0xffffffffU; /* special sentinel */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Figure out card supported bus width and speed.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore *
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * TODO: SDIO: For IO cards, we have to check what speed the card
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * supports by looking in the CCCR_CAPAB register. (SDIO cards
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * can go low-speed only, full-speed, or high-speed.)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (slot->s_flags & SLOTF_MEMORY) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * We need to obtain the CSD.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = sda_init_cmd(slot, CMD_SEND_CSD, slot->s_rca << 16, R2,
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_rcsd);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (rv != 0) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "Failed getting card CSD (%d)", rv);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore goto done;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Calculate the maxclock.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_maxclk = sda_mem_maxclk(slot);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (((slot->s_flags & SLOTF_SDMEM) != 0) &&
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore ((slot->s_caps & SLOT_CAP_4BITS) != 0)) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_flags |= SLOTF_4BITS;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (slot->s_flags & SLOTF_SDIO) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_debug(slot, "Wide SDIO bus not yet supported");
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_flags &= ~SLOTF_4BITS;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Now select the card.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((rv = sda_init_cmd(slot, CMD_SELECT_CARD, slot->s_rca << 16,
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore R1b, &resp)) != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "Failed selecting card (%d, %x)", rv, resp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore goto done;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((rv = sda_init_highspeed(slot)) != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore goto done;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_init_clock(slot, slot->s_maxclk);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Lets go to 4-bit bus mode, if possible.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((rv = sda_init_width(slot)) != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore goto done;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((rv = sda_init_blocklen(slot)) != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore goto done;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* note if a card is writable */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((sda_getprop(slot, SDA_PROP_WPROTECT, &val) == SDA_EOK) &&
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore (val == 0)) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_flags |= SLOTF_WRITABLE;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = SDA_EOK;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoredone:
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_enter(slot);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_init = B_FALSE;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_exit(slot);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_wakeup(slot);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (rv);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore}
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_err_t
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_init_blocklen(sda_slot_t *slot)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore{
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore int rv;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint32_t resp;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((slot->s_flags & SLOTF_MEMORY) == 0) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * All memory cards support block sizes of 512. Full stop.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = sda_init_cmd(slot, CMD_SET_BLOCKLEN, 512, R1, &resp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (rv != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "Unable to set block length (%d, %x)",
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv, resp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (rv);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore}
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amorevoid
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_init_clock(sda_slot_t *slot, uint32_t hz)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore{
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore int rv;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint32_t act;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Note that at no time is a failure programming the clock
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * itself necessarily a fatal error. Although if the clock
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * wasn't programmed, other things will probably not work during
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * initialization.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((rv = sda_setprop(slot, SDA_PROP_CLOCK, hz)) != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "Failed setting clock to %u Hz (%d)",
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore hz, rv);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* XXX: FMA fail the slot */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
f2b90c3c415ff04d4adb3a54242822b41d74bfd9Garrett D'Amore rv = sda_getprop(slot, SDA_PROP_CLOCK, &act);
f2b90c3c415ff04d4adb3a54242822b41d74bfd9Garrett D'Amore sda_slot_debug(slot,
f2b90c3c415ff04d4adb3a54242822b41d74bfd9Garrett D'Amore rv == SDA_EOK ? "Clock set to %u Hz (requested %u Hz)" :
f2b90c3c415ff04d4adb3a54242822b41d74bfd9Garrett D'Amore "Clock frequency unknown (good luck).", act, hz);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * For now, just wait 10msec for clocks to stabilize to the
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * card. (Is this really necessary?)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore delay(drv_usectohz(10000));
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore}
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_err_t
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_init_width(sda_slot_t *slot)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore{
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore int rv;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint32_t resp;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Spec says we should command the card first.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = sda_setprop(slot, SDA_PROP_BUSWIDTH, 1);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (rv != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "Unable to set slot 1-bit mode (%d)", rv);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (rv);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((slot->s_flags & SLOTF_4BITS) == 0) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * TODO: SDIO: SDIO cards set the CCCR_BUS_WIDTH
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * and CCCR_CD_DISABLE bits here.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * If we're going to use all 4 pins, we really need to disconnect
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * the card pullup resistor. A consquence of this, is that hosts
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * which use that resistor for detection must not claim to support
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * 4-bit bus mode. This is a limitation of our implementation.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = sda_init_acmd(slot, ACMD_SET_CLR_CARD_DETECT, 1, R1, &resp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (rv != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot,
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore "Unable disconnect DAT3 resistor on card (%d, %x)",
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv, resp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* non-fatal error, muddle on */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = sda_init_acmd(slot, ACMD_SET_BUS_WIDTH, 2, R1, &resp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (rv != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "Unable to set card 4-bit mode (%d, %x)",
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv, resp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* non-fatal error, muddle on */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = sda_setprop(slot, SDA_PROP_BUSWIDTH, 4);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (rv != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * This is bad news. We've already asked for the card to
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * to use 4-bit mode, but the host is not complying. It
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * shouldn't ever happen, so we just error out.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "Unable to set slot 4-bit mode (%d)", rv);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (rv);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore}
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_err_t
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_init_ifcond(sda_slot_t *slot)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore{
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore int rv;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore int tries;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint32_t vchk;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint32_t resp;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Try SEND_IF_COND. Note that this assumes that the host is
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * supplying 2.7 - 3.6 voltage range. The standard is not
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * defined for any other ranges.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore vchk = R7_VHS_27_36V | R7_PATTERN;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* we try this a few times, just to be sure */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore for (tries = 0; tries < 5; tries++) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = sda_init_cmd(slot, CMD_GO_IDLE, 0, R0, NULL);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (rv != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "Failed to IDLE card");
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (rv);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = sda_init_cmd(slot, CMD_SEND_IF_COND, vchk, R7, &resp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (rv == SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore break;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore delay(drv_usectohz(10000));
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (rv != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore (void) sda_init_cmd(slot, CMD_GO_IDLE, 0, R0, NULL);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_flags &= ~SLOTF_IFCOND;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore } else if (resp != vchk) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "Card voltages incompatible! (%x)", resp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_ENOTSUP);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore } else {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* SDHC compliant */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_flags |= SLOTF_IFCOND;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore}
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_err_t
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_init_rca(sda_slot_t *slot)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore{
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore int rv;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore int tries;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint32_t resp;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * Program the RCA. Note that MMC has a different mechanism
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * for this.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore for (tries = 0; tries < 10; tries++) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (slot->s_flags & SLOTF_MMC) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * For MMC, we push the RCA to the MMC. We
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * arbitrarily start at 0x100, and add from
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * there.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = sda_init_cmd(slot, CMD_SEND_RCA,
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore (0x100 + tries) << 16, R1, NULL);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (rv == SDA_EOK)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_rca = 0x100 + tries;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore } else {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * For SDcard, we are basically asking the
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * card to propose a value. It *may* propose
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * a value of zero, in which case we will have
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * to ask again.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = sda_init_cmd(slot, CMD_SEND_RCA, 0, R6, &resp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (rv == SDA_EOK)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_rca = resp >> 16;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((rv == SDA_EOK) && (slot->s_rca != 0)) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_debug(slot, "Relative address (RCA) = %d",
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_rca);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "Unable to negotiate a suitable RCA (%d)", rv);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return ((rv != SDA_EOK) ? rv : SDA_EINVAL);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore}
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_err_t
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_init_switch(sda_slot_t *slot, uint8_t mode, uint8_t grp, uint8_t val,
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint8_t *data)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore{
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_cmd_t *cmdp;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_err_t errno;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint32_t arg;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /*
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * The spec says we should leave unselected groups set to 0xf,
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore * to prevent inadvertent changes.
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore arg = (mode << 31) | 0xffffff;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore arg &= ~(0xf << (grp << 2));
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore arg |= (val << (grp << 2));
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore cmdp = sda_cmd_alloc(slot, CMD_SWITCH_FUNC, arg, R1, NULL, KM_SLEEP);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore cmdp->sc_flags |= SDA_CMDF_INIT | SDA_CMDF_DAT | SDA_CMDF_READ;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore cmdp->sc_blksz = 64;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore cmdp->sc_nblks = 1;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore cmdp->sc_kvaddr = (void *)data;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore errno = sda_cmd_exec(slot, cmdp, NULL);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_cmd_free(cmdp);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (errno);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore}
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_err_t
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amoresda_init_highspeed(sda_slot_t *slot)
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore{
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint32_t ccc;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore uint8_t data[64];
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_err_t rv;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((slot->s_caps & SLOT_CAP_HISPEED) == 0) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((slot->s_flags & SLOTF_SDMEM) == 0) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore ccc = sda_mem_getbits(slot->s_rcsd, 95, 12);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((ccc & (1 << 10)) == 0) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = sda_init_switch(slot, 0, 0, 1, data);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* these are big-endian bits, bit 401 */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if ((rv != SDA_EOK) || ((data[13] & (1 << 1)) == 0)) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = sda_init_switch(slot, 1, 0, 1, data);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (rv != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (SDA_EOK);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* now program the card */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore rv = sda_setprop(slot, SDA_PROP_HISPEED, 1);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore if (rv != SDA_EOK) {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore sda_slot_err(slot, "Failed setting slot to high speed mode");
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore } else {
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore /* the card should now support 50 MHz */
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore slot->s_maxclk = 50000000;
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore }
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore return (rv);
4bb7efa72ed531c10f097919636e67724ec4c25aGarrett D'Amore}