mmc: block: Add CMD13 polling for MMC IOCTLS with R1B response
MMC IOCTLS with R1B responses may cause the card to enter the busy state, which means it's not ready to receive a new request. To prevent new requests from being sent to the card, use a CMD13 polling loop to verify that the card returns to the transfer state, before completing the request. Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com> Reviewed-by: Avri Altman <avri.altman@wdc.com> Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
3869468e0c
commit
a0d4c7eb71
1 changed files with 55 additions and 92 deletions
|
@ -408,38 +408,6 @@ static int mmc_blk_ioctl_copy_to_user(struct mmc_ioc_cmd __user *ic_ptr,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status,
|
|
||||||
u32 retries_max)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
u32 retry_count = 0;
|
|
||||||
|
|
||||||
if (!status || !retries_max)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
do {
|
|
||||||
err = __mmc_send_status(card, status, 5);
|
|
||||||
if (err)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (!R1_STATUS(*status) &&
|
|
||||||
(R1_CURRENT_STATE(*status) != R1_STATE_PRG))
|
|
||||||
break; /* RPMB programming operation complete */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Rechedule to give the MMC device a chance to continue
|
|
||||||
* processing the previous command without being polled too
|
|
||||||
* frequently.
|
|
||||||
*/
|
|
||||||
usleep_range(1000, 5000);
|
|
||||||
} while (++retry_count < retries_max);
|
|
||||||
|
|
||||||
if (retry_count == retries_max)
|
|
||||||
err = -EPERM;
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ioctl_do_sanitize(struct mmc_card *card)
|
static int ioctl_do_sanitize(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
@ -468,6 +436,58 @@ out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool mmc_blk_in_tran_state(u32 status)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Some cards mishandle the status bits, so make sure to check both the
|
||||||
|
* busy indication and the card state.
|
||||||
|
*/
|
||||||
|
return status & R1_READY_FOR_DATA &&
|
||||||
|
(R1_CURRENT_STATE(status) == R1_STATE_TRAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
|
||||||
|
u32 *resp_errs)
|
||||||
|
{
|
||||||
|
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
|
||||||
|
int err = 0;
|
||||||
|
u32 status;
|
||||||
|
|
||||||
|
do {
|
||||||
|
bool done = time_after(jiffies, timeout);
|
||||||
|
|
||||||
|
err = __mmc_send_status(card, &status, 5);
|
||||||
|
if (err) {
|
||||||
|
dev_err(mmc_dev(card->host),
|
||||||
|
"error %d requesting status\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accumulate any response error bits seen */
|
||||||
|
if (resp_errs)
|
||||||
|
*resp_errs |= status;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Timeout if the device never becomes ready for data and never
|
||||||
|
* leaves the program state.
|
||||||
|
*/
|
||||||
|
if (done) {
|
||||||
|
dev_err(mmc_dev(card->host),
|
||||||
|
"Card stuck in wrong state! %s status: %#x\n",
|
||||||
|
__func__, status);
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some cards mishandle the status bits,
|
||||||
|
* so make sure to check both the busy
|
||||||
|
* indication and the card state.
|
||||||
|
*/
|
||||||
|
} while (!mmc_blk_in_tran_state(status));
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
||||||
struct mmc_blk_ioc_data *idata)
|
struct mmc_blk_ioc_data *idata)
|
||||||
{
|
{
|
||||||
|
@ -477,7 +497,6 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
int err;
|
int err;
|
||||||
unsigned int target_part;
|
unsigned int target_part;
|
||||||
u32 status = 0;
|
|
||||||
|
|
||||||
if (!card || !md || !idata)
|
if (!card || !md || !idata)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -611,16 +630,12 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
||||||
|
|
||||||
memcpy(&(idata->ic.response), cmd.resp, sizeof(cmd.resp));
|
memcpy(&(idata->ic.response), cmd.resp, sizeof(cmd.resp));
|
||||||
|
|
||||||
if (idata->rpmb) {
|
if (idata->rpmb || (cmd.flags & MMC_RSP_R1B)) {
|
||||||
/*
|
/*
|
||||||
* Ensure RPMB command has completed by polling CMD13
|
* Ensure RPMB/R1B command has completed by polling CMD13
|
||||||
* "Send Status".
|
* "Send Status".
|
||||||
*/
|
*/
|
||||||
err = ioctl_rpmb_card_status_poll(card, &status, 5);
|
err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, NULL);
|
||||||
if (err)
|
|
||||||
dev_err(mmc_dev(card->host),
|
|
||||||
"%s: Card Status=0x%08X, error %d\n",
|
|
||||||
__func__, status, err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -970,58 +985,6 @@ static unsigned int mmc_blk_data_timeout_ms(struct mmc_host *host,
|
||||||
return ms;
|
return ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool mmc_blk_in_tran_state(u32 status)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Some cards mishandle the status bits, so make sure to check both the
|
|
||||||
* busy indication and the card state.
|
|
||||||
*/
|
|
||||||
return status & R1_READY_FOR_DATA &&
|
|
||||||
(R1_CURRENT_STATE(status) == R1_STATE_TRAN);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
|
|
||||||
u32 *resp_errs)
|
|
||||||
{
|
|
||||||
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
|
|
||||||
int err = 0;
|
|
||||||
u32 status;
|
|
||||||
|
|
||||||
do {
|
|
||||||
bool done = time_after(jiffies, timeout);
|
|
||||||
|
|
||||||
err = __mmc_send_status(card, &status, 5);
|
|
||||||
if (err) {
|
|
||||||
dev_err(mmc_dev(card->host),
|
|
||||||
"error %d requesting status\n", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Accumulate any response error bits seen */
|
|
||||||
if (resp_errs)
|
|
||||||
*resp_errs |= status;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Timeout if the device never becomes ready for data and never
|
|
||||||
* leaves the program state.
|
|
||||||
*/
|
|
||||||
if (done) {
|
|
||||||
dev_err(mmc_dev(card->host),
|
|
||||||
"Card stuck in wrong state! %s status: %#x\n",
|
|
||||||
__func__, status);
|
|
||||||
return -ETIMEDOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Some cards mishandle the status bits,
|
|
||||||
* so make sure to check both the busy
|
|
||||||
* indication and the card state.
|
|
||||||
*/
|
|
||||||
} while (!mmc_blk_in_tran_state(status));
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host,
|
static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host,
|
||||||
int type)
|
int type)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue