#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/time.h>
#include <dev/mmc/bridge.h>
#include <dev/mmc/mmc_private.h>
#include <dev/mmc/mmc_subr.h>
#include <dev/mmc/mmcreg.h>
#include <dev/mmc/mmcbrvar.h>
#include "mmcbus_if.h"
#define CMD_RETRIES 3
#define LOG_PPS 5
int
mmc_wait_for_cmd(device_t busdev, device_t dev, struct mmc_command *cmd,
int retries)
{
struct mmc_request mreq;
struct mmc_softc *sc;
int err;
do {
memset(&mreq, 0, sizeof(mreq));
memset(cmd->resp, 0, sizeof(cmd->resp));
cmd->retries = 0;
cmd->mrq = &mreq;
if (cmd->data != NULL)
cmd->data->mrq = &mreq;
mreq.cmd = cmd;
if (MMCBUS_WAIT_FOR_REQUEST(busdev, dev, &mreq) != 0)
err = MMC_ERR_FAILED;
else
err = cmd->error;
} while (err != MMC_ERR_NONE && retries-- > 0);
if (err != MMC_ERR_NONE && busdev == dev) {
sc = device_get_softc(busdev);
if (sc->squelched == 0 && ppsratecheck(&sc->log_time,
&sc->log_count, LOG_PPS)) {
device_printf(sc->dev, "CMD%d failed, RESULT: %d\n",
cmd->opcode, err);
}
}
return (err);
}
int
mmc_wait_for_app_cmd(device_t busdev, device_t dev, uint16_t rca,
struct mmc_command *cmd, int retries)
{
struct mmc_command appcmd;
struct mmc_softc *sc;
int err;
sc = device_get_softc(busdev);
cmd->flags |= MMC_CMD_IS_APP;
sc->squelched++;
do {
memset(&appcmd, 0, sizeof(appcmd));
appcmd.opcode = MMC_APP_CMD;
appcmd.arg = (uint32_t)rca << 16;
appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
if (mmc_wait_for_cmd(busdev, dev, &appcmd, 0) != 0)
err = MMC_ERR_FAILED;
else
err = appcmd.error;
if (err == MMC_ERR_NONE) {
if (!(appcmd.resp[0] & R1_APP_CMD))
err = MMC_ERR_FAILED;
else if (mmc_wait_for_cmd(busdev, dev, cmd, 0) != 0)
err = MMC_ERR_FAILED;
else
err = cmd->error;
}
} while (err != MMC_ERR_NONE && retries-- > 0);
sc->squelched--;
if (err != MMC_ERR_NONE && busdev == dev) {
if (sc->squelched == 0 && ppsratecheck(&sc->log_time,
&sc->log_count, LOG_PPS)) {
device_printf(sc->dev, "ACMD%d failed, RESULT: %d\n",
cmd->opcode, err);
}
}
return (err);
}
int
mmc_switch(device_t busdev, device_t dev, uint16_t rca, uint8_t set,
uint8_t index, uint8_t value, u_int timeout, bool status)
{
struct mmc_command cmd;
struct mmc_softc *sc;
int err;
KASSERT(timeout != 0, ("%s: no timeout", __func__));
sc = device_get_softc(busdev);
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = MMC_SWITCH_FUNC;
cmd.arg = (MMC_SWITCH_FUNC_WR << 24) | (index << 16) | (value << 8) |
set;
if (mmcbr_get_caps(busdev) & MMC_CAP_WAIT_WHILE_BUSY &&
timeout > mmcbr_get_max_busy_timeout(busdev))
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
else
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
sc->retune_paused++;
err = mmc_wait_for_cmd(busdev, dev, &cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE || status == false)
goto out;
err = mmc_switch_status(busdev, dev, rca, timeout);
out:
sc->retune_paused--;
return (err);
}
int
mmc_switch_status(device_t busdev, device_t dev, uint16_t rca, u_int timeout)
{
struct timeval cur, end;
int err, crc_timeout;
uint32_t status;
KASSERT(timeout != 0, ("%s: no timeout", __func__));
end.tv_sec = end.tv_usec = 0;
for (;;) {
crc_timeout=0;
do {
err = mmc_send_status(busdev, dev, rca, &status);
crc_timeout++;
} while (err == MMC_ERR_BADCRC && crc_timeout < 10);
if (err != MMC_ERR_NONE)
break;
if (R1_CURRENT_STATE(status) == R1_STATE_TRAN)
break;
getmicrouptime(&cur);
if (end.tv_sec == 0 && end.tv_usec == 0) {
end.tv_usec = timeout;
timevaladd(&end, &cur);
}
if (timevalcmp(&cur, &end, >)) {
err = MMC_ERR_TIMEOUT;
break;
}
}
if (err == MMC_ERR_NONE && (status & R1_SWITCH_ERROR) != 0)
return (MMC_ERR_FAILED);
return (err);
}
int
mmc_send_ext_csd(device_t busdev, device_t dev, uint8_t *rawextcsd)
{
struct mmc_command cmd;
struct mmc_data data;
int err;
memset(&cmd, 0, sizeof(cmd));
memset(&data, 0, sizeof(data));
memset(rawextcsd, 0, MMC_EXTCSD_SIZE);
cmd.opcode = MMC_SEND_EXT_CSD;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
cmd.data = &data;
data.data = rawextcsd;
data.len = MMC_EXTCSD_SIZE;
data.flags = MMC_DATA_READ;
err = mmc_wait_for_cmd(busdev, dev, &cmd, CMD_RETRIES);
return (err);
}
int
mmc_send_status(device_t busdev, device_t dev, uint16_t rca, uint32_t *status)
{
struct mmc_command cmd;
int err;
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = MMC_SEND_STATUS;
cmd.arg = (uint32_t)rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(busdev, dev, &cmd, CMD_RETRIES);
*status = cmd.resp[0];
return (err);
}