MMC core:
- mmc: core: Avoid hang when claiming host MMC host: - dw_mmc: Avoid hang when accessing registers - dw_mmc: Fix out-of-bounds access for slot's caps - dw_mmc-k3: Fix out-of-bounds access through DT alias - sdhci-pci: Fix S0i3 for Intel BYT-based controllers -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJamTk2AAoJEP4mhCVzWIwp108P/RcicjtOpcvTKMYnt7l6hAQ6 1Qr4tjAtj75FSVZooEKo1B13GTJOqgGh3O5x1nEcCGI2iA8ZV2XclN1QZhJeVJcF 5B1/hAXMjRaUkXzUrD9pOi+m+s3IBK82hJ40ac4JHYUpRT7fRLkB1PdU0gV3V9yZ Uy967pL9spHMbgDDbGut2gnBt9MyTysRfvCnEUKPKvPPqL6QaOGNEEIlXQedrSoi vUvvQjpAAUMd3kDWhnxNileLyHUFatpaJOYtxfTWiXNgP9LHv1o1X2QbbagnXaqY AybOjvWinbKoaX6+5G+ZZJMrW14D6gG9AZ/vi57U7ta8NiKRWmn0KjzTk0VVKsbo 5oKqjRT3Hu/SCqyqhVvo8dY+HSqhv0v1EtXVjfI1D8mxBt4v4fwnQrzNr9dBCouD Lb+l6hnZJq/xq3BDF+pZk0HiErGdXrXeq/pEKZXRL4EvX6TF0YZhc4VIyShDmRa5 5ZNgCndkUQ9Y/LYYnuwDvqkqlysR49FTCw5fGZDxtJzud6rupgF+/0QGwyGjGCZk 4Add0EcTKgWsSBcVwkyRJbDTmQB4tSVwAGMjAnlMJW3YX8chuzg2//DuW3DZLRao TH0PMPYTQeWclsz3ySBE/FU9IQhSQ44s0VUO+G+XxtfffaqOcMVs5D2ZKTjk8jWD lvdznNBylRrleG9K5zcA =pILk -----END PGP SIGNATURE----- Merge tag 'mmc-v4.16-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc Pull MMC fixes from Ulf Hansson: "MMC core: - mmc: core: Avoid hang when claiming host MMC host: - dw_mmc: Avoid hang when accessing registers - dw_mmc: Fix out-of-bounds access for slot's caps - dw_mmc-k3: Fix out-of-bounds access through DT alias - sdhci-pci: Fix S0i3 for Intel BYT-based controllers" * tag 'mmc-v4.16-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: mmc: core: Avoid hanging to claim host for mmc via some nested calls mmc: dw_mmc: Avoid accessing registers in runtime suspended state mmc: dw_mmc: Fix out-of-bounds access for slot's caps mmc: dw_mmc: Factor out dw_mci_init_slot_caps mmc: dw_mmc-k3: Fix out-of-bounds access through DT alias mmc: sdhci-pci: Fix S0i3 for Intel BYT-based controllers
This commit is contained in:
commit
ff06b55ec4
|
@ -848,7 +848,6 @@ int mmc_interrupt_hpi(struct mmc_card *card)
|
|||
return 1;
|
||||
}
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
err = mmc_send_status(card, &status);
|
||||
if (err) {
|
||||
pr_err("%s: Get card status fail\n", mmc_hostname(card->host));
|
||||
|
@ -890,7 +889,6 @@ int mmc_interrupt_hpi(struct mmc_card *card)
|
|||
} while (!err);
|
||||
|
||||
out:
|
||||
mmc_release_host(card->host);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -932,9 +930,7 @@ static int mmc_read_bkops_status(struct mmc_card *card)
|
|||
int err;
|
||||
u8 *ext_csd;
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
err = mmc_get_ext_csd(card, &ext_csd);
|
||||
mmc_release_host(card->host);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
|
@ -487,6 +487,7 @@ static unsigned long exynos_dwmmc_caps[4] = {
|
|||
|
||||
static const struct dw_mci_drv_data exynos_drv_data = {
|
||||
.caps = exynos_dwmmc_caps,
|
||||
.num_caps = ARRAY_SIZE(exynos_dwmmc_caps),
|
||||
.init = dw_mci_exynos_priv_init,
|
||||
.set_ios = dw_mci_exynos_set_ios,
|
||||
.parse_dt = dw_mci_exynos_parse_dt,
|
||||
|
|
|
@ -135,6 +135,9 @@ static int dw_mci_hi6220_parse_dt(struct dw_mci *host)
|
|||
if (priv->ctrl_id < 0)
|
||||
priv->ctrl_id = 0;
|
||||
|
||||
if (priv->ctrl_id >= TIMING_MODE)
|
||||
return -EINVAL;
|
||||
|
||||
host->priv = priv;
|
||||
return 0;
|
||||
}
|
||||
|
@ -207,6 +210,7 @@ static int dw_mci_hi6220_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
|
|||
|
||||
static const struct dw_mci_drv_data hi6220_data = {
|
||||
.caps = dw_mci_hi6220_caps,
|
||||
.num_caps = ARRAY_SIZE(dw_mci_hi6220_caps),
|
||||
.switch_voltage = dw_mci_hi6220_switch_voltage,
|
||||
.set_ios = dw_mci_hi6220_set_ios,
|
||||
.parse_dt = dw_mci_hi6220_parse_dt,
|
||||
|
|
|
@ -319,6 +319,7 @@ static const struct dw_mci_drv_data rk2928_drv_data = {
|
|||
|
||||
static const struct dw_mci_drv_data rk3288_drv_data = {
|
||||
.caps = dw_mci_rk3288_dwmmc_caps,
|
||||
.num_caps = ARRAY_SIZE(dw_mci_rk3288_dwmmc_caps),
|
||||
.set_ios = dw_mci_rk3288_set_ios,
|
||||
.execute_tuning = dw_mci_rk3288_execute_tuning,
|
||||
.parse_dt = dw_mci_rk3288_parse_dt,
|
||||
|
|
|
@ -195,6 +195,7 @@ static unsigned long zx_dwmmc_caps[3] = {
|
|||
|
||||
static const struct dw_mci_drv_data zx_drv_data = {
|
||||
.caps = zx_dwmmc_caps,
|
||||
.num_caps = ARRAY_SIZE(zx_dwmmc_caps),
|
||||
.execute_tuning = dw_mci_zx_execute_tuning,
|
||||
.prepare_hs400_tuning = dw_mci_zx_prepare_hs400_tuning,
|
||||
.parse_dt = dw_mci_zx_parse_dt,
|
||||
|
|
|
@ -165,6 +165,8 @@ static int dw_mci_regs_show(struct seq_file *s, void *v)
|
|||
{
|
||||
struct dw_mci *host = s->private;
|
||||
|
||||
pm_runtime_get_sync(host->dev);
|
||||
|
||||
seq_printf(s, "STATUS:\t0x%08x\n", mci_readl(host, STATUS));
|
||||
seq_printf(s, "RINTSTS:\t0x%08x\n", mci_readl(host, RINTSTS));
|
||||
seq_printf(s, "CMD:\t0x%08x\n", mci_readl(host, CMD));
|
||||
|
@ -172,6 +174,8 @@ static int dw_mci_regs_show(struct seq_file *s, void *v)
|
|||
seq_printf(s, "INTMASK:\t0x%08x\n", mci_readl(host, INTMASK));
|
||||
seq_printf(s, "CLKENA:\t0x%08x\n", mci_readl(host, CLKENA));
|
||||
|
||||
pm_runtime_put_autosuspend(host->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2778,12 +2782,57 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int dw_mci_init_slot_caps(struct dw_mci_slot *slot)
|
||||
{
|
||||
struct dw_mci *host = slot->host;
|
||||
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
struct mmc_host *mmc = slot->mmc;
|
||||
int ctrl_id;
|
||||
|
||||
if (host->pdata->caps)
|
||||
mmc->caps = host->pdata->caps;
|
||||
|
||||
/*
|
||||
* Support MMC_CAP_ERASE by default.
|
||||
* It needs to use trim/discard/erase commands.
|
||||
*/
|
||||
mmc->caps |= MMC_CAP_ERASE;
|
||||
|
||||
if (host->pdata->pm_caps)
|
||||
mmc->pm_caps = host->pdata->pm_caps;
|
||||
|
||||
if (host->dev->of_node) {
|
||||
ctrl_id = of_alias_get_id(host->dev->of_node, "mshc");
|
||||
if (ctrl_id < 0)
|
||||
ctrl_id = 0;
|
||||
} else {
|
||||
ctrl_id = to_platform_device(host->dev)->id;
|
||||
}
|
||||
|
||||
if (drv_data && drv_data->caps) {
|
||||
if (ctrl_id >= drv_data->num_caps) {
|
||||
dev_err(host->dev, "invalid controller id %d\n",
|
||||
ctrl_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
mmc->caps |= drv_data->caps[ctrl_id];
|
||||
}
|
||||
|
||||
if (host->pdata->caps2)
|
||||
mmc->caps2 = host->pdata->caps2;
|
||||
|
||||
/* Process SDIO IRQs through the sdio_irq_work. */
|
||||
if (mmc->caps & MMC_CAP_SDIO_IRQ)
|
||||
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_init_slot(struct dw_mci *host)
|
||||
{
|
||||
struct mmc_host *mmc;
|
||||
struct dw_mci_slot *slot;
|
||||
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
int ctrl_id, ret;
|
||||
int ret;
|
||||
u32 freq[2];
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
|
||||
|
@ -2817,38 +2866,13 @@ static int dw_mci_init_slot(struct dw_mci *host)
|
|||
if (!mmc->ocr_avail)
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
|
||||
if (host->pdata->caps)
|
||||
mmc->caps = host->pdata->caps;
|
||||
|
||||
/*
|
||||
* Support MMC_CAP_ERASE by default.
|
||||
* It needs to use trim/discard/erase commands.
|
||||
*/
|
||||
mmc->caps |= MMC_CAP_ERASE;
|
||||
|
||||
if (host->pdata->pm_caps)
|
||||
mmc->pm_caps = host->pdata->pm_caps;
|
||||
|
||||
if (host->dev->of_node) {
|
||||
ctrl_id = of_alias_get_id(host->dev->of_node, "mshc");
|
||||
if (ctrl_id < 0)
|
||||
ctrl_id = 0;
|
||||
} else {
|
||||
ctrl_id = to_platform_device(host->dev)->id;
|
||||
}
|
||||
if (drv_data && drv_data->caps)
|
||||
mmc->caps |= drv_data->caps[ctrl_id];
|
||||
|
||||
if (host->pdata->caps2)
|
||||
mmc->caps2 = host->pdata->caps2;
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret)
|
||||
goto err_host_allocated;
|
||||
|
||||
/* Process SDIO IRQs through the sdio_irq_work. */
|
||||
if (mmc->caps & MMC_CAP_SDIO_IRQ)
|
||||
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
|
||||
ret = dw_mci_init_slot_caps(slot);
|
||||
if (ret)
|
||||
goto err_host_allocated;
|
||||
|
||||
/* Useful defaults if platform data is unset. */
|
||||
if (host->use_dma == TRANS_MODE_IDMAC) {
|
||||
|
|
|
@ -543,6 +543,7 @@ struct dw_mci_slot {
|
|||
/**
|
||||
* dw_mci driver data - dw-mshc implementation specific driver data.
|
||||
* @caps: mmc subsystem specified capabilities of the controller(s).
|
||||
* @num_caps: number of capabilities specified by @caps.
|
||||
* @init: early implementation specific initialization.
|
||||
* @set_ios: handle bus specific extensions.
|
||||
* @parse_dt: parse implementation specific device tree properties.
|
||||
|
@ -554,6 +555,7 @@ struct dw_mci_slot {
|
|||
*/
|
||||
struct dw_mci_drv_data {
|
||||
unsigned long *caps;
|
||||
u32 num_caps;
|
||||
int (*init)(struct dw_mci *host);
|
||||
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
|
||||
int (*parse_dt)(struct dw_mci *host);
|
||||
|
|
|
@ -654,9 +654,36 @@ static void byt_read_dsm(struct sdhci_pci_slot *slot)
|
|||
slot->chip->rpm_retune = intel_host->d3_retune;
|
||||
}
|
||||
|
||||
static int intel_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
{
|
||||
int err = sdhci_execute_tuning(mmc, opcode);
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Tuning can leave the IP in an active state (Buffer Read Enable bit
|
||||
* set) which prevents the entry to low power states (i.e. S0i3). Data
|
||||
* reset will clear it.
|
||||
*/
|
||||
sdhci_reset(host, SDHCI_RESET_DATA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void byt_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
struct mmc_host_ops *ops = &slot->host->mmc_host_ops;
|
||||
|
||||
byt_read_dsm(slot);
|
||||
|
||||
ops->execute_tuning = intel_execute_tuning;
|
||||
}
|
||||
|
||||
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
byt_read_dsm(slot);
|
||||
byt_probe_slot(slot);
|
||||
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
|
||||
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
|
||||
MMC_CAP_CMD_DURING_TFR |
|
||||
|
@ -779,7 +806,7 @@ static int ni_byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
|
|||
{
|
||||
int err;
|
||||
|
||||
byt_read_dsm(slot);
|
||||
byt_probe_slot(slot);
|
||||
|
||||
err = ni_set_max_freq(slot);
|
||||
if (err)
|
||||
|
@ -792,7 +819,7 @@ static int ni_byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
|
|||
|
||||
static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
byt_read_dsm(slot);
|
||||
byt_probe_slot(slot);
|
||||
slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE |
|
||||
MMC_CAP_WAIT_WHILE_BUSY;
|
||||
return 0;
|
||||
|
@ -800,7 +827,7 @@ static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
|
|||
|
||||
static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
byt_read_dsm(slot);
|
||||
byt_probe_slot(slot);
|
||||
slot->host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY |
|
||||
MMC_CAP_AGGRESSIVE_PM | MMC_CAP_CD_WAKE;
|
||||
slot->cd_idx = 0;
|
||||
|
|
Loading…
Reference in New Issue