#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/time.h>
#include <sys/bus.h>
#include <bus/mmc/bridge.h>
#include <bus/mmc/mmc_private.h>
#include <bus/mmc/mmc_subr.h>
#include <bus/mmc/mmcreg.h>
#include <bus/mmc/mmcbrvar.h>
#include "mmcbus_if.h"
#define CMD_RETRIES 3
#define LOG_PPS 5
int
mmc_wait_for_cmd(device_t brdev, device_t reqdev, 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(brdev, reqdev, &mreq) != 0)
err = MMC_ERR_FAILED;
else
err = cmd->error;
} while (err != MMC_ERR_NONE && retries-- > 0);
if (err != MMC_ERR_NONE && brdev == reqdev) {
sc = device_get_softc(brdev);
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 brdev, device_t reqdev, uint16_t rca,
struct mmc_command *cmd, int retries)
{
struct mmc_command appcmd;
struct mmc_softc *sc;
int err;
sc = device_get_softc(brdev);
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(brdev, reqdev, &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(brdev, reqdev, 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 && brdev == reqdev) {
sc = device_get_softc(brdev);
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 brdev, device_t reqdev, uint16_t rca, uint8_t set,
uint8_t index, uint8_t value, u_int timeout, bool status)
{
struct mmc_command cmd;
int err;
KASSERT(timeout != 0, ("%s: no timeout", __func__));
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(brdev) & MMC_CAP_WAIT_WHILE_BUSY &&
timeout > mmcbr_get_max_busy_timeout(brdev))
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
else
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
err = mmc_wait_for_cmd(brdev, reqdev, &cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE || status == false)
return (err);
return (mmc_switch_status(brdev, reqdev, rca, timeout));
}
int
mmc_switch_status(device_t brdev, device_t reqdev, uint16_t rca, u_int timeout)
{
struct timeval cur, end;
int err;
uint32_t status;
KASSERT(timeout != 0, ("%s: no timeout", __func__));
end.tv_sec = end.tv_usec = 0;
for (;;) {
err = mmc_send_status(brdev, reqdev, rca, &status);
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 && R1_CURRENT_STATE(status) == R1_SWITCH_ERROR)
return (MMC_ERR_FAILED);
return (err);
}
int
mmc_send_ext_csd(device_t brdev, device_t reqdev, 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(brdev, reqdev, &cmd, CMD_RETRIES);
return (err);
}
int
mmc_send_status(device_t brdev, device_t reqdev, 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(brdev, reqdev, &cmd, CMD_RETRIES);
*status = cmd.resp[0];
return (err);
}