On 21/04/22 21:28, Kamal Dasu wrote: > From: Al Cooper <alcooperx@xxxxxxxxx> > > Enabling this feature will allow the controller to stop the bus > clock when the bus is idle. The feature is not part of the standard > and is unique to newer Arasan cores and is enabled with a bit in a > vendor specific register. This feature will only be enabled for > non-removable devices because they don't switch the voltage and > clock gating breaks SD Card volatge switching. > > Signed-off-by: Al Cooper <alcooperx@xxxxxxxxx> > Signed-off-by: Kamal Dasu <kdasu.kdev@xxxxxxxxx> Acked-by: Adrian Hunter <adrian.hunter@xxxxxxxxx> > --- > drivers/mmc/host/sdhci-brcmstb.c | 35 +++++++++++++++++++++++++++++++- > 1 file changed, 34 insertions(+), 1 deletion(-) > > diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c > index f32aa045c26d..d5cb3e8978b2 100644 > --- a/drivers/mmc/host/sdhci-brcmstb.c > +++ b/drivers/mmc/host/sdhci-brcmstb.c > @@ -17,11 +17,14 @@ > > #define SDHCI_VENDOR 0x78 > #define SDHCI_VENDOR_ENHANCED_STRB 0x1 > +#define SDHCI_VENDOR_GATE_SDCLK_EN 0x2 > > #define BRCMSTB_MATCH_FLAGS_NO_64BIT BIT(0) > #define BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT BIT(1) > +#define BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE BIT(2) > > #define BRCMSTB_PRIV_FLAGS_HAS_CQE BIT(0) > +#define BRCMSTB_PRIV_FLAGS_GATE_CLOCK BIT(1) > > #define SDHCI_ARASAN_CQE_BASE_ADDR 0x200 > > @@ -36,6 +39,27 @@ struct brcmstb_match_priv { > const unsigned int flags; > }; > > +static inline void enable_clock_gating(struct sdhci_host *host) > +{ > + u32 reg; > + > + reg = sdhci_readl(host, SDHCI_VENDOR); > + reg |= SDHCI_VENDOR_GATE_SDCLK_EN; > + sdhci_writel(host, reg, SDHCI_VENDOR); > +} > + > +void brcmstb_reset(struct sdhci_host *host, u8 mask) > +{ > + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); > + struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); > + > + sdhci_reset(host, mask); > + > + /* Reset will clear this, so re-enable it */ > + if (priv->flags & BRCMSTB_PRIV_FLAGS_GATE_CLOCK) > + enable_clock_gating(host); > +} > + > static void sdhci_brcmstb_hs400es(struct mmc_host *mmc, struct mmc_ios *ios) > { > struct sdhci_host *host = mmc_priv(mmc); > @@ -131,7 +155,7 @@ static struct sdhci_ops sdhci_brcmstb_ops = { > static struct sdhci_ops sdhci_brcmstb_ops_7216 = { > .set_clock = sdhci_brcmstb_set_clock, > .set_bus_width = sdhci_set_bus_width, > - .reset = sdhci_reset, > + .reset = brcmstb_reset, > .set_uhs_signaling = sdhci_brcmstb_set_uhs_signaling, > }; > > @@ -147,6 +171,7 @@ static struct brcmstb_match_priv match_priv_7445 = { > }; > > static const struct brcmstb_match_priv match_priv_7216 = { > + .flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE, > .hs400es = sdhci_brcmstb_hs400es, > .ops = &sdhci_brcmstb_ops_7216, > }; > @@ -273,6 +298,14 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) > if (res) > goto err; > > + /* > + * Automatic clock gating does not work for SD cards that may > + * voltage switch so only enable it for non-removable devices. > + */ > + if ((match_priv->flags & BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE) && > + (host->mmc->caps & MMC_CAP_NONREMOVABLE)) > + priv->flags |= BRCMSTB_PRIV_FLAGS_GATE_CLOCK; > + > /* > * If the chip has enhanced strobe and it's enabled, add > * callback