Message ID | 1478585341-6749-3-git-send-email-yong.mao@mediatek.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Tue, Nov 8, 2016 at 2:08 PM, Yong Mao <yong.mao@mediatek.com> wrote: > From: yong mao <yong.mao@mediatek.com> > > 1. Add irqlock to protect accessing the shared register > 2. Modify the implementation of msdc_card_busy due to SDIO > 3. Implement enable_sdio_irq > 4. Add msdc_recheck_sdio_irq mechanism to make sure all > interrupts can be processed immediately > > Signed-off-by: Yong Mao <yong.mao@mediatek.com> > Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com> > --- > drivers/mmc/host/mtk-sd.c | 167 ++++++++++++++++++++++++++++++++++----------- > 1 file changed, 129 insertions(+), 38 deletions(-) > > diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c > index b29683b..37edf30 100644 > --- a/drivers/mmc/host/mtk-sd.c > +++ b/drivers/mmc/host/mtk-sd.c > @@ -117,6 +117,7 @@ > #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 */ > > @@ -304,6 +305,7 @@ struct msdc_host { > int cmd_rsp; > > spinlock_t lock; > + spinlock_t irqlock; /* sdio irq lock */ > struct mmc_request *mrq; > struct mmc_command *cmd; > struct mmc_data *data; > @@ -322,12 +324,14 @@ struct msdc_host { > struct pinctrl_state *pins_uhs; > struct delayed_work req_timeout; > int irq; /* host interrupt */ > + bool irq_thread_alive; > > struct clk *src_clk; /* msdc source clock */ > struct clk *h_clk; /* msdc h_clk */ > u32 mclk; /* mmc subsystem clock frequency */ > u32 src_clk_freq; /* source clock frequency */ > u32 sclk; /* SD/MS bus clock frequency */ > + bool clock_on; > unsigned char timing; > bool vqmmc_enabled; > u32 hs400_ds_delay; > @@ -387,6 +391,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_recheck_sdio_irq(struct msdc_host *host); > > static const u32 cmd_ints_mask = MSDC_INTEN_CMDRDY | MSDC_INTEN_RSPCRCERR | > MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY | > @@ -513,6 +518,7 @@ static void msdc_gate_clock(struct msdc_host *host) > { > clk_disable_unprepare(host->src_clk); > clk_disable_unprepare(host->h_clk); > + host->clock_on = false; > } > > static void msdc_ungate_clock(struct msdc_host *host) > @@ -521,6 +527,7 @@ static void msdc_ungate_clock(struct msdc_host *host) > clk_prepare_enable(host->src_clk); > while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB)) > cpu_relax(); > + host->clock_on = true; > } > > static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) > @@ -529,6 +536,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) > u32 flags; > u32 div; > u32 sclk; > + unsigned long irq_flags; > > if (!hz) { > dev_dbg(host->dev, "set mclk to 0\n"); > @@ -537,8 +545,11 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) > return; > } > > + spin_lock_irqsave(&host->irqlock, irq_flags); > flags = readl(host->base + MSDC_INTEN); > sdr_clr_bits(host->base + MSDC_INTEN, flags); > + spin_unlock_irqrestore(&host->irqlock, irq_flags); > + > sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_HS400_CK_MODE); > if (timing == MMC_TIMING_UHS_DDR50 || > timing == MMC_TIMING_MMC_DDR52 || > @@ -588,7 +599,10 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) > host->timing = timing; > /* need because clk changed. */ > msdc_set_timeout(host, host->timeout_ns, host->timeout_clks); > + > + spin_lock_irqsave(&host->irqlock, irq_flags); > sdr_set_bits(host->base + MSDC_INTEN, flags); > + spin_unlock_irqrestore(&host->irqlock, irq_flags); > > /* > * mmc_select_hs400() will drop to 50Mhz and High speed mode, > @@ -690,6 +704,7 @@ static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host, > static void msdc_start_data(struct msdc_host *host, struct mmc_request *mrq, > struct mmc_command *cmd, struct mmc_data *data) > { > + unsigned long flags; > bool read; > > WARN_ON(host->data); > @@ -698,8 +713,12 @@ static void msdc_start_data(struct msdc_host *host, struct mmc_request *mrq, > > mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT); > msdc_dma_setup(host, &host->dma, data); > + > + spin_lock_irqsave(&host->irqlock, flags); > sdr_set_bits(host->base + MSDC_INTEN, data_ints_mask); > sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1); > + spin_unlock_irqrestore(&host->irqlock, flags); > + > dev_dbg(host->dev, "DMA start\n"); > dev_dbg(host->dev, "%s: cmd=%d DMA data: %d blocks; read=%d\n", > __func__, cmd->opcode, data->blocks, read); > @@ -756,6 +775,7 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq) > if (mrq->data) > msdc_unprepare_data(host, mrq); > mmc_request_done(host->mmc, mrq); > + msdc_recheck_sdio_irq(host); > } > > /* returns true if command is fully handled; returns false otherwise */ > @@ -779,15 +799,17 @@ static bool msdc_cmd_done(struct msdc_host *host, int events, > | MSDC_INT_CMDTMO))) > return done; > > - spin_lock_irqsave(&host->lock, flags); > done = !host->cmd; > + spin_lock_irqsave(&host->lock, flags); > host->cmd = NULL; > spin_unlock_irqrestore(&host->lock, flags); > > if (done) > return true; > > + spin_lock_irqsave(&host->irqlock, flags); > sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask); > + spin_unlock_irqrestore(&host->irqlock, flags); > > if (cmd->flags & MMC_RSP_PRESENT) { > if (cmd->flags & MMC_RSP_136) { > @@ -902,6 +924,7 @@ static inline bool msdc_cmd_is_ready(struct msdc_host *host, > static void msdc_start_command(struct msdc_host *host, > struct mmc_request *mrq, struct mmc_command *cmd) > { > + unsigned long flags; > u32 rawcmd; > > WARN_ON(host->cmd); > @@ -920,7 +943,10 @@ static void msdc_start_command(struct msdc_host *host, > rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd); > mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT); > > + spin_lock_irqsave(&host->irqlock, flags); > sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask); > + spin_unlock_irqrestore(&host->irqlock, flags); > + > writel(cmd->arg, host->base + SDC_ARG); > writel(rawcmd, host->base + SDC_CMD); > } > @@ -1013,8 +1039,8 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events, > | MSDC_INT_DMA_BDCSERR | MSDC_INT_DMA_GPDCSERR > | MSDC_INT_DMA_PROTECT); > > - spin_lock_irqsave(&host->lock, flags); > done = !host->data; > + spin_lock_irqsave(&host->lock, flags); > if (check_data) > host->data = NULL; > spin_unlock_irqrestore(&host->lock, flags); > @@ -1029,7 +1055,11 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events, > 1); > while (readl(host->base + MSDC_DMA_CFG) & MSDC_DMA_CFG_STS) > cpu_relax(); > + > + spin_lock_irqsave(&host->irqlock, flags); > sdr_clr_bits(host->base + MSDC_INTEN, data_ints_mask); > + spin_unlock_irqrestore(&host->irqlock, flags); > + > dev_dbg(host->dev, "DMA stop\n"); > > if ((events & MSDC_INT_XFER_COMPL) && (!stop || !stop->error)) { > @@ -1134,44 +1164,47 @@ static void msdc_request_timeout(struct work_struct *work) > > static irqreturn_t msdc_irq(int irq, void *dev_id) > { > + unsigned long flags; > struct msdc_host *host = (struct msdc_host *) dev_id; > + struct mmc_request *mrq; > + struct mmc_command *cmd; > + struct mmc_data *data; > + u32 events, event_mask; > + > + spin_lock_irqsave(&host->irqlock, flags); > + events = readl(host->base + MSDC_INT); > + event_mask = readl(host->base + MSDC_INTEN); > + /* clear interrupts */ > + writel(events & event_mask, host->base + MSDC_INT); > + > + mrq = host->mrq; > + cmd = host->cmd; > + data = host->data; > + spin_unlock_irqrestore(&host->irqlock, flags); > + > + if ((events & event_mask) & MSDC_INT_SDIOIRQ) { > + mmc_signal_sdio_irq(host->mmc); > + if (!mrq) > + return IRQ_HANDLED; > + } > > - while (true) { > - unsigned long flags; > - struct mmc_request *mrq; > - struct mmc_command *cmd; > - struct mmc_data *data; > - u32 events, event_mask; > - > - spin_lock_irqsave(&host->lock, flags); > - events = readl(host->base + MSDC_INT); > - event_mask = readl(host->base + MSDC_INTEN); > - /* clear interrupts */ > - writel(events & event_mask, host->base + MSDC_INT); > - > - mrq = host->mrq; > - cmd = host->cmd; > - data = host->data; > - spin_unlock_irqrestore(&host->lock, flags); > - > - if (!(events & event_mask)) > - break; > + if (!(events & (event_mask & ~MSDC_INT_SDIOIRQ))) > + return IRQ_HANDLED; > > - if (!mrq) { > - dev_err(host->dev, > - "%s: MRQ=NULL; events=%08X; event_mask=%08X\n", > - __func__, events, event_mask); > - WARN_ON(1); > - break; > - } > + if (!mrq) { > + dev_err(host->dev, > + "%s: MRQ=NULL; events=%08X; event_mask=%08X\n", > + __func__, events, event_mask); > + WARN_ON(1); > + return IRQ_HANDLED; > + } > > - dev_dbg(host->dev, "%s: events=%08X\n", __func__, events); > + dev_dbg(host->dev, "%s: events=%08X\n", __func__, events); > > - if (cmd) > - msdc_cmd_done(host, events, mrq, cmd); > - else if (data) > - msdc_data_xfer_done(host, events, mrq, data); > - } > + if (cmd) > + msdc_cmd_done(host, events, mrq, cmd); > + else if (data) > + msdc_data_xfer_done(host, events, mrq, data); > > return IRQ_HANDLED; > } > @@ -1179,6 +1212,7 @@ static irqreturn_t msdc_irq(int irq, void *dev_id) > static void msdc_init_hw(struct msdc_host *host) > { > u32 val; > + unsigned long flags; > > /* Configure to MMC/SD mode, clock free running */ > sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_MODE | MSDC_CFG_CKPDN); > @@ -1190,9 +1224,11 @@ static void msdc_init_hw(struct msdc_host *host) > sdr_clr_bits(host->base + MSDC_PS, MSDC_PS_CDEN); > > /* Disable and clear all interrupts */ > + spin_lock_irqsave(&host->irqlock, flags); > writel(0, host->base + MSDC_INTEN); > val = readl(host->base + MSDC_INT); > writel(val, host->base + MSDC_INT); > + spin_unlock_irqrestore(&host->irqlock, flags); > > writel(0, host->base + MSDC_PAD_TUNE); > writel(0, host->base + MSDC_IOCON); > @@ -1207,9 +1243,11 @@ static void msdc_init_hw(struct msdc_host *host) > */ > sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIO); > > - /* disable detect SDIO device interrupt function */ > - sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); > - > + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) > + sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); > + else > + /* disable detect SDIO device interrupt function */ > + 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); > > @@ -1221,11 +1259,15 @@ static void msdc_init_hw(struct msdc_host *host) > static void msdc_deinit_hw(struct msdc_host *host) > { > u32 val; > + unsigned long flags; > + > /* Disable and clear all interrupts */ > + spin_lock_irqsave(&host->irqlock, flags); > writel(0, host->base + MSDC_INTEN); > > val = readl(host->base + MSDC_INT); > writel(val, host->base + MSDC_INT); > + spin_unlock_irqrestore(&host->irqlock, flags); > } > > /* init gpd and bd list in msdc_drv_probe */ > @@ -1493,6 +1535,52 @@ static void msdc_hw_reset(struct mmc_host *mmc) > sdr_clr_bits(host->base + EMMC_IOCON, 1); > } > > +/** > + * msdc_recheck_sdio_irq - recheck whether the SDIO IRQ is lost > + * @host: The host to check. > + * > + * 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_ps; > + > + if (host->clock_on && > + (host->mmc->caps & MMC_CAP_SDIO_IRQ) && > + host->irq_thread_alive) { > + 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))) { > + mmc_signal_sdio_irq(host->mmc); > + } > + } > +} > + > +static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enable) > +{ > + unsigned long flags; > + struct msdc_host *host = mmc_priv(mmc); > + > + host->irq_thread_alive = true; > + if (enable) { > + pm_runtime_get_sync(host->dev);\ This cause problems in kernel >= 3.19. Since pm_runtime_get_sync calls __might_sleep, and you are not suppose to sleep in a IRQ handler. 2017-01-20T00:32:49.855603-08:00 WARNING kernel: [ 11.068860] do not call blocking ops when !TASK_RUNNING; state=1 set at [<ffffffc0007a350c>] sdio_irq_thread+0x11c/0x1dc ... 2017-01-20T00:32:49.856042-08:00 EMERG kernel: [ 12.136156] Call trace: 2017-01-20T00:32:49.856044-08:00 WARNING kernel: [ 12.138584] [<ffffffc000249d84>] __might_sleep+0x64/0x90 2017-01-20T00:32:49.856047-08:00 WARNING kernel: [ 12.143855] [<ffffffc000630f54>] __pm_runtime_resume+0x40/0x9c 2017-01-20T00:32:49.856049-08:00 WARNING kernel: [ 12.149644] [<ffffffc0007ae994>] msdc_enable_sdio_irq+0x44/0xd0 2017-01-20T00:32:49.856051-08:00 WARNING kernel: [ 12.155516] [<ffffffc0007a3544>] sdio_irq_thread+0x154/0x1dc 2017-01-20T00:32:49.856053-08:00 WARNING kernel: [ 12.161131] [<ffffffc00023f30c>] kthread+0x10c/0x114 2017-01-20T00:32:49.856055-08:00 WARNING kernel: [ 12.166056] [<ffffffc000203dd0>] ret_from_fork+0x10/0x40 2017-01-20T00:32:49.856057-08:00 WARNING kernel: [ 12.171368] sched: RT throttling activated for rt_rq ffffffc0fff5a5c0 (cpu 1) 2017-01-20T00:32:49.856059-08:00 WARNING kernel: [ 12.171368] potential CPU hogs: 2017-01-20T00:32:49.856061-08:00 WARNING kernel: [ 12.171368] ksdioirqd/mmc2 (1772) dw_mmc also enables autosuspend but it didn't call any pm_runtime_get* function in it's enable_sdio_irq function, I don't really know how it worked around this. Ulf, any suggestions on how we can do this? Wei-Ning > + msdc_recheck_sdio_irq(host); > + > + spin_lock_irqsave(&host->irqlock, flags); > + sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); > + sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ); > + spin_unlock_irqrestore(&host->irqlock, flags); > + } else { > + spin_lock_irqsave(&host->irqlock, flags); > + sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ); > + spin_unlock_irqrestore(&host->irqlock, flags); > + } > +} > + > static struct mmc_host_ops mt_msdc_ops = { > .post_req = msdc_post_req, > .pre_req = msdc_pre_req, > @@ -1504,6 +1592,7 @@ static void msdc_hw_reset(struct mmc_host *mmc) > .execute_tuning = msdc_execute_tuning, > .prepare_hs400_tuning = msdc_prepare_hs400_tuning, > .hw_reset = msdc_hw_reset, > + .enable_sdio_irq = msdc_enable_sdio_irq, > }; > > static int msdc_drv_probe(struct platform_device *pdev) > @@ -1600,6 +1689,7 @@ static int msdc_drv_probe(struct platform_device *pdev) > mmc_dev(mmc)->dma_mask = &host->dma_mask; > > host->timeout_clks = 3 * 1048576; > + host->irq_thread_alive = false; > host->dma.gpd = dma_alloc_coherent(&pdev->dev, > 2 * sizeof(struct mt_gpdma_desc), > &host->dma.gpd_addr, GFP_KERNEL); > @@ -1613,6 +1703,7 @@ static int msdc_drv_probe(struct platform_device *pdev) > msdc_init_gpd_bd(host, &host->dma); > INIT_DELAYED_WORK(&host->req_timeout, msdc_request_timeout); > spin_lock_init(&host->lock); > + spin_lock_init(&host->irqlock); > > platform_set_drvdata(pdev, mmc); > msdc_ungate_clock(host); > -- > 1.7.9.5 >
On Mon, Jan 23, 2017 at 7:24 PM, Wei-Ning Huang <wnhuang@google.com> wrote: > On Tue, Nov 8, 2016 at 2:08 PM, Yong Mao <yong.mao@mediatek.com> wrote: >> From: yong mao <yong.mao@mediatek.com> >> >> 1. Add irqlock to protect accessing the shared register >> 2. Modify the implementation of msdc_card_busy due to SDIO >> 3. Implement enable_sdio_irq >> 4. Add msdc_recheck_sdio_irq mechanism to make sure all >> interrupts can be processed immediately >> >> Signed-off-by: Yong Mao <yong.mao@mediatek.com> >> Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com> >> --- >> drivers/mmc/host/mtk-sd.c | 167 ++++++++++++++++++++++++++++++++++----------- >> 1 file changed, 129 insertions(+), 38 deletions(-) >> >> diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c >> index b29683b..37edf30 100644 >> --- a/drivers/mmc/host/mtk-sd.c >> +++ b/drivers/mmc/host/mtk-sd.c >> @@ -117,6 +117,7 @@ >> #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 */ >> >> @@ -304,6 +305,7 @@ struct msdc_host { >> int cmd_rsp; >> >> spinlock_t lock; >> + spinlock_t irqlock; /* sdio irq lock */ >> struct mmc_request *mrq; >> struct mmc_command *cmd; >> struct mmc_data *data; >> @@ -322,12 +324,14 @@ struct msdc_host { >> struct pinctrl_state *pins_uhs; >> struct delayed_work req_timeout; >> int irq; /* host interrupt */ >> + bool irq_thread_alive; >> >> struct clk *src_clk; /* msdc source clock */ >> struct clk *h_clk; /* msdc h_clk */ >> u32 mclk; /* mmc subsystem clock frequency */ >> u32 src_clk_freq; /* source clock frequency */ >> u32 sclk; /* SD/MS bus clock frequency */ >> + bool clock_on; >> unsigned char timing; >> bool vqmmc_enabled; >> u32 hs400_ds_delay; >> @@ -387,6 +391,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_recheck_sdio_irq(struct msdc_host *host); >> >> static const u32 cmd_ints_mask = MSDC_INTEN_CMDRDY | MSDC_INTEN_RSPCRCERR | >> MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY | >> @@ -513,6 +518,7 @@ static void msdc_gate_clock(struct msdc_host *host) >> { >> clk_disable_unprepare(host->src_clk); >> clk_disable_unprepare(host->h_clk); >> + host->clock_on = false; >> } >> >> static void msdc_ungate_clock(struct msdc_host *host) >> @@ -521,6 +527,7 @@ static void msdc_ungate_clock(struct msdc_host *host) >> clk_prepare_enable(host->src_clk); >> while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB)) >> cpu_relax(); >> + host->clock_on = true; >> } >> >> static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) >> @@ -529,6 +536,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) >> u32 flags; >> u32 div; >> u32 sclk; >> + unsigned long irq_flags; >> >> if (!hz) { >> dev_dbg(host->dev, "set mclk to 0\n"); >> @@ -537,8 +545,11 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) >> return; >> } >> >> + spin_lock_irqsave(&host->irqlock, irq_flags); >> flags = readl(host->base + MSDC_INTEN); >> sdr_clr_bits(host->base + MSDC_INTEN, flags); >> + spin_unlock_irqrestore(&host->irqlock, irq_flags); >> + >> sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_HS400_CK_MODE); >> if (timing == MMC_TIMING_UHS_DDR50 || >> timing == MMC_TIMING_MMC_DDR52 || >> @@ -588,7 +599,10 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) >> host->timing = timing; >> /* need because clk changed. */ >> msdc_set_timeout(host, host->timeout_ns, host->timeout_clks); >> + >> + spin_lock_irqsave(&host->irqlock, irq_flags); >> sdr_set_bits(host->base + MSDC_INTEN, flags); >> + spin_unlock_irqrestore(&host->irqlock, irq_flags); >> >> /* >> * mmc_select_hs400() will drop to 50Mhz and High speed mode, >> @@ -690,6 +704,7 @@ static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host, >> static void msdc_start_data(struct msdc_host *host, struct mmc_request *mrq, >> struct mmc_command *cmd, struct mmc_data *data) >> { >> + unsigned long flags; >> bool read; >> >> WARN_ON(host->data); >> @@ -698,8 +713,12 @@ static void msdc_start_data(struct msdc_host *host, struct mmc_request *mrq, >> >> mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT); >> msdc_dma_setup(host, &host->dma, data); >> + >> + spin_lock_irqsave(&host->irqlock, flags); >> sdr_set_bits(host->base + MSDC_INTEN, data_ints_mask); >> sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1); >> + spin_unlock_irqrestore(&host->irqlock, flags); >> + >> dev_dbg(host->dev, "DMA start\n"); >> dev_dbg(host->dev, "%s: cmd=%d DMA data: %d blocks; read=%d\n", >> __func__, cmd->opcode, data->blocks, read); >> @@ -756,6 +775,7 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq) >> if (mrq->data) >> msdc_unprepare_data(host, mrq); >> mmc_request_done(host->mmc, mrq); >> + msdc_recheck_sdio_irq(host); >> } >> >> /* returns true if command is fully handled; returns false otherwise */ >> @@ -779,15 +799,17 @@ static bool msdc_cmd_done(struct msdc_host *host, int events, >> | MSDC_INT_CMDTMO))) >> return done; >> >> - spin_lock_irqsave(&host->lock, flags); >> done = !host->cmd; >> + spin_lock_irqsave(&host->lock, flags); >> host->cmd = NULL; >> spin_unlock_irqrestore(&host->lock, flags); >> >> if (done) >> return true; >> >> + spin_lock_irqsave(&host->irqlock, flags); >> sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask); >> + spin_unlock_irqrestore(&host->irqlock, flags); >> >> if (cmd->flags & MMC_RSP_PRESENT) { >> if (cmd->flags & MMC_RSP_136) { >> @@ -902,6 +924,7 @@ static inline bool msdc_cmd_is_ready(struct msdc_host *host, >> static void msdc_start_command(struct msdc_host *host, >> struct mmc_request *mrq, struct mmc_command *cmd) >> { >> + unsigned long flags; >> u32 rawcmd; >> >> WARN_ON(host->cmd); >> @@ -920,7 +943,10 @@ static void msdc_start_command(struct msdc_host *host, >> rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd); >> mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT); >> >> + spin_lock_irqsave(&host->irqlock, flags); >> sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask); >> + spin_unlock_irqrestore(&host->irqlock, flags); >> + >> writel(cmd->arg, host->base + SDC_ARG); >> writel(rawcmd, host->base + SDC_CMD); >> } >> @@ -1013,8 +1039,8 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events, >> | MSDC_INT_DMA_BDCSERR | MSDC_INT_DMA_GPDCSERR >> | MSDC_INT_DMA_PROTECT); >> >> - spin_lock_irqsave(&host->lock, flags); >> done = !host->data; >> + spin_lock_irqsave(&host->lock, flags); >> if (check_data) >> host->data = NULL; >> spin_unlock_irqrestore(&host->lock, flags); >> @@ -1029,7 +1055,11 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events, >> 1); >> while (readl(host->base + MSDC_DMA_CFG) & MSDC_DMA_CFG_STS) >> cpu_relax(); >> + >> + spin_lock_irqsave(&host->irqlock, flags); >> sdr_clr_bits(host->base + MSDC_INTEN, data_ints_mask); >> + spin_unlock_irqrestore(&host->irqlock, flags); >> + >> dev_dbg(host->dev, "DMA stop\n"); >> >> if ((events & MSDC_INT_XFER_COMPL) && (!stop || !stop->error)) { >> @@ -1134,44 +1164,47 @@ static void msdc_request_timeout(struct work_struct *work) >> >> static irqreturn_t msdc_irq(int irq, void *dev_id) >> { >> + unsigned long flags; >> struct msdc_host *host = (struct msdc_host *) dev_id; >> + struct mmc_request *mrq; >> + struct mmc_command *cmd; >> + struct mmc_data *data; >> + u32 events, event_mask; >> + >> + spin_lock_irqsave(&host->irqlock, flags); >> + events = readl(host->base + MSDC_INT); >> + event_mask = readl(host->base + MSDC_INTEN); >> + /* clear interrupts */ >> + writel(events & event_mask, host->base + MSDC_INT); >> + >> + mrq = host->mrq; >> + cmd = host->cmd; >> + data = host->data; >> + spin_unlock_irqrestore(&host->irqlock, flags); >> + >> + if ((events & event_mask) & MSDC_INT_SDIOIRQ) { >> + mmc_signal_sdio_irq(host->mmc); >> + if (!mrq) >> + return IRQ_HANDLED; >> + } >> >> - while (true) { >> - unsigned long flags; >> - struct mmc_request *mrq; >> - struct mmc_command *cmd; >> - struct mmc_data *data; >> - u32 events, event_mask; >> - >> - spin_lock_irqsave(&host->lock, flags); >> - events = readl(host->base + MSDC_INT); >> - event_mask = readl(host->base + MSDC_INTEN); >> - /* clear interrupts */ >> - writel(events & event_mask, host->base + MSDC_INT); >> - >> - mrq = host->mrq; >> - cmd = host->cmd; >> - data = host->data; >> - spin_unlock_irqrestore(&host->lock, flags); >> - >> - if (!(events & event_mask)) >> - break; >> + if (!(events & (event_mask & ~MSDC_INT_SDIOIRQ))) >> + return IRQ_HANDLED; >> >> - if (!mrq) { >> - dev_err(host->dev, >> - "%s: MRQ=NULL; events=%08X; event_mask=%08X\n", >> - __func__, events, event_mask); >> - WARN_ON(1); >> - break; >> - } >> + if (!mrq) { >> + dev_err(host->dev, >> + "%s: MRQ=NULL; events=%08X; event_mask=%08X\n", >> + __func__, events, event_mask); >> + WARN_ON(1); >> + return IRQ_HANDLED; >> + } >> >> - dev_dbg(host->dev, "%s: events=%08X\n", __func__, events); >> + dev_dbg(host->dev, "%s: events=%08X\n", __func__, events); >> >> - if (cmd) >> - msdc_cmd_done(host, events, mrq, cmd); >> - else if (data) >> - msdc_data_xfer_done(host, events, mrq, data); >> - } >> + if (cmd) >> + msdc_cmd_done(host, events, mrq, cmd); >> + else if (data) >> + msdc_data_xfer_done(host, events, mrq, data); >> >> return IRQ_HANDLED; >> } >> @@ -1179,6 +1212,7 @@ static irqreturn_t msdc_irq(int irq, void *dev_id) >> static void msdc_init_hw(struct msdc_host *host) >> { >> u32 val; >> + unsigned long flags; >> >> /* Configure to MMC/SD mode, clock free running */ >> sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_MODE | MSDC_CFG_CKPDN); >> @@ -1190,9 +1224,11 @@ static void msdc_init_hw(struct msdc_host *host) >> sdr_clr_bits(host->base + MSDC_PS, MSDC_PS_CDEN); >> >> /* Disable and clear all interrupts */ >> + spin_lock_irqsave(&host->irqlock, flags); >> writel(0, host->base + MSDC_INTEN); >> val = readl(host->base + MSDC_INT); >> writel(val, host->base + MSDC_INT); >> + spin_unlock_irqrestore(&host->irqlock, flags); >> >> writel(0, host->base + MSDC_PAD_TUNE); >> writel(0, host->base + MSDC_IOCON); >> @@ -1207,9 +1243,11 @@ static void msdc_init_hw(struct msdc_host *host) >> */ >> sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIO); >> >> - /* disable detect SDIO device interrupt function */ >> - sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); >> - >> + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) >> + sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); >> + else >> + /* disable detect SDIO device interrupt function */ >> + 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); >> >> @@ -1221,11 +1259,15 @@ static void msdc_init_hw(struct msdc_host *host) >> static void msdc_deinit_hw(struct msdc_host *host) >> { >> u32 val; >> + unsigned long flags; >> + >> /* Disable and clear all interrupts */ >> + spin_lock_irqsave(&host->irqlock, flags); >> writel(0, host->base + MSDC_INTEN); >> >> val = readl(host->base + MSDC_INT); >> writel(val, host->base + MSDC_INT); >> + spin_unlock_irqrestore(&host->irqlock, flags); >> } >> >> /* init gpd and bd list in msdc_drv_probe */ >> @@ -1493,6 +1535,52 @@ static void msdc_hw_reset(struct mmc_host *mmc) >> sdr_clr_bits(host->base + EMMC_IOCON, 1); >> } >> >> +/** >> + * msdc_recheck_sdio_irq - recheck whether the SDIO IRQ is lost >> + * @host: The host to check. >> + * >> + * 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_ps; >> + >> + if (host->clock_on && >> + (host->mmc->caps & MMC_CAP_SDIO_IRQ) && >> + host->irq_thread_alive) { >> + 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))) { >> + mmc_signal_sdio_irq(host->mmc); >> + } >> + } >> +} >> + >> +static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enable) >> +{ >> + unsigned long flags; >> + struct msdc_host *host = mmc_priv(mmc); >> + >> + host->irq_thread_alive = true; >> + if (enable) { >> + pm_runtime_get_sync(host->dev);\ > This cause problems in kernel >= 3.19. Since pm_runtime_get_sync calls > __might_sleep, and you are not suppose to sleep in a IRQ handler. Ignore above, apparently I don't know what I'm talking about ... It's actually because the sdio_irq_thread set task state to TASK_INTERRUPTABLE before calling enable_sdio_irq: set_current_state(TASK_INTERRUPTIBLE); if (host->caps & MMC_CAP_SDIO_IRQ) host->ops->enable_sdio_irq(host, 1); if (!kthread_should_stop()) schedule_timeout(period); set_current_state(TASK_RUNNING); so we are not suppose to call __might_sleep in the enable_sdio_irq callback. > > 2017-01-20T00:32:49.855603-08:00 WARNING kernel: [ 11.068860] do not > call blocking ops when !TASK_RUNNING; state=1 set at > [<ffffffc0007a350c>] sdio_irq_thread+0x11c/0x1dc > ... > 2017-01-20T00:32:49.856042-08:00 EMERG kernel: [ 12.136156] Call > trace: > 2017-01-20T00:32:49.856044-08:00 WARNING kernel: [ 12.138584] > [<ffffffc000249d84>] __might_sleep+0x64/0x90 > 2017-01-20T00:32:49.856047-08:00 WARNING kernel: [ 12.143855] > [<ffffffc000630f54>] __pm_runtime_resume+0x40/0x9c > 2017-01-20T00:32:49.856049-08:00 WARNING kernel: [ 12.149644] > [<ffffffc0007ae994>] msdc_enable_sdio_irq+0x44/0xd0 > 2017-01-20T00:32:49.856051-08:00 WARNING kernel: [ 12.155516] > [<ffffffc0007a3544>] sdio_irq_thread+0x154/0x1dc > 2017-01-20T00:32:49.856053-08:00 WARNING kernel: [ 12.161131] > [<ffffffc00023f30c>] kthread+0x10c/0x114 > 2017-01-20T00:32:49.856055-08:00 WARNING kernel: [ 12.166056] > [<ffffffc000203dd0>] ret_from_fork+0x10/0x40 > 2017-01-20T00:32:49.856057-08:00 WARNING kernel: [ 12.171368] sched: > RT throttling activated for rt_rq ffffffc0fff5a5c0 (cpu 1) > 2017-01-20T00:32:49.856059-08:00 WARNING kernel: [ 12.171368] > potential CPU hogs: > 2017-01-20T00:32:49.856061-08:00 WARNING kernel: [ 12.171368] > ksdioirqd/mmc2 (1772) > > dw_mmc also enables autosuspend but it didn't call any pm_runtime_get* > function in it's enable_sdio_irq function, I don't really know how it > worked around this. > Ulf, any suggestions on how we can do this? > > Wei-Ning > >> + msdc_recheck_sdio_irq(host); >> + >> + spin_lock_irqsave(&host->irqlock, flags); >> + sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); >> + sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ); >> + spin_unlock_irqrestore(&host->irqlock, flags); >> + } else { >> + spin_lock_irqsave(&host->irqlock, flags); >> + sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ); >> + spin_unlock_irqrestore(&host->irqlock, flags); >> + } >> +} >> + >> static struct mmc_host_ops mt_msdc_ops = { >> .post_req = msdc_post_req, >> .pre_req = msdc_pre_req, >> @@ -1504,6 +1592,7 @@ static void msdc_hw_reset(struct mmc_host *mmc) >> .execute_tuning = msdc_execute_tuning, >> .prepare_hs400_tuning = msdc_prepare_hs400_tuning, >> .hw_reset = msdc_hw_reset, >> + .enable_sdio_irq = msdc_enable_sdio_irq, >> }; >> >> static int msdc_drv_probe(struct platform_device *pdev) >> @@ -1600,6 +1689,7 @@ static int msdc_drv_probe(struct platform_device *pdev) >> mmc_dev(mmc)->dma_mask = &host->dma_mask; >> >> host->timeout_clks = 3 * 1048576; >> + host->irq_thread_alive = false; >> host->dma.gpd = dma_alloc_coherent(&pdev->dev, >> 2 * sizeof(struct mt_gpdma_desc), >> &host->dma.gpd_addr, GFP_KERNEL); >> @@ -1613,6 +1703,7 @@ static int msdc_drv_probe(struct platform_device *pdev) >> msdc_init_gpd_bd(host, &host->dma); >> INIT_DELAYED_WORK(&host->req_timeout, msdc_request_timeout); >> spin_lock_init(&host->lock); >> + spin_lock_init(&host->irqlock); >> >> platform_set_drvdata(pdev, mmc); >> msdc_ungate_clock(host); >> -- >> 1.7.9.5 >> > > > > -- > Wei-Ning Huang, 黃偉寧 | Software Engineer, Google Inc., Taiwan | > wnhuang@google.com | Cell: +886 910-380678
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index b29683b..37edf30 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -117,6 +117,7 @@ #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 */ @@ -304,6 +305,7 @@ struct msdc_host { int cmd_rsp; spinlock_t lock; + spinlock_t irqlock; /* sdio irq lock */ struct mmc_request *mrq; struct mmc_command *cmd; struct mmc_data *data; @@ -322,12 +324,14 @@ struct msdc_host { struct pinctrl_state *pins_uhs; struct delayed_work req_timeout; int irq; /* host interrupt */ + bool irq_thread_alive; struct clk *src_clk; /* msdc source clock */ struct clk *h_clk; /* msdc h_clk */ u32 mclk; /* mmc subsystem clock frequency */ u32 src_clk_freq; /* source clock frequency */ u32 sclk; /* SD/MS bus clock frequency */ + bool clock_on; unsigned char timing; bool vqmmc_enabled; u32 hs400_ds_delay; @@ -387,6 +391,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_recheck_sdio_irq(struct msdc_host *host); static const u32 cmd_ints_mask = MSDC_INTEN_CMDRDY | MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY | @@ -513,6 +518,7 @@ static void msdc_gate_clock(struct msdc_host *host) { clk_disable_unprepare(host->src_clk); clk_disable_unprepare(host->h_clk); + host->clock_on = false; } static void msdc_ungate_clock(struct msdc_host *host) @@ -521,6 +527,7 @@ static void msdc_ungate_clock(struct msdc_host *host) clk_prepare_enable(host->src_clk); while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB)) cpu_relax(); + host->clock_on = true; } static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) @@ -529,6 +536,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) u32 flags; u32 div; u32 sclk; + unsigned long irq_flags; if (!hz) { dev_dbg(host->dev, "set mclk to 0\n"); @@ -537,8 +545,11 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) return; } + spin_lock_irqsave(&host->irqlock, irq_flags); flags = readl(host->base + MSDC_INTEN); sdr_clr_bits(host->base + MSDC_INTEN, flags); + spin_unlock_irqrestore(&host->irqlock, irq_flags); + sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_HS400_CK_MODE); if (timing == MMC_TIMING_UHS_DDR50 || timing == MMC_TIMING_MMC_DDR52 || @@ -588,7 +599,10 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) host->timing = timing; /* need because clk changed. */ msdc_set_timeout(host, host->timeout_ns, host->timeout_clks); + + spin_lock_irqsave(&host->irqlock, irq_flags); sdr_set_bits(host->base + MSDC_INTEN, flags); + spin_unlock_irqrestore(&host->irqlock, irq_flags); /* * mmc_select_hs400() will drop to 50Mhz and High speed mode, @@ -690,6 +704,7 @@ static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host, static void msdc_start_data(struct msdc_host *host, struct mmc_request *mrq, struct mmc_command *cmd, struct mmc_data *data) { + unsigned long flags; bool read; WARN_ON(host->data); @@ -698,8 +713,12 @@ static void msdc_start_data(struct msdc_host *host, struct mmc_request *mrq, mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT); msdc_dma_setup(host, &host->dma, data); + + spin_lock_irqsave(&host->irqlock, flags); sdr_set_bits(host->base + MSDC_INTEN, data_ints_mask); sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1); + spin_unlock_irqrestore(&host->irqlock, flags); + dev_dbg(host->dev, "DMA start\n"); dev_dbg(host->dev, "%s: cmd=%d DMA data: %d blocks; read=%d\n", __func__, cmd->opcode, data->blocks, read); @@ -756,6 +775,7 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq) if (mrq->data) msdc_unprepare_data(host, mrq); mmc_request_done(host->mmc, mrq); + msdc_recheck_sdio_irq(host); } /* returns true if command is fully handled; returns false otherwise */ @@ -779,15 +799,17 @@ static bool msdc_cmd_done(struct msdc_host *host, int events, | MSDC_INT_CMDTMO))) return done; - spin_lock_irqsave(&host->lock, flags); done = !host->cmd; + spin_lock_irqsave(&host->lock, flags); host->cmd = NULL; spin_unlock_irqrestore(&host->lock, flags); if (done) return true; + spin_lock_irqsave(&host->irqlock, flags); sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask); + spin_unlock_irqrestore(&host->irqlock, flags); if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_136) { @@ -902,6 +924,7 @@ static inline bool msdc_cmd_is_ready(struct msdc_host *host, static void msdc_start_command(struct msdc_host *host, struct mmc_request *mrq, struct mmc_command *cmd) { + unsigned long flags; u32 rawcmd; WARN_ON(host->cmd); @@ -920,7 +943,10 @@ static void msdc_start_command(struct msdc_host *host, rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd); mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT); + spin_lock_irqsave(&host->irqlock, flags); sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask); + spin_unlock_irqrestore(&host->irqlock, flags); + writel(cmd->arg, host->base + SDC_ARG); writel(rawcmd, host->base + SDC_CMD); } @@ -1013,8 +1039,8 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events, | MSDC_INT_DMA_BDCSERR | MSDC_INT_DMA_GPDCSERR | MSDC_INT_DMA_PROTECT); - spin_lock_irqsave(&host->lock, flags); done = !host->data; + spin_lock_irqsave(&host->lock, flags); if (check_data) host->data = NULL; spin_unlock_irqrestore(&host->lock, flags); @@ -1029,7 +1055,11 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events, 1); while (readl(host->base + MSDC_DMA_CFG) & MSDC_DMA_CFG_STS) cpu_relax(); + + spin_lock_irqsave(&host->irqlock, flags); sdr_clr_bits(host->base + MSDC_INTEN, data_ints_mask); + spin_unlock_irqrestore(&host->irqlock, flags); + dev_dbg(host->dev, "DMA stop\n"); if ((events & MSDC_INT_XFER_COMPL) && (!stop || !stop->error)) { @@ -1134,44 +1164,47 @@ static void msdc_request_timeout(struct work_struct *work) static irqreturn_t msdc_irq(int irq, void *dev_id) { + unsigned long flags; struct msdc_host *host = (struct msdc_host *) dev_id; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + u32 events, event_mask; + + spin_lock_irqsave(&host->irqlock, flags); + events = readl(host->base + MSDC_INT); + event_mask = readl(host->base + MSDC_INTEN); + /* clear interrupts */ + writel(events & event_mask, host->base + MSDC_INT); + + mrq = host->mrq; + cmd = host->cmd; + data = host->data; + spin_unlock_irqrestore(&host->irqlock, flags); + + if ((events & event_mask) & MSDC_INT_SDIOIRQ) { + mmc_signal_sdio_irq(host->mmc); + if (!mrq) + return IRQ_HANDLED; + } - while (true) { - unsigned long flags; - struct mmc_request *mrq; - struct mmc_command *cmd; - struct mmc_data *data; - u32 events, event_mask; - - spin_lock_irqsave(&host->lock, flags); - events = readl(host->base + MSDC_INT); - event_mask = readl(host->base + MSDC_INTEN); - /* clear interrupts */ - writel(events & event_mask, host->base + MSDC_INT); - - mrq = host->mrq; - cmd = host->cmd; - data = host->data; - spin_unlock_irqrestore(&host->lock, flags); - - if (!(events & event_mask)) - break; + if (!(events & (event_mask & ~MSDC_INT_SDIOIRQ))) + return IRQ_HANDLED; - if (!mrq) { - dev_err(host->dev, - "%s: MRQ=NULL; events=%08X; event_mask=%08X\n", - __func__, events, event_mask); - WARN_ON(1); - break; - } + if (!mrq) { + dev_err(host->dev, + "%s: MRQ=NULL; events=%08X; event_mask=%08X\n", + __func__, events, event_mask); + WARN_ON(1); + return IRQ_HANDLED; + } - dev_dbg(host->dev, "%s: events=%08X\n", __func__, events); + dev_dbg(host->dev, "%s: events=%08X\n", __func__, events); - if (cmd) - msdc_cmd_done(host, events, mrq, cmd); - else if (data) - msdc_data_xfer_done(host, events, mrq, data); - } + if (cmd) + msdc_cmd_done(host, events, mrq, cmd); + else if (data) + msdc_data_xfer_done(host, events, mrq, data); return IRQ_HANDLED; } @@ -1179,6 +1212,7 @@ static irqreturn_t msdc_irq(int irq, void *dev_id) static void msdc_init_hw(struct msdc_host *host) { u32 val; + unsigned long flags; /* Configure to MMC/SD mode, clock free running */ sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_MODE | MSDC_CFG_CKPDN); @@ -1190,9 +1224,11 @@ static void msdc_init_hw(struct msdc_host *host) sdr_clr_bits(host->base + MSDC_PS, MSDC_PS_CDEN); /* Disable and clear all interrupts */ + spin_lock_irqsave(&host->irqlock, flags); writel(0, host->base + MSDC_INTEN); val = readl(host->base + MSDC_INT); writel(val, host->base + MSDC_INT); + spin_unlock_irqrestore(&host->irqlock, flags); writel(0, host->base + MSDC_PAD_TUNE); writel(0, host->base + MSDC_IOCON); @@ -1207,9 +1243,11 @@ static void msdc_init_hw(struct msdc_host *host) */ sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIO); - /* disable detect SDIO device interrupt function */ - sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); - + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) + sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); + else + /* disable detect SDIO device interrupt function */ + 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); @@ -1221,11 +1259,15 @@ static void msdc_init_hw(struct msdc_host *host) static void msdc_deinit_hw(struct msdc_host *host) { u32 val; + unsigned long flags; + /* Disable and clear all interrupts */ + spin_lock_irqsave(&host->irqlock, flags); writel(0, host->base + MSDC_INTEN); val = readl(host->base + MSDC_INT); writel(val, host->base + MSDC_INT); + spin_unlock_irqrestore(&host->irqlock, flags); } /* init gpd and bd list in msdc_drv_probe */ @@ -1493,6 +1535,52 @@ static void msdc_hw_reset(struct mmc_host *mmc) sdr_clr_bits(host->base + EMMC_IOCON, 1); } +/** + * msdc_recheck_sdio_irq - recheck whether the SDIO IRQ is lost + * @host: The host to check. + * + * 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_ps; + + if (host->clock_on && + (host->mmc->caps & MMC_CAP_SDIO_IRQ) && + host->irq_thread_alive) { + 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))) { + mmc_signal_sdio_irq(host->mmc); + } + } +} + +static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + unsigned long flags; + struct msdc_host *host = mmc_priv(mmc); + + host->irq_thread_alive = true; + if (enable) { + pm_runtime_get_sync(host->dev); + msdc_recheck_sdio_irq(host); + + spin_lock_irqsave(&host->irqlock, flags); + sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); + sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ); + spin_unlock_irqrestore(&host->irqlock, flags); + } else { + spin_lock_irqsave(&host->irqlock, flags); + sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ); + spin_unlock_irqrestore(&host->irqlock, flags); + } +} + static struct mmc_host_ops mt_msdc_ops = { .post_req = msdc_post_req, .pre_req = msdc_pre_req, @@ -1504,6 +1592,7 @@ static void msdc_hw_reset(struct mmc_host *mmc) .execute_tuning = msdc_execute_tuning, .prepare_hs400_tuning = msdc_prepare_hs400_tuning, .hw_reset = msdc_hw_reset, + .enable_sdio_irq = msdc_enable_sdio_irq, }; static int msdc_drv_probe(struct platform_device *pdev) @@ -1600,6 +1689,7 @@ static int msdc_drv_probe(struct platform_device *pdev) mmc_dev(mmc)->dma_mask = &host->dma_mask; host->timeout_clks = 3 * 1048576; + host->irq_thread_alive = false; host->dma.gpd = dma_alloc_coherent(&pdev->dev, 2 * sizeof(struct mt_gpdma_desc), &host->dma.gpd_addr, GFP_KERNEL); @@ -1613,6 +1703,7 @@ static int msdc_drv_probe(struct platform_device *pdev) msdc_init_gpd_bd(host, &host->dma); INIT_DELAYED_WORK(&host->req_timeout, msdc_request_timeout); spin_lock_init(&host->lock); + spin_lock_init(&host->irqlock); platform_set_drvdata(pdev, mmc); msdc_ungate_clock(host);