Skip to content

Commit

Permalink
Merge tag 'mmc-v4.16-rc3' of git://git.kernel.org/pub/scm/linux/kerne…
Browse files Browse the repository at this point in the history
…l/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
  • Loading branch information
torvalds committed Mar 2, 2018
2 parents a5c05b7 + 3a57491 commit ff06b55
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 38 deletions.
4 changes: 0 additions & 4 deletions drivers/mmc/core/mmc_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -890,7 +889,6 @@ int mmc_interrupt_hpi(struct mmc_card *card)
} while (!err);

out:
mmc_release_host(card->host);
return err;
}

Expand Down Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions drivers/mmc/host/dw_mmc-exynos.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions drivers/mmc/host/dw_mmc-k3.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions drivers/mmc/host/dw_mmc-rockchip.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions drivers/mmc/host/dw_mmc-zx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
84 changes: 54 additions & 30 deletions drivers/mmc/host/dw_mmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,17 @@ 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));
seq_printf(s, "CTRL:\t0x%08x\n", mci_readl(host, CTRL));
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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 2 additions & 0 deletions drivers/mmc/host/dw_mmc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);
Expand Down
35 changes: 31 additions & 4 deletions drivers/mmc/host/sdhci-pci-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -654,9 +654,36 @@ static void byt_read_dsm(struct sdhci_pci_slot *slot)
slot->chip->rpm_retune = intel_host->d3_retune;
}

static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
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_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 |
Expand Down Expand Up @@ -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)
Expand All @@ -792,15 +819,15 @@ 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;
}

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;
Expand Down

0 comments on commit ff06b55

Please sign in to comment.