Hi, CC'd maintainer. On 05/15/2015 06:19 PM, Yangbo Lu wrote: > This quirk is used to add workaround for silicon erratum A-003980, and > another erratum A-005055 shares the same workaround since they are the > same issue. > A-003980: SDHC: Glitch is generated on the card clock with software reset > or clock divider change > Description: A glitch may occur on the SDHC card clock when the software > sets the RSTA bit (software reset) in the system control register. It can > also be generated by setting the clock divider value. The glitch produced > can cause the external card to switch to an unknown state. The occurrence > is not deterministic. > > Workaround: > A simple workaround is to disable the SD card clock before the software > reset, and enable it when the module resumes normal operation. The Host > and the SD card are in a master-slave relationship. The Host provides > clock and control transfer across the interface. Therefore, any existing > operation is discarded when the Host controller is reset. > The recommended flow is as follows: > 1. Software disable bit[3], SDCLKEN, of the System Control Register > 2. Trigger software reset and/or set clock divider > 3. Check bit[3], SDSTB, of the Present State Register for stable clock > 4. Enable bit[3], SDCLKEN, of the System Control Register > Using the above method, the eSDHC cannot send command or transfer data > when there is a glitch in the clock line, and the glitch does not cause > any issue. Then is this workaround patch? Is it not controller problem? > > Signed-off-by: Yangbo Lu <yangbo.lu@xxxxxxxxxxxxx> > --- > drivers/mmc/host/sdhci-esdhc.h | 4 ++ > drivers/mmc/host/sdhci-of-esdhc.c | 78 ++++++++++++++++++++++++++++++++++++++- > drivers/mmc/host/sdhci.c | 6 +++ > drivers/mmc/host/sdhci.h | 4 ++ > 4 files changed, 90 insertions(+), 2 deletions(-) Separate esdhc and sdhci core parts. > > diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h > index 3497cfa..ede49f5 100644 > --- a/drivers/mmc/host/sdhci-esdhc.h > +++ b/drivers/mmc/host/sdhci-esdhc.h > @@ -27,10 +27,14 @@ > #define ESDHC_CLOCK_MASK 0x0000fff0 > #define ESDHC_PREDIV_SHIFT 8 > #define ESDHC_DIVIDER_SHIFT 4 > +#define ESDHC_CLOCK_CRDEN 0x00000008 > #define ESDHC_CLOCK_PEREN 0x00000004 > #define ESDHC_CLOCK_HCKEN 0x00000002 > #define ESDHC_CLOCK_IPGEN 0x00000001 > > +#define ESDHCI_PRESENT_STATE 0x24 > +#define ESDHC_CLK_STABLE 0x00000008 > + > /* pltfm-specific */ > #define ESDHC_HOST_CONTROL_LE 0x20 > > diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c > index 22e9111..af8b1b1 100644 > --- a/drivers/mmc/host/sdhci-of-esdhc.c > +++ b/drivers/mmc/host/sdhci-of-esdhc.c > @@ -22,8 +22,13 @@ > #include "sdhci-pltfm.h" > #include "sdhci-esdhc.h" > > +#include <asm/mpc85xx.h> I think this is not good. it has dependent with architecture. > + > #define VENDOR_V_22 0x12 > #define VENDOR_V_23 0x13 > + > +static u32 svr; > + > static u32 esdhc_readl(struct sdhci_host *host, int reg) > { > u32 ret; > @@ -202,6 +207,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) > int pre_div = 2; > int div = 1; > u32 temp; > + u32 timeout; > > host->mmc->actual_clock = 0; > > @@ -218,7 +224,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) > > temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); > temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN > - | ESDHC_CLOCK_MASK); > + | ESDHC_CLOCK_MASK | ESDHC_CLOCK_CRDEN); > sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); > > while (host->max_clk / pre_div / 16 > clock && pre_div < 256) > @@ -238,13 +244,52 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) > | (div << ESDHC_DIVIDER_SHIFT) > | (pre_div << ESDHC_PREDIV_SHIFT)); > sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); > - mdelay(1); > + > + /* Wait max 20 ms */ > + timeout = 20; > + while (!(sdhci_readl(host, ESDHCI_PRESENT_STATE) & ESDHC_CLK_STABLE)) { > + if (timeout == 0) { > + pr_err("%s: Internal clock never stabilised.\n", > + mmc_hostname(host->mmc)); > + return; > + } > + timeout--; > + mdelay(1); > + } > + > + temp |= ESDHC_CLOCK_CRDEN; > + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); > +} > + > +static void esdhc_of_platform_reset_enter(struct sdhci_host *host, u8 mask) > +{ > + if ((host->quirks2 & SDHCI_QUIRK2_DISABLE_CLOCK_BEFORE_RESET) && > + (mask & SDHCI_RESET_ALL)) { > + u16 clk; > + > + clk = esdhc_readw(host, SDHCI_CLOCK_CONTROL); > + clk &= ~ESDHC_CLOCK_CRDEN; > + esdhc_writew(host, clk, SDHCI_CLOCK_CONTROL); > + } > +} > + > +static void esdhc_of_platform_reset_exit(struct sdhci_host *host, u8 mask) > +{ > + if ((host->quirks2 & SDHCI_QUIRK2_DISABLE_CLOCK_BEFORE_RESET) && > + (mask & SDHCI_RESET_ALL)) { > + u16 clk; > + > + clk = esdhc_readw(host, SDHCI_CLOCK_CONTROL); > + clk |= ESDHC_CLOCK_CRDEN; > + esdhc_writew(host, clk, SDHCI_CLOCK_CONTROL); > + } > } > > static void esdhc_of_platform_init(struct sdhci_host *host) > { > u32 vvn; > > + svr = mfspr(SPRN_SVR); > vvn = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); > vvn = (vvn & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; > if (vvn == VENDOR_V_22) > @@ -252,6 +297,33 @@ static void esdhc_of_platform_init(struct sdhci_host *host) > > if (vvn > VENDOR_V_22) > host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; > + > + /* > + * Check for A-005055 and A-003980: A glitch is generated on the > + * card clock due to software reset or a clock change > + * Impact list: > + * T4240-4160-R1.0 B4860-4420-R1.0-R2.0 P3041-R1.0-R1.1-R2.0 > + * P2041-2040-R1.0-R1.1-R2.0 P1010-1014-R1.0 P5020-5010-R1.0-R2.0 > + * P5040-5021-R1.0-R2.0-R2.1 > + */ > + if (((SVR_SOC_VER(svr) == SVR_T4240) && (SVR_REV(svr) == 0x10)) || > + ((SVR_SOC_VER(svr) == SVR_T4240) && (SVR_REV(svr) == 0x20)) || > + ((SVR_SOC_VER(svr) == SVR_T4160) && (SVR_REV(svr) == 0x10)) || > + ((SVR_SOC_VER(svr) == SVR_T4160) && (SVR_REV(svr) == 0x20)) || > + ((SVR_SOC_VER(svr) == SVR_B4860) && (SVR_REV(svr) == 0x10)) || > + ((SVR_SOC_VER(svr) == SVR_B4860) && (SVR_REV(svr) == 0x20)) || > + ((SVR_SOC_VER(svr) == SVR_B4420) && (SVR_REV(svr) == 0x10)) || > + ((SVR_SOC_VER(svr) == SVR_B4420) && (SVR_REV(svr) == 0x20)) || > + ((SVR_SOC_VER(svr) == SVR_P5040) && (SVR_REV(svr) <= 0x21)) || > + ((SVR_SOC_VER(svr) == SVR_P5020) && (SVR_REV(svr) <= 0x20)) || > + ((SVR_SOC_VER(svr) == SVR_P5021) && (SVR_REV(svr) <= 0x21)) || > + ((SVR_SOC_VER(svr) == SVR_P5010) && (SVR_REV(svr) <= 0x20)) || > + ((SVR_SOC_VER(svr) == SVR_P3041) && (SVR_REV(svr) <= 0x20)) || > + ((SVR_SOC_VER(svr) == SVR_P2041) && (SVR_REV(svr) <= 0x20)) || > + ((SVR_SOC_VER(svr) == SVR_P2040) && (SVR_REV(svr) <= 0x20)) || > + ((SVR_SOC_VER(svr) == SVR_P1014) && (SVR_REV(svr) == 0x10)) || > + ((SVR_SOC_VER(svr) == SVR_P1010) && (SVR_REV(svr) == 0x10))) > + host->quirks2 |= SDHCI_QUIRK2_DISABLE_CLOCK_BEFORE_RESET; Why don't use the dt-file? > } > > static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) > @@ -295,6 +367,8 @@ static const struct sdhci_ops sdhci_esdhc_ops = { > .enable_dma = esdhc_of_enable_dma, > .get_max_clock = esdhc_of_get_max_clock, > .get_min_clock = esdhc_of_get_min_clock, > + .platform_reset_enter = esdhc_of_platform_reset_enter, > + .platform_reset_exit = esdhc_of_platform_reset_exit, Well, i don't know whether this ops is really needs. Best Regards, Jaehoon Chung > .platform_init = esdhc_of_platform_init, > .adma_workaround = esdhci_of_adma_workaround, > .set_bus_width = esdhc_pltfm_set_bus_width, > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c > index c80287a..89bba64 100644 > --- a/drivers/mmc/host/sdhci.c > +++ b/drivers/mmc/host/sdhci.c > @@ -179,6 +179,9 @@ void sdhci_reset(struct sdhci_host *host, u8 mask) > { > unsigned long timeout; > > + if (host->ops->platform_reset_enter) > + host->ops->platform_reset_enter(host, mask); > + > sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET); > > if (mask & SDHCI_RESET_ALL) { > @@ -202,6 +205,9 @@ void sdhci_reset(struct sdhci_host *host, u8 mask) > timeout--; > mdelay(1); > } > + > + if (host->ops->platform_reset_exit) > + host->ops->platform_reset_exit(host, mask); > } > EXPORT_SYMBOL_GPL(sdhci_reset); > > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h > index e639b7f..565790a 100644 > --- a/drivers/mmc/host/sdhci.h > +++ b/drivers/mmc/host/sdhci.h > @@ -409,6 +409,8 @@ struct sdhci_host { > #define SDHCI_QUIRK2_SUPPORT_SINGLE (1<<13) > /* Controller broken with using ACMD23 */ > #define SDHCI_QUIRK2_ACMD23_BROKEN (1<<14) > +/* Controller need to disable clock before reset all */ > +#define SDHCI_QUIRK2_DISABLE_CLOCK_BEFORE_RESET (1<<15) > > int irq; /* Device IRQ */ > void __iomem *ioaddr; /* Mapped address */ > @@ -541,6 +543,8 @@ struct sdhci_ops { > void (*platform_init)(struct sdhci_host *host); > void (*card_event)(struct sdhci_host *host); > void (*voltage_switch)(struct sdhci_host *host); > + void (*platform_reset_enter)(struct sdhci_host *host, u8 mask); > + void (*platform_reset_exit)(struct sdhci_host *host, u8 mask); > }; > > #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS > -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html