Commit 6331d118 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mmc-v5.2-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc

Pull MMC fixes from Ulf Hansson:
 "Here's quite a few MMC fixes intended for v5.2-rc6. This time it also
  contains fixes for a WiFi driver, which device is attached to the SDIO
  interface. Patches for the WiFi driver have been acked by the
  corresponding maintainers.

  Summary:

  MMC core:
   - Make switch to eMMC HS400 more robust for some controllers
   - Add two SDIO func API to manage re-tuning constraints
   - Prevent processing SDIO IRQs when the card is suspended

  MMC host:
   - sdhi: Disallow broken HS400 for M3-W ES1.2, RZ/G2M and V3H
   - mtk-sd: Fixup support for SDIO IRQs
   - sdhci-pci-o2micro: Fixup support for tuning

  Wireless BRCMFMAC (SDIO):
   - Deal with expected transmission errors related to the idle states
     (handled by the Always-On-Subsystem or AOS) on the SDIO-based WiFi
     on rk3288-veyron-minnie, rk3288-veyron-speedy and
     rk3288-veyron-mickey"

* tag 'mmc-v5.2-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc:
  mmc: core: Prevent processing SDIO IRQs when the card is suspended
  mmc: sdhci: sdhci-pci-o2micro: Correctly set bus width when tuning
  brcmfmac: sdio: Don't tune while the card is off
  mmc: core: Add sdio_retune_hold_now() and sdio_retune_release()
  brcmfmac: sdio: Disable auto-tuning around commands expected to fail
  mmc: core: API to temporarily disable retuning for SDIO CRC errors
  Revert "brcmfmac: disable command decode in sdio_aos"
  mmc: mediatek: fix SDIO IRQ detection issue
  mmc: mediatek: fix SDIO IRQ interrupt handle flow
  mmc: core: complete HS400 before checking status
  mmc: sdhi: disallow HS400 for M3-W ES1.2, RZ/G2M, and V3H
parents 41a247d8 83293386
...@@ -144,8 +144,9 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) ...@@ -144,8 +144,9 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
int err = cmd->error; int err = cmd->error;
/* Flag re-tuning needed on CRC errors */ /* Flag re-tuning needed on CRC errors */
if ((cmd->opcode != MMC_SEND_TUNING_BLOCK && if (cmd->opcode != MMC_SEND_TUNING_BLOCK &&
cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) && cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200 &&
!host->retune_crc_disable &&
(err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) || (err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) ||
(mrq->data && mrq->data->error == -EILSEQ) || (mrq->data && mrq->data->error == -EILSEQ) ||
(mrq->stop && mrq->stop->error == -EILSEQ))) (mrq->stop && mrq->stop->error == -EILSEQ)))
......
...@@ -1212,13 +1212,13 @@ static int mmc_select_hs400(struct mmc_card *card) ...@@ -1212,13 +1212,13 @@ static int mmc_select_hs400(struct mmc_card *card)
mmc_set_timing(host, MMC_TIMING_MMC_HS400); mmc_set_timing(host, MMC_TIMING_MMC_HS400);
mmc_set_bus_speed(card); mmc_set_bus_speed(card);
if (host->ops->hs400_complete)
host->ops->hs400_complete(host);
err = mmc_switch_status(card); err = mmc_switch_status(card);
if (err) if (err)
goto out_err; goto out_err;
if (host->ops->hs400_complete)
host->ops->hs400_complete(host);
return 0; return 0;
out_err: out_err:
......
...@@ -937,6 +937,10 @@ static int mmc_sdio_pre_suspend(struct mmc_host *host) ...@@ -937,6 +937,10 @@ static int mmc_sdio_pre_suspend(struct mmc_host *host)
*/ */
static int mmc_sdio_suspend(struct mmc_host *host) static int mmc_sdio_suspend(struct mmc_host *host)
{ {
/* Prevent processing of SDIO IRQs in suspended state. */
mmc_card_set_suspended(host->card);
cancel_delayed_work_sync(&host->sdio_irq_work);
mmc_claim_host(host); mmc_claim_host(host);
if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host))
...@@ -985,13 +989,20 @@ static int mmc_sdio_resume(struct mmc_host *host) ...@@ -985,13 +989,20 @@ static int mmc_sdio_resume(struct mmc_host *host)
err = sdio_enable_4bit_bus(host->card); err = sdio_enable_4bit_bus(host->card);
} }
if (!err && host->sdio_irqs) { if (err)
goto out;
/* Allow SDIO IRQs to be processed again. */
mmc_card_clr_suspended(host->card);
if (host->sdio_irqs) {
if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD))
wake_up_process(host->sdio_irq_thread); wake_up_process(host->sdio_irq_thread);
else if (host->caps & MMC_CAP_SDIO_IRQ) else if (host->caps & MMC_CAP_SDIO_IRQ)
host->ops->enable_sdio_irq(host, 1); host->ops->enable_sdio_irq(host, 1);
} }
out:
mmc_release_host(host); mmc_release_host(host);
host->pm_flags &= ~MMC_PM_KEEP_POWER; host->pm_flags &= ~MMC_PM_KEEP_POWER;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "sdio_ops.h" #include "sdio_ops.h"
#include "core.h" #include "core.h"
#include "card.h" #include "card.h"
#include "host.h"
/** /**
* sdio_claim_host - exclusively claim a bus for a certain SDIO function * sdio_claim_host - exclusively claim a bus for a certain SDIO function
...@@ -734,3 +735,79 @@ int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags) ...@@ -734,3 +735,79 @@ int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(sdio_set_host_pm_flags); EXPORT_SYMBOL_GPL(sdio_set_host_pm_flags);
/**
* sdio_retune_crc_disable - temporarily disable retuning on CRC errors
* @func: SDIO function attached to host
*
* If the SDIO card is known to be in a state where it might produce
* CRC errors on the bus in response to commands (like if we know it is
* transitioning between power states), an SDIO function driver can
* call this function to temporarily disable the SD/MMC core behavior of
* triggering an automatic retuning.
*
* This function should be called while the host is claimed and the host
* should remain claimed until sdio_retune_crc_enable() is called.
* Specifically, the expected sequence of calls is:
* - sdio_claim_host()
* - sdio_retune_crc_disable()
* - some number of calls like sdio_writeb() and sdio_readb()
* - sdio_retune_crc_enable()
* - sdio_release_host()
*/
void sdio_retune_crc_disable(struct sdio_func *func)
{
func->card->host->retune_crc_disable = true;
}
EXPORT_SYMBOL_GPL(sdio_retune_crc_disable);
/**
* sdio_retune_crc_enable - re-enable retuning on CRC errors
* @func: SDIO function attached to host
*
* This is the compement to sdio_retune_crc_disable().
*/
void sdio_retune_crc_enable(struct sdio_func *func)
{
func->card->host->retune_crc_disable = false;
}
EXPORT_SYMBOL_GPL(sdio_retune_crc_enable);
/**
* sdio_retune_hold_now - start deferring retuning requests till release
* @func: SDIO function attached to host
*
* This function can be called if it's currently a bad time to do
* a retune of the SDIO card. Retune requests made during this time
* will be held and we'll actually do the retune sometime after the
* release.
*
* This function could be useful if an SDIO card is in a power state
* where it can respond to a small subset of commands that doesn't
* include the retuning command. Care should be taken when using
* this function since (presumably) the retuning request we might be
* deferring was made for a good reason.
*
* This function should be called while the host is claimed.
*/
void sdio_retune_hold_now(struct sdio_func *func)
{
mmc_retune_hold_now(func->card->host);
}
EXPORT_SYMBOL_GPL(sdio_retune_hold_now);
/**
* sdio_retune_release - signal that it's OK to retune now
* @func: SDIO function attached to host
*
* This is the complement to sdio_retune_hold_now(). Calling this
* function won't make a retune happen right away but will allow
* them to be scheduled normally.
*
* This function should be called while the host is claimed.
*/
void sdio_retune_release(struct sdio_func *func)
{
mmc_retune_release(func->card->host);
}
EXPORT_SYMBOL_GPL(sdio_retune_release);
...@@ -34,6 +34,10 @@ static int process_sdio_pending_irqs(struct mmc_host *host) ...@@ -34,6 +34,10 @@ static int process_sdio_pending_irqs(struct mmc_host *host)
unsigned char pending; unsigned char pending;
struct sdio_func *func; struct sdio_func *func;
/* Don't process SDIO IRQs if the card is suspended. */
if (mmc_card_suspended(card))
return 0;
/* /*
* Optimization, if there is only 1 function interrupt registered * Optimization, if there is only 1 function interrupt registered
* and we know an IRQ was signaled then call irq handler directly. * and we know an IRQ was signaled then call irq handler directly.
......
...@@ -1023,6 +1023,8 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq) ...@@ -1023,6 +1023,8 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
msdc_track_cmd_data(host, mrq->cmd, mrq->data); msdc_track_cmd_data(host, mrq->cmd, mrq->data);
if (mrq->data) if (mrq->data)
msdc_unprepare_data(host, mrq); msdc_unprepare_data(host, mrq);
if (host->error)
msdc_reset_hw(host);
mmc_request_done(host->mmc, mrq); mmc_request_done(host->mmc, mrq);
} }
...@@ -1375,24 +1377,25 @@ static void msdc_request_timeout(struct work_struct *work) ...@@ -1375,24 +1377,25 @@ static void msdc_request_timeout(struct work_struct *work)
} }
} }
static void __msdc_enable_sdio_irq(struct mmc_host *mmc, int enb) static void __msdc_enable_sdio_irq(struct msdc_host *host, int enb)
{ {
unsigned long flags; if (enb) {
struct msdc_host *host = mmc_priv(mmc);
spin_lock_irqsave(&host->lock, flags);
if (enb)
sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ); sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ);
else sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
} else {
sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ); sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ);
spin_unlock_irqrestore(&host->lock, flags); sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
}
} }
static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enb) static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enb)
{ {
unsigned long flags;
struct msdc_host *host = mmc_priv(mmc); struct msdc_host *host = mmc_priv(mmc);
__msdc_enable_sdio_irq(mmc, enb); spin_lock_irqsave(&host->lock, flags);
__msdc_enable_sdio_irq(host, enb);
spin_unlock_irqrestore(&host->lock, flags);
if (enb) if (enb)
pm_runtime_get_noresume(host->dev); pm_runtime_get_noresume(host->dev);
...@@ -1414,6 +1417,8 @@ static irqreturn_t msdc_irq(int irq, void *dev_id) ...@@ -1414,6 +1417,8 @@ static irqreturn_t msdc_irq(int irq, void *dev_id)
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
events = readl(host->base + MSDC_INT); events = readl(host->base + MSDC_INT);
event_mask = readl(host->base + MSDC_INTEN); event_mask = readl(host->base + MSDC_INTEN);
if ((events & event_mask) & MSDC_INT_SDIOIRQ)
__msdc_enable_sdio_irq(host, 0);
/* clear interrupts */ /* clear interrupts */
writel(events & event_mask, host->base + MSDC_INT); writel(events & event_mask, host->base + MSDC_INT);
...@@ -1422,10 +1427,8 @@ static irqreturn_t msdc_irq(int irq, void *dev_id) ...@@ -1422,10 +1427,8 @@ static irqreturn_t msdc_irq(int irq, void *dev_id)
data = host->data; data = host->data;
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
if ((events & event_mask) & MSDC_INT_SDIOIRQ) { if ((events & event_mask) & MSDC_INT_SDIOIRQ)
__msdc_enable_sdio_irq(host->mmc, 0);
sdio_signal_irq(host->mmc); sdio_signal_irq(host->mmc);
}
if ((events & event_mask) & MSDC_INT_CDSC) { if ((events & event_mask) & MSDC_INT_CDSC) {
if (host->internal_cd) if (host->internal_cd)
...@@ -1564,9 +1567,6 @@ static void msdc_init_hw(struct msdc_host *host) ...@@ -1564,9 +1567,6 @@ static void msdc_init_hw(struct msdc_host *host)
sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIO); sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIO);
/* Config SDIO device detect interrupt function */ /* Config SDIO device detect interrupt function */
if (host->mmc->caps & MMC_CAP_SDIO_IRQ)
sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
else
sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
/* Configure to default data timeout */ /* Configure to default data timeout */
...@@ -2095,7 +2095,12 @@ static void msdc_hw_reset(struct mmc_host *mmc) ...@@ -2095,7 +2095,12 @@ static void msdc_hw_reset(struct mmc_host *mmc)
static void msdc_ack_sdio_irq(struct mmc_host *mmc) static void msdc_ack_sdio_irq(struct mmc_host *mmc)
{ {
__msdc_enable_sdio_irq(mmc, 1); unsigned long flags;
struct msdc_host *host = mmc_priv(mmc);
spin_lock_irqsave(&host->lock, flags);
__msdc_enable_sdio_irq(host, 1);
spin_unlock_irqrestore(&host->lock, flags);
} }
static int msdc_get_cd(struct mmc_host *mmc) static int msdc_get_cd(struct mmc_host *mmc)
......
...@@ -620,11 +620,16 @@ static const struct renesas_sdhi_quirks sdhi_quirks_h3_es2 = { ...@@ -620,11 +620,16 @@ static const struct renesas_sdhi_quirks sdhi_quirks_h3_es2 = {
.hs400_4taps = true, .hs400_4taps = true,
}; };
static const struct renesas_sdhi_quirks sdhi_quirks_nohs400 = {
.hs400_disabled = true,
};
static const struct soc_device_attribute sdhi_quirks_match[] = { static const struct soc_device_attribute sdhi_quirks_match[] = {
{ .soc_id = "r8a7795", .revision = "ES1.*", .data = &sdhi_quirks_h3_m3w_es1 }, { .soc_id = "r8a7795", .revision = "ES1.*", .data = &sdhi_quirks_h3_m3w_es1 },
{ .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_h3_es2 }, { .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_h3_es2 },
{ .soc_id = "r8a7796", .revision = "ES1.0", .data = &sdhi_quirks_h3_m3w_es1 }, { .soc_id = "r8a7796", .revision = "ES1.[012]", .data = &sdhi_quirks_h3_m3w_es1 },
{ .soc_id = "r8a7796", .revision = "ES1.1", .data = &sdhi_quirks_h3_m3w_es1 }, { .soc_id = "r8a774a1", .revision = "ES1.[012]", .data = &sdhi_quirks_h3_m3w_es1 },
{ .soc_id = "r8a77980", .data = &sdhi_quirks_nohs400 },
{ /* Sentinel. */ }, { /* Sentinel. */ },
}; };
......
...@@ -115,6 +115,7 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode) ...@@ -115,6 +115,7 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode)
*/ */
if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) {
current_bus_width = mmc->ios.bus_width; current_bus_width = mmc->ios.bus_width;
mmc->ios.bus_width = MMC_BUS_WIDTH_4;
sdhci_set_bus_width(host, MMC_BUS_WIDTH_4); sdhci_set_bus_width(host, MMC_BUS_WIDTH_4);
} }
...@@ -126,8 +127,10 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode) ...@@ -126,8 +127,10 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode)
sdhci_end_tuning(host); sdhci_end_tuning(host);
if (current_bus_width == MMC_BUS_WIDTH_8) if (current_bus_width == MMC_BUS_WIDTH_8) {
mmc->ios.bus_width = MMC_BUS_WIDTH_8;
sdhci_set_bus_width(host, current_bus_width); sdhci_set_bus_width(host, current_bus_width);
}
host->flags &= ~SDHCI_HS400_TUNING; host->flags &= ~SDHCI_HS400_TUNING;
return 0; return 0;
......
...@@ -678,6 +678,12 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) ...@@ -678,6 +678,12 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
brcmf_dbg(TRACE, "Enter: on=%d\n", on); brcmf_dbg(TRACE, "Enter: on=%d\n", on);
sdio_retune_crc_disable(bus->sdiodev->func1);
/* Cannot re-tune if device is asleep; defer till we're awake */
if (on)
sdio_retune_hold_now(bus->sdiodev->func1);
wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
/* 1st KSO write goes to AOS wake up core if device is asleep */ /* 1st KSO write goes to AOS wake up core if device is asleep */
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err); brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err);
...@@ -738,6 +744,11 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) ...@@ -738,6 +744,11 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
if (try_cnt > MAX_KSO_ATTEMPTS) if (try_cnt > MAX_KSO_ATTEMPTS)
brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err); brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err);
if (on)
sdio_retune_release(bus->sdiodev->func1);
sdio_retune_crc_enable(bus->sdiodev->func1);
return err; return err;
} }
...@@ -3375,11 +3386,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, ...@@ -3375,11 +3386,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus,
static bool brcmf_sdio_aos_no_decode(struct brcmf_sdio *bus) static bool brcmf_sdio_aos_no_decode(struct brcmf_sdio *bus)
{ {
if (bus->ci->chip == CY_CC_43012_CHIP_ID || if (bus->ci->chip == CY_CC_43012_CHIP_ID)
bus->ci->chip == CY_CC_4373_CHIP_ID ||
bus->ci->chip == BRCM_CC_4339_CHIP_ID ||
bus->ci->chip == BRCM_CC_4345_CHIP_ID ||
bus->ci->chip == BRCM_CC_4354_CHIP_ID)
return true; return true;
else else
return false; return false;
......
...@@ -398,6 +398,7 @@ struct mmc_host { ...@@ -398,6 +398,7 @@ struct mmc_host {
unsigned int retune_now:1; /* do re-tuning at next req */ unsigned int retune_now:1; /* do re-tuning at next req */
unsigned int retune_paused:1; /* re-tuning is temporarily disabled */ unsigned int retune_paused:1; /* re-tuning is temporarily disabled */
unsigned int use_blk_mq:1; /* use blk-mq */ unsigned int use_blk_mq:1; /* use blk-mq */
unsigned int retune_crc_disable:1; /* don't trigger retune upon crc */
int rescan_disable; /* disable card detection */ int rescan_disable; /* disable card detection */
int rescan_entered; /* used with nonremovable devices */ int rescan_entered; /* used with nonremovable devices */
......
...@@ -167,4 +167,10 @@ extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b, ...@@ -167,4 +167,10 @@ extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b,
extern mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func); extern mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func);
extern int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags); extern int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags);
extern void sdio_retune_crc_disable(struct sdio_func *func);
extern void sdio_retune_crc_enable(struct sdio_func *func);
extern void sdio_retune_hold_now(struct sdio_func *func);
extern void sdio_retune_release(struct sdio_func *func);
#endif /* LINUX_MMC_SDIO_FUNC_H */ #endif /* LINUX_MMC_SDIO_FUNC_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment