mmc: core: Add support for cache ctrl for SD cards
In SD spec v6.x the SD function extension registers for performance enhancements were introduced. As a part of this an optional internal cache on the SD card, can be used to improve performance. The let the SD card use the cache, the host needs to enable it and manage flushing of the cache, so let's add support for this. Note that for an SD card supporting the cache it's mandatory for it, to also support the poweroff notification feature. According to the SD spec, if the cache has been enabled and a poweroff notification is sent to the card, that implicitly also means that the card should flush its internal cache. Therefore, dealing with cache flushing for REQ_OP_FLUSH block requests is sufficient. Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> Reviewed-by: Avri Altman <avri.altman@wdc.com> Link: https://lore.kernel.org/r/20210511101359.83521-1-ulf.hansson@linaro.org
This commit is contained in:
Родитель
34dd3cccca
Коммит
130206a615
|
@ -456,6 +456,7 @@ static int mmc_busy_cb(void *cb_data, bool *busy)
|
|||
err = R1_STATUS(status) ? -EIO : 0;
|
||||
break;
|
||||
case MMC_BUSY_HPI:
|
||||
case MMC_BUSY_EXTR_SINGLE:
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
|
|
|
@ -14,6 +14,7 @@ enum mmc_busy_cmd {
|
|||
MMC_BUSY_CMD6,
|
||||
MMC_BUSY_ERASE,
|
||||
MMC_BUSY_HPI,
|
||||
MMC_BUSY_EXTR_SINGLE,
|
||||
};
|
||||
|
||||
struct mmc_host;
|
||||
|
|
|
@ -67,6 +67,7 @@ static const unsigned int sd_au_size[] = {
|
|||
})
|
||||
|
||||
#define SD_POWEROFF_NOTIFY_TIMEOUT_MS 2000
|
||||
#define SD_WRITE_EXTR_SINGLE_TIMEOUT_MS 1000
|
||||
|
||||
struct sd_busy_data {
|
||||
struct mmc_card *card;
|
||||
|
@ -1287,6 +1288,96 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static bool sd_cache_enabled(struct mmc_host *host)
|
||||
{
|
||||
return host->card->ext_perf.feature_enabled & SD_EXT_PERF_CACHE;
|
||||
}
|
||||
|
||||
static int sd_flush_cache(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_card *card = host->card;
|
||||
u8 *reg_buf, fno, page;
|
||||
u16 offset;
|
||||
int err;
|
||||
|
||||
if (!sd_cache_enabled(host))
|
||||
return 0;
|
||||
|
||||
reg_buf = kzalloc(512, GFP_KERNEL);
|
||||
if (!reg_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Set Flush Cache at bit 0 in the performance enhancement register at
|
||||
* 261 bytes offset.
|
||||
*/
|
||||
fno = card->ext_perf.fno;
|
||||
page = card->ext_perf.page;
|
||||
offset = card->ext_perf.offset + 261;
|
||||
|
||||
err = sd_write_ext_reg(card, fno, page, offset, BIT(0));
|
||||
if (err) {
|
||||
pr_warn("%s: error %d writing Cache Flush bit\n",
|
||||
mmc_hostname(host), err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = mmc_poll_for_busy(card, SD_WRITE_EXTR_SINGLE_TIMEOUT_MS, false,
|
||||
MMC_BUSY_EXTR_SINGLE);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Read the Flush Cache bit. The card shall reset it, to confirm that
|
||||
* it's has completed the flushing of the cache.
|
||||
*/
|
||||
err = sd_read_ext_reg(card, fno, page, offset, 1, reg_buf);
|
||||
if (err) {
|
||||
pr_warn("%s: error %d reading Cache Flush bit\n",
|
||||
mmc_hostname(host), err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (reg_buf[0] & BIT(0))
|
||||
err = -ETIMEDOUT;
|
||||
out:
|
||||
kfree(reg_buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sd_enable_cache(struct mmc_card *card)
|
||||
{
|
||||
u8 *reg_buf;
|
||||
int err;
|
||||
|
||||
card->ext_perf.feature_enabled &= ~SD_EXT_PERF_CACHE;
|
||||
|
||||
reg_buf = kzalloc(512, GFP_KERNEL);
|
||||
if (!reg_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Set Cache Enable at bit 0 in the performance enhancement register at
|
||||
* 260 bytes offset.
|
||||
*/
|
||||
err = sd_write_ext_reg(card, card->ext_perf.fno, card->ext_perf.page,
|
||||
card->ext_perf.offset + 260, BIT(0));
|
||||
if (err) {
|
||||
pr_warn("%s: error %d writing Cache Enable bit\n",
|
||||
mmc_hostname(card->host), err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = mmc_poll_for_busy(card, SD_WRITE_EXTR_SINGLE_TIMEOUT_MS, false,
|
||||
MMC_BUSY_EXTR_SINGLE);
|
||||
if (!err)
|
||||
card->ext_perf.feature_enabled |= SD_EXT_PERF_CACHE;
|
||||
|
||||
out:
|
||||
kfree(reg_buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the detection and initialisation of a card.
|
||||
*
|
||||
|
@ -1442,6 +1533,13 @@ retry:
|
|||
goto free_card;
|
||||
}
|
||||
|
||||
/* Enable internal SD cache if supported. */
|
||||
if (card->ext_perf.feature_support & SD_EXT_PERF_CACHE) {
|
||||
err = sd_enable_cache(card);
|
||||
if (err)
|
||||
goto free_card;
|
||||
}
|
||||
|
||||
if (host->cqe_ops && !host->cqe_enabled) {
|
||||
err = host->cqe_ops->cqe_enable(host, card);
|
||||
if (!err) {
|
||||
|
@ -1694,6 +1792,8 @@ static const struct mmc_bus_ops mmc_sd_ops = {
|
|||
.alive = mmc_sd_alive,
|
||||
.shutdown = mmc_sd_suspend,
|
||||
.hw_reset = mmc_sd_hw_reset,
|
||||
.cache_enabled = sd_cache_enabled,
|
||||
.flush_cache = sd_flush_cache,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -196,6 +196,7 @@ struct sd_ext_reg {
|
|||
u8 page;
|
||||
u16 offset;
|
||||
u8 rev;
|
||||
u8 feature_enabled;
|
||||
u8 feature_support;
|
||||
/* Power Management Function. */
|
||||
#define SD_EXT_POWER_OFF_NOTIFY (1<<0)
|
||||
|
|
Загрузка…
Ссылка в новой задаче