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:
Linus Torvalds 2018-03-02 08:44:11 -08:00
commit ff06b55ec4
8 changed files with 94 additions and 38 deletions

View File

@ -848,7 +848,6 @@ int mmc_interrupt_hpi(struct mmc_card *card)
return 1; return 1;
} }
mmc_claim_host(card->host);
err = mmc_send_status(card, &status); err = mmc_send_status(card, &status);
if (err) { if (err) {
pr_err("%s: Get card status fail\n", mmc_hostname(card->host)); 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); } while (!err);
out: out:
mmc_release_host(card->host);
return err; return err;
} }
@ -932,9 +930,7 @@ static int mmc_read_bkops_status(struct mmc_card *card)
int err; int err;
u8 *ext_csd; u8 *ext_csd;
mmc_claim_host(card->host);
err = mmc_get_ext_csd(card, &ext_csd); err = mmc_get_ext_csd(card, &ext_csd);
mmc_release_host(card->host);
if (err) if (err)
return err; return err;

View File

@ -487,6 +487,7 @@ static unsigned long exynos_dwmmc_caps[4] = {
static const struct dw_mci_drv_data exynos_drv_data = { static const struct dw_mci_drv_data exynos_drv_data = {
.caps = exynos_dwmmc_caps, .caps = exynos_dwmmc_caps,
.num_caps = ARRAY_SIZE(exynos_dwmmc_caps),
.init = dw_mci_exynos_priv_init, .init = dw_mci_exynos_priv_init,
.set_ios = dw_mci_exynos_set_ios, .set_ios = dw_mci_exynos_set_ios,
.parse_dt = dw_mci_exynos_parse_dt, .parse_dt = dw_mci_exynos_parse_dt,

View File

@ -135,6 +135,9 @@ static int dw_mci_hi6220_parse_dt(struct dw_mci *host)
if (priv->ctrl_id < 0) if (priv->ctrl_id < 0)
priv->ctrl_id = 0; priv->ctrl_id = 0;
if (priv->ctrl_id >= TIMING_MODE)
return -EINVAL;
host->priv = priv; host->priv = priv;
return 0; 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 = { static const struct dw_mci_drv_data hi6220_data = {
.caps = dw_mci_hi6220_caps, .caps = dw_mci_hi6220_caps,
.num_caps = ARRAY_SIZE(dw_mci_hi6220_caps),
.switch_voltage = dw_mci_hi6220_switch_voltage, .switch_voltage = dw_mci_hi6220_switch_voltage,
.set_ios = dw_mci_hi6220_set_ios, .set_ios = dw_mci_hi6220_set_ios,
.parse_dt = dw_mci_hi6220_parse_dt, .parse_dt = dw_mci_hi6220_parse_dt,

View File

@ -319,6 +319,7 @@ static const struct dw_mci_drv_data rk2928_drv_data = {
static const struct dw_mci_drv_data rk3288_drv_data = { static const struct dw_mci_drv_data rk3288_drv_data = {
.caps = dw_mci_rk3288_dwmmc_caps, .caps = dw_mci_rk3288_dwmmc_caps,
.num_caps = ARRAY_SIZE(dw_mci_rk3288_dwmmc_caps),
.set_ios = dw_mci_rk3288_set_ios, .set_ios = dw_mci_rk3288_set_ios,
.execute_tuning = dw_mci_rk3288_execute_tuning, .execute_tuning = dw_mci_rk3288_execute_tuning,
.parse_dt = dw_mci_rk3288_parse_dt, .parse_dt = dw_mci_rk3288_parse_dt,

View File

@ -195,6 +195,7 @@ static unsigned long zx_dwmmc_caps[3] = {
static const struct dw_mci_drv_data zx_drv_data = { static const struct dw_mci_drv_data zx_drv_data = {
.caps = zx_dwmmc_caps, .caps = zx_dwmmc_caps,
.num_caps = ARRAY_SIZE(zx_dwmmc_caps),
.execute_tuning = dw_mci_zx_execute_tuning, .execute_tuning = dw_mci_zx_execute_tuning,
.prepare_hs400_tuning = dw_mci_zx_prepare_hs400_tuning, .prepare_hs400_tuning = dw_mci_zx_prepare_hs400_tuning,
.parse_dt = dw_mci_zx_parse_dt, .parse_dt = dw_mci_zx_parse_dt,

View File

@ -165,6 +165,8 @@ static int dw_mci_regs_show(struct seq_file *s, void *v)
{ {
struct dw_mci *host = s->private; 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, "STATUS:\t0x%08x\n", mci_readl(host, STATUS));
seq_printf(s, "RINTSTS:\t0x%08x\n", mci_readl(host, RINTSTS)); seq_printf(s, "RINTSTS:\t0x%08x\n", mci_readl(host, RINTSTS));
seq_printf(s, "CMD:\t0x%08x\n", mci_readl(host, CMD)); 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, "INTMASK:\t0x%08x\n", mci_readl(host, INTMASK));
seq_printf(s, "CLKENA:\t0x%08x\n", mci_readl(host, CLKENA)); seq_printf(s, "CLKENA:\t0x%08x\n", mci_readl(host, CLKENA));
pm_runtime_put_autosuspend(host->dev);
return 0; return 0;
} }
@ -2778,12 +2782,57 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
return IRQ_HANDLED; 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) static int dw_mci_init_slot(struct dw_mci *host)
{ {
struct mmc_host *mmc; struct mmc_host *mmc;
struct dw_mci_slot *slot; struct dw_mci_slot *slot;
const struct dw_mci_drv_data *drv_data = host->drv_data; int ret;
int ctrl_id, ret;
u32 freq[2]; u32 freq[2];
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); 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) if (!mmc->ocr_avail)
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 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); ret = mmc_of_parse(mmc);
if (ret) if (ret)
goto err_host_allocated; goto err_host_allocated;
/* Process SDIO IRQs through the sdio_irq_work. */ ret = dw_mci_init_slot_caps(slot);
if (mmc->caps & MMC_CAP_SDIO_IRQ) if (ret)
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; goto err_host_allocated;
/* Useful defaults if platform data is unset. */ /* Useful defaults if platform data is unset. */
if (host->use_dma == TRANS_MODE_IDMAC) { if (host->use_dma == TRANS_MODE_IDMAC) {

View File

@ -543,6 +543,7 @@ struct dw_mci_slot {
/** /**
* dw_mci driver data - dw-mshc implementation specific driver data. * dw_mci driver data - dw-mshc implementation specific driver data.
* @caps: mmc subsystem specified capabilities of the controller(s). * @caps: mmc subsystem specified capabilities of the controller(s).
* @num_caps: number of capabilities specified by @caps.
* @init: early implementation specific initialization. * @init: early implementation specific initialization.
* @set_ios: handle bus specific extensions. * @set_ios: handle bus specific extensions.
* @parse_dt: parse implementation specific device tree properties. * @parse_dt: parse implementation specific device tree properties.
@ -554,6 +555,7 @@ struct dw_mci_slot {
*/ */
struct dw_mci_drv_data { struct dw_mci_drv_data {
unsigned long *caps; unsigned long *caps;
u32 num_caps;
int (*init)(struct dw_mci *host); int (*init)(struct dw_mci *host);
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios); void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
int (*parse_dt)(struct dw_mci *host); int (*parse_dt)(struct dw_mci *host);

View File

@ -654,9 +654,36 @@ static void byt_read_dsm(struct sdhci_pci_slot *slot)
slot->chip->rpm_retune = intel_host->d3_retune; 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) 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 | slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR | MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
MMC_CAP_CMD_DURING_TFR | MMC_CAP_CMD_DURING_TFR |
@ -779,7 +806,7 @@ static int ni_byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
{ {
int err; int err;
byt_read_dsm(slot); byt_probe_slot(slot);
err = ni_set_max_freq(slot); err = ni_set_max_freq(slot);
if (err) 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) 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 | slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE |
MMC_CAP_WAIT_WHILE_BUSY; MMC_CAP_WAIT_WHILE_BUSY;
return 0; 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) 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 | slot->host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY |
MMC_CAP_AGGRESSIVE_PM | MMC_CAP_CD_WAKE; MMC_CAP_AGGRESSIVE_PM | MMC_CAP_CD_WAKE;
slot->cd_idx = 0; slot->cd_idx = 0;