mmc: mediatek: fix SDIO irq issue
authoryong mao <yong.mao@mediatek.com>
Fri, 27 Mar 2020 08:51:37 +0000 (16:51 +0800)
committerUlf Hansson <ulf.hansson@linaro.org>
Fri, 27 Mar 2020 12:21:30 +0000 (13:21 +0100)
SDIO irq is not triggered by low level, but by falling edge in our
previous IC. This mechanism only have one chance to catch the SDIO irq
if a SDIO irq comes within the multiple block transmission. This SDIO
irq may be easily lost, because falling edge appears only once within 2
clock after data transmission is completed.

SDIO irq recheck mechanism will make sure all irqs can be
processed correctly.

Signed-off-by: Yong Mao <yong.mao@mediatek.com>
Link: https://lore.kernel.org/r/1585299097-6897-2-git-send-email-yong.mao@mediatek.com
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/mtk-sd.c

index 7726dcf48f2ce14ad9241b3eaca1bcf772dfd239..b221c02cc71f8ffdfbfd57af1c16e5bbe03c78e9 100644 (file)
 #define MSDC_PS_CDSTS           (0x1 << 1)     /* R  */
 #define MSDC_PS_CDDEBOUNCE      (0xf << 12)    /* RW */
 #define MSDC_PS_DAT             (0xff << 16)   /* R  */
+#define MSDC_PS_DATA1           (0x1 << 17)    /* R  */
 #define MSDC_PS_CMD             (0x1 << 24)    /* R  */
 #define MSDC_PS_WP              (0x1 << 31)    /* R  */
 
@@ -361,6 +362,7 @@ struct msdc_save_para {
 
 struct mtk_mmc_compatible {
        u8 clk_div_bits;
+       bool recheck_sdio_irq;
        bool hs400_tune; /* only used for MT8173 */
        u32 pad_tune_reg;
        bool async_fifo;
@@ -436,6 +438,7 @@ struct msdc_host {
 
 static const struct mtk_mmc_compatible mt8135_compat = {
        .clk_div_bits = 8,
+       .recheck_sdio_irq = false,
        .hs400_tune = false,
        .pad_tune_reg = MSDC_PAD_TUNE,
        .async_fifo = false,
@@ -448,6 +451,7 @@ static const struct mtk_mmc_compatible mt8135_compat = {
 
 static const struct mtk_mmc_compatible mt8173_compat = {
        .clk_div_bits = 8,
+       .recheck_sdio_irq = true,
        .hs400_tune = true,
        .pad_tune_reg = MSDC_PAD_TUNE,
        .async_fifo = false,
@@ -460,6 +464,7 @@ static const struct mtk_mmc_compatible mt8173_compat = {
 
 static const struct mtk_mmc_compatible mt8183_compat = {
        .clk_div_bits = 12,
+       .recheck_sdio_irq = false,
        .hs400_tune = false,
        .pad_tune_reg = MSDC_PAD_TUNE0,
        .async_fifo = true,
@@ -472,6 +477,7 @@ static const struct mtk_mmc_compatible mt8183_compat = {
 
 static const struct mtk_mmc_compatible mt2701_compat = {
        .clk_div_bits = 12,
+       .recheck_sdio_irq = false,
        .hs400_tune = false,
        .pad_tune_reg = MSDC_PAD_TUNE0,
        .async_fifo = true,
@@ -484,6 +490,7 @@ static const struct mtk_mmc_compatible mt2701_compat = {
 
 static const struct mtk_mmc_compatible mt2712_compat = {
        .clk_div_bits = 12,
+       .recheck_sdio_irq = false,
        .hs400_tune = false,
        .pad_tune_reg = MSDC_PAD_TUNE0,
        .async_fifo = true,
@@ -496,6 +503,7 @@ static const struct mtk_mmc_compatible mt2712_compat = {
 
 static const struct mtk_mmc_compatible mt7622_compat = {
        .clk_div_bits = 12,
+       .recheck_sdio_irq = false,
        .hs400_tune = false,
        .pad_tune_reg = MSDC_PAD_TUNE0,
        .async_fifo = true,
@@ -508,6 +516,7 @@ static const struct mtk_mmc_compatible mt7622_compat = {
 
 static const struct mtk_mmc_compatible mt8516_compat = {
        .clk_div_bits = 12,
+       .recheck_sdio_irq = false,
        .hs400_tune = false,
        .pad_tune_reg = MSDC_PAD_TUNE0,
        .async_fifo = true,
@@ -518,6 +527,7 @@ static const struct mtk_mmc_compatible mt8516_compat = {
 
 static const struct mtk_mmc_compatible mt7620_compat = {
        .clk_div_bits = 8,
+       .recheck_sdio_irq = false,
        .hs400_tune = false,
        .pad_tune_reg = MSDC_PAD_TUNE,
        .async_fifo = false,
@@ -591,6 +601,7 @@ static void msdc_reset_hw(struct msdc_host *host)
 
 static void msdc_cmd_next(struct msdc_host *host,
                struct mmc_request *mrq, struct mmc_command *cmd);
+static void __msdc_enable_sdio_irq(struct msdc_host *host, int enb);
 
 static const u32 cmd_ints_mask = MSDC_INTEN_CMDRDY | MSDC_INTEN_RSPCRCERR |
                        MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY |
@@ -1007,6 +1018,32 @@ static int msdc_auto_cmd_done(struct msdc_host *host, int events,
        return cmd->error;
 }
 
+/**
+ * msdc_recheck_sdio_irq - recheck whether the SDIO irq is lost
+ *
+ * Host controller may lost interrupt in some special case.
+ * Add SDIO irq recheck mechanism to make sure all interrupts
+ * can be processed immediately
+ *
+ */
+static void msdc_recheck_sdio_irq(struct msdc_host *host)
+{
+       u32 reg_int, reg_inten, reg_ps;
+
+       if (host->mmc->caps & MMC_CAP_SDIO_IRQ) {
+               reg_inten = readl(host->base + MSDC_INTEN);
+               if (reg_inten & MSDC_INTEN_SDIOIRQ) {
+                       reg_int = readl(host->base + MSDC_INT);
+                       reg_ps = readl(host->base + MSDC_PS);
+                       if (!(reg_int & MSDC_INT_SDIOIRQ ||
+                             reg_ps & MSDC_PS_DATA1)) {
+                               __msdc_enable_sdio_irq(host, 0);
+                               sdio_signal_irq(host->mmc);
+                       }
+               }
+       }
+}
+
 static void msdc_track_cmd_data(struct msdc_host *host,
                                struct mmc_command *cmd, struct mmc_data *data)
 {
@@ -1035,6 +1072,8 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
        if (host->error)
                msdc_reset_hw(host);
        mmc_request_done(host->mmc, mrq);
+       if (host->dev_comp->recheck_sdio_irq)
+               msdc_recheck_sdio_irq(host);
 }
 
 /* returns true if command is fully handled; returns false otherwise */
@@ -1393,6 +1432,8 @@ 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);
+               if (host->dev_comp->recheck_sdio_irq)
+                       msdc_recheck_sdio_irq(host);
        } else {
                sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ);
                sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);