On 18 April 2017 at 12:13, Yong Mao <yong.mao@xxxxxxxxxxxx> wrote: > From: yong mao <yong.mao@xxxxxxxxxxxx> > > 1. Add irqlock to protect accessing the shared register > 2. Implement enable_sdio_irq interface > 3. Add msdc_recheck_sdio_irq mechanism to make sure all interrupts > can be processed immediately I think this should be split up in more pieces. Perhaps three as the changelog describes. Moreover I would appreciate some more information about why/how. > > Signed-off-by: Yong Mao <yong.mao@xxxxxxxxxxxx> > Signed-off-by: Chaotian Jing <chaotian.jing@xxxxxxxxxxxx> > --- > drivers/mmc/host/mtk-sd.c | 182 +++++++++++++++++++++++++++++++++++---------- > 1 file changed, 143 insertions(+), 39 deletions(-) > > diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c > index 07f3236..fdae197 100644 > --- a/drivers/mmc/host/mtk-sd.c > +++ b/drivers/mmc/host/mtk-sd.c > @@ -118,6 +118,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 */ > > @@ -312,6 +313,7 @@ struct msdc_host { > int cmd_rsp; > > spinlock_t lock; > + spinlock_t irqlock; /* irq lock */ > struct mmc_request *mrq; > struct mmc_command *cmd; > struct mmc_data *data; > @@ -330,12 +332,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; > @@ -343,6 +347,7 @@ struct msdc_host { > u32 hs400_cmd_int_delay; /* cmd internal delay for HS400 */ > bool hs400_cmd_resp_sel_rising; > /* cmd response sample selection for HS400 */ > + u32 clk_pad_delay; Parsing and using of pad_delay seems like it also should be a separate change. > bool hs400_mode; /* current eMMC will run at hs400 mode */ > struct msdc_save_para save_para; /* used when gate HCLK */ > struct msdc_tune_para def_tune_para; /* default tune setting */ > @@ -399,6 +404,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 | > @@ -525,6 +531,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; This looks weird. Why do you need to keep track of this? > } > > static void msdc_ungate_clock(struct msdc_host *host) > @@ -533,6 +540,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; Ditto. > } > > static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) > @@ -541,6 +549,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"); > @@ -549,8 +558,11 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) > return; > } > > + spin_lock_irqsave(&host->irqlock, irq_flags); Why is the spin_lock needed now, and not before. Could you elaborate on that? No matter what, seems like it should be separate change. > 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 || > @@ -600,7 +612,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, > @@ -708,6 +723,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); > @@ -716,8 +732,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); > @@ -774,6 +794,8 @@ 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); This I don't get. Why checking for SDIO IRQ here? Is it like an optimization thing or? > } > > /* returns true if command is fully handled; returns false otherwise */ > @@ -797,15 +819,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) { > @@ -883,6 +907,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); > @@ -901,7 +926,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); > } > @@ -993,8 +1021,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); > @@ -1009,7 +1037,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)) { > @@ -1123,44 +1155,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; > } > @@ -1168,6 +1203,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); > @@ -1179,11 +1215,14 @@ 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); > + sdr_set_field(host->base + MSDC_PAD_TUNE, > + MSDC_PAD_TUNE_CLKTDLY, host->clk_pad_delay); > writel(0, host->base + MSDC_IOCON); > sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0); > writel(0x403c0046, host->base + MSDC_PATCH_BIT); > @@ -1196,9 +1235,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); > > @@ -1210,11 +1251,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 */ > @@ -1582,6 +1627,48 @@ 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) { > + 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, > @@ -1593,6 +1680,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 void msdc_of_property_parse(struct platform_device *pdev, > @@ -1612,6 +1700,9 @@ static void msdc_of_property_parse(struct platform_device *pdev, > host->hs400_cmd_resp_sel_rising = true; > else > host->hs400_cmd_resp_sel_rising = false; > + > + of_property_read_u32(pdev->dev.of_node, "mediatek,clk-pad-delay", > + &host->clk_pad_delay); > } > > static int msdc_drv_probe(struct platform_device *pdev) > @@ -1705,6 +1796,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); > @@ -1718,6 +1810,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); I don't get why the host->lock can't be used here? Why do you need a new one? > > platform_set_drvdata(pdev, mmc); > msdc_ungate_clock(host); > @@ -1732,6 +1825,10 @@ static int msdc_drv_probe(struct platform_device *pdev) > pm_runtime_set_autosuspend_delay(host->dev, MTK_MMC_AUTOSUSPEND_DELAY); > pm_runtime_use_autosuspend(host->dev); > pm_runtime_enable(host->dev); > + > + /* In SDIO irq mode, DATA1 slways need to be detected */ > + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) > + pm_runtime_get_sync(host->dev); This seems reasonable, however I think we can make this more fine grained. It is actually not until there is a SDIO func driver that has claimed an SDIO IRQ, to when you need to make sure to keep the host runtime PM resumed. Please look into the following series [1], which I recently posted and try to see if using MMC_CAP2_SDIO_IRQ_NOTHREAD can suite you. In principle I think $subject patch can be greatly simplified if you convert to MMC_CAP2_SDIO_IRQ_NOTHREAD. > ret = mmc_add_host(mmc); > > if (ret) > @@ -1821,6 +1918,10 @@ static int msdc_runtime_suspend(struct device *dev) > > msdc_save_reg(host); > msdc_gate_clock(host); > + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) { > + pm_runtime_mark_last_busy(dev); > + pm_runtime_put_autosuspend(dev); > + } > return 0; > } > > @@ -1829,6 +1930,9 @@ static int msdc_runtime_resume(struct device *dev) > struct mmc_host *mmc = dev_get_drvdata(dev); > struct msdc_host *host = mmc_priv(mmc); > > + /* In SDIO irq mode, DATA1 slways need to be detected */ > + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) > + pm_runtime_get_sync(host->dev); > msdc_ungate_clock(host); > msdc_restore_reg(host); > return 0; > -- > 1.7.9.5 > Kind regards Uffe [1] https://www.spinics.net/lists/linux-mmc/msg43763.html -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html