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. -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAl0LaFgXHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCnnkg//f4s1IsuIQjifX1+0crD3xSPF ZFvn7LMldslZxIfXzwJxvp50ZA6KUZVUpAlmTFev1Dbz+SoYJvOYxXIcv0/ASQYn t2f0qNSI3Mw9vQa2S2PMYyThO8KSoqtu0Z3gXdSjGgox6et3g9HMW/ubOuj+91Gx UhkVd7B0O6uY83CxJWnhEMuDS5iMhuZJ0HMFk6R1XrtsCJf9T1jZMYa2iJ1247TZ pChYrfw1NJzUDEzGOlUlShGRUdqLjcNeSj2gN50O2GZpPeZnnSwabdOd0OcyE7rH 563GxCz0GZRLs8cCxzp+9OirQER7+ZBK/e9Q34MVcNsmx/FwEclg1dfhscOZhll3 27NBf16sAyas54MhDaMRmh2SQfqucEO4M4Wn0QzP/eo1t9o2MZiwY/iw+NRC6SDm V2tVd7rRNly1I3KlmYEOAEYC2f1UXXyb8LYa8+D6g9CSQGcf0HrqoUzHESDCf1eF hU6JaijQQhs2sDqsH6Hyg0i5yACm68RQhpfTTTMHXbDLTzVZbChVlsox3YszHsFt 8eOWS/LeGI058i40EKa4ulbbCjMmGoghYneyTBQSKBYGiJwIVpvZGqje+Nmfo3rw o4d+OTzM2qdkAk0GZs9o0/xWj00LU43C8W0NO6MV/WgKaysk+WNueawrZWV7/nL3 up0ea93jvrwpUf3f2jw= =IkL6 -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
6331d118ac
|
@ -144,8 +144,9 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
|
|||
int err = cmd->error;
|
||||
|
||||
/* Flag re-tuning needed on CRC errors */
|
||||
if ((cmd->opcode != MMC_SEND_TUNING_BLOCK &&
|
||||
cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) &&
|
||||
if (cmd->opcode != MMC_SEND_TUNING_BLOCK &&
|
||||
cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200 &&
|
||||
!host->retune_crc_disable &&
|
||||
(err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) ||
|
||||
(mrq->data && mrq->data->error == -EILSEQ) ||
|
||||
(mrq->stop && mrq->stop->error == -EILSEQ)))
|
||||
|
|
|
@ -1212,13 +1212,13 @@ static int mmc_select_hs400(struct mmc_card *card)
|
|||
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
|
||||
mmc_set_bus_speed(card);
|
||||
|
||||
if (host->ops->hs400_complete)
|
||||
host->ops->hs400_complete(host);
|
||||
|
||||
err = mmc_switch_status(card);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
if (host->ops->hs400_complete)
|
||||
host->ops->hs400_complete(host);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
|
|
|
@ -937,6 +937,10 @@ static int mmc_sdio_pre_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);
|
||||
|
||||
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)
|
|||
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))
|
||||
wake_up_process(host->sdio_irq_thread);
|
||||
else if (host->caps & MMC_CAP_SDIO_IRQ)
|
||||
host->ops->enable_sdio_irq(host, 1);
|
||||
}
|
||||
|
||||
out:
|
||||
mmc_release_host(host);
|
||||
|
||||
host->pm_flags &= ~MMC_PM_KEEP_POWER;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "sdio_ops.h"
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
#include "host.h"
|
||||
|
||||
/**
|
||||
* 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)
|
|||
return 0;
|
||||
}
|
||||
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)
|
|||
unsigned char pending;
|
||||
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
|
||||
* 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)
|
|||
msdc_track_cmd_data(host, mrq->cmd, mrq->data);
|
||||
if (mrq->data)
|
||||
msdc_unprepare_data(host, mrq);
|
||||
if (host->error)
|
||||
msdc_reset_hw(host);
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
if (enb) {
|
||||
sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ);
|
||||
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 + SDC_CFG, SDC_CFG_SDIOIDE);
|
||||
}
|
||||
}
|
||||
|
||||
static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enb)
|
||||
{
|
||||
unsigned long flags;
|
||||
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);
|
||||
else
|
||||
sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ);
|
||||
__msdc_enable_sdio_irq(host, enb);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enb)
|
||||
{
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
|
||||
__msdc_enable_sdio_irq(mmc, enb);
|
||||
|
||||
if (enb)
|
||||
pm_runtime_get_noresume(host->dev);
|
||||
|
@ -1414,6 +1417,8 @@ static irqreturn_t msdc_irq(int irq, void *dev_id)
|
|||
spin_lock_irqsave(&host->lock, flags);
|
||||
events = readl(host->base + MSDC_INT);
|
||||
event_mask = readl(host->base + MSDC_INTEN);
|
||||
if ((events & event_mask) & MSDC_INT_SDIOIRQ)
|
||||
__msdc_enable_sdio_irq(host, 0);
|
||||
/* clear interrupts */
|
||||
writel(events & event_mask, host->base + MSDC_INT);
|
||||
|
||||
|
@ -1422,10 +1427,8 @@ static irqreturn_t msdc_irq(int irq, void *dev_id)
|
|||
data = host->data;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
if ((events & event_mask) & MSDC_INT_SDIOIRQ) {
|
||||
__msdc_enable_sdio_irq(host->mmc, 0);
|
||||
if ((events & event_mask) & MSDC_INT_SDIOIRQ)
|
||||
sdio_signal_irq(host->mmc);
|
||||
}
|
||||
|
||||
if ((events & event_mask) & MSDC_INT_CDSC) {
|
||||
if (host->internal_cd)
|
||||
|
@ -1564,10 +1567,7 @@ static void msdc_init_hw(struct msdc_host *host)
|
|||
sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIO);
|
||||
|
||||
/* 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 */
|
||||
sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, 3);
|
||||
|
@ -2095,7 +2095,12 @@ static void msdc_hw_reset(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)
|
||||
|
|
|
@ -620,11 +620,16 @@ static const struct renesas_sdhi_quirks sdhi_quirks_h3_es2 = {
|
|||
.hs400_4taps = true,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_nohs400 = {
|
||||
.hs400_disabled = true,
|
||||
};
|
||||
|
||||
static const struct soc_device_attribute sdhi_quirks_match[] = {
|
||||
{ .soc_id = "r8a7795", .revision = "ES1.*", .data = &sdhi_quirks_h3_m3w_es1 },
|
||||
{ .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.1", .data = &sdhi_quirks_h3_m3w_es1 },
|
||||
{ .soc_id = "r8a7796", .revision = "ES1.[012]", .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. */ },
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
current_bus_width = mmc->ios.bus_width;
|
||||
mmc->ios.bus_width = 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)
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
host->flags &= ~SDHCI_HS400_TUNING;
|
||||
return 0;
|
||||
|
|
|
@ -678,6 +678,12 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool 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);
|
||||
/* 1st KSO write goes to AOS wake up core if device is asleep */
|
||||
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)
|
|||
if (try_cnt > MAX_KSO_ATTEMPTS)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -3375,11 +3386,7 @@ err:
|
|||
|
||||
static bool brcmf_sdio_aos_no_decode(struct brcmf_sdio *bus)
|
||||
{
|
||||
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)
|
||||
if (bus->ci->chip == CY_CC_43012_CHIP_ID)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
|
|
@ -398,6 +398,7 @@ struct mmc_host {
|
|||
unsigned int retune_now:1; /* do re-tuning at next req */
|
||||
unsigned int retune_paused:1; /* re-tuning is temporarily disabled */
|
||||
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_entered; /* used with nonremovable devices */
|
||||
|
|
|
@ -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 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 */
|
||||
|
|
Loading…
Reference in New Issue