Re: [PATCH v2 1/2] mmc: dw_mmc: fix bug that cause 'Timeout sending command'
On 2015/2/12 21:59, Alim Akhtar wrote: On Thu, Feb 12, 2015 at 4:40 PM, Andrzej Hajda a.ha...@samsung.com wrote: On 02/12/2015 03:28 AM, addy ke wrote: Hi Andrzej and Alim On 2015/2/12 07:20, Alim Akhtar wrote: Hi Andrzej, On Wed, Feb 11, 2015 at 5:28 PM, Andrzej Hajda a.ha...@samsung.com wrote: Hi Alim, On 02/11/2015 03:57 AM, Addy wrote: On 2015/02/10 23:22, Alim Akhtar wrote: Hi Addy, On Mon, Feb 9, 2015 at 12:55 PM, Addy Ke addy...@rock-chips.com wrote: Because of some uncertain factors, such as worse card or worse hardware, DAT[3:0](the data lines) may be pulled down by card, and mmc controller will be in busy state. This should not happend when mmc controller send command to update card clocks. If this happends, mci_send_cmd will be failed and we will get 'Timeout sending command', and then system will be blocked. To avoid this, we need reset mmc controller. Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 28 1 file changed, 28 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4d2e3c2..b0b57e3 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -100,6 +100,7 @@ struct idmac_desc { }; #endif /* CONFIG_MMC_DW_IDMAC */ +static int dw_mci_card_busy(struct mmc_host *mmc); static bool dw_mci_reset(struct dw_mci *host); static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); @@ -888,6 +889,31 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) cmd, arg, cmd_status); } +static void dw_mci_wait_busy(struct dw_mci_slot *slot) +{ + struct dw_mci *host = slot-host; + unsigned long timeout = jiffies + msecs_to_jiffies(500); + Why 500 msec? This timeout value is the same as mci_send_cmd: static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) { struct dw_mci *host = slot-host; unsigned long timeout = jiffies + msecs_to_jiffies(500); } I have not clear that which is suitable. Do you have any suggestion on it? + do { + if (!dw_mci_card_busy(slot-mmc)) + return; + cpu_relax(); + } while (time_before(jiffies, timeout)); + + dev_err(host-dev, Data busy (status %#x)\n, + mci_readl(slot-host, STATUS)); + + /* +* Data busy, this should not happend when mmc controller send command +* to update card clocks in non-volt-switch state. If it happends, we +* should reset controller to avoid getting Timeout sending command. +*/ + dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS); + Why you need to reset all blocks? may be CTRL_RESET is good enough here. I have tested on rk3288, if only reset ctroller, data busy bit will not be cleaned,and we will still get Timeout sending command. + /* Fail to reset controller or still data busy, WARN_ON! */ + WARN_ON(dw_mci_card_busy(slot-mmc)); +} + static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) { struct dw_mci *host = slot-host; @@ -899,6 +925,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* We must continue to set bit 28 in CMD until the change is complete */ if (host-state == STATE_WAITING_CMD11_DONE) sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; + else + dw_mci_wait_busy(slot); hmm...I would suggest you to call dw_mci_wait_busy() from inside mci_send_cmd(), seems like dw_mmc hangs while sending update clock cmd in multiple cases.see [1] [1]: http://permalink.gmane.org/gmane.linux.kernel.mmc/31140 I think this patch is more reasonable. So I will resend patches based on this patch. thank you! I have tested your patches instead [1] above and they do not solve my issue: Board: odroid-xu3/exynos5422/dw_mmc_250a. MMC card: absent, broken-cd quirk SD card: present I doubt $SUBJECT patch in current form can resolve you issue. I have already given comments on $subject patch. Can you try out below patch (I have not tested yet) on top of $SUBJECT patch? === diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b0b57e3..ea87844 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -101,6 +101,7 @@ struct idmac_desc { #endif /* CONFIG_MMC_DW_IDMAC */ static int dw_mci_card_busy(struct mmc_host *mmc); +static void dw_mci_wait_busy(struct dw_mci_slot *slot); static bool dw_mci_reset(struct dw_mci *host); static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); @@ -874,16 +875,22 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) struct dw_mci *host = slot-host; unsigned long timeout = jiffies + msecs_to_jiffies(500); unsigned int cmd_status = 0; + int re_try
Re: [PATCH v2 1/2] mmc: dw_mmc: fix bug that cause 'Timeout sending command'
Hi Andrzej and Alim On 2015/2/12 07:20, Alim Akhtar wrote: Hi Andrzej, On Wed, Feb 11, 2015 at 5:28 PM, Andrzej Hajda a.ha...@samsung.com wrote: Hi Alim, On 02/11/2015 03:57 AM, Addy wrote: On 2015/02/10 23:22, Alim Akhtar wrote: Hi Addy, On Mon, Feb 9, 2015 at 12:55 PM, Addy Ke addy...@rock-chips.com wrote: Because of some uncertain factors, such as worse card or worse hardware, DAT[3:0](the data lines) may be pulled down by card, and mmc controller will be in busy state. This should not happend when mmc controller send command to update card clocks. If this happends, mci_send_cmd will be failed and we will get 'Timeout sending command', and then system will be blocked. To avoid this, we need reset mmc controller. Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 28 1 file changed, 28 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4d2e3c2..b0b57e3 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -100,6 +100,7 @@ struct idmac_desc { }; #endif /* CONFIG_MMC_DW_IDMAC */ +static int dw_mci_card_busy(struct mmc_host *mmc); static bool dw_mci_reset(struct dw_mci *host); static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); @@ -888,6 +889,31 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) cmd, arg, cmd_status); } +static void dw_mci_wait_busy(struct dw_mci_slot *slot) +{ + struct dw_mci *host = slot-host; + unsigned long timeout = jiffies + msecs_to_jiffies(500); + Why 500 msec? This timeout value is the same as mci_send_cmd: static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) { struct dw_mci *host = slot-host; unsigned long timeout = jiffies + msecs_to_jiffies(500); } I have not clear that which is suitable. Do you have any suggestion on it? + do { + if (!dw_mci_card_busy(slot-mmc)) + return; + cpu_relax(); + } while (time_before(jiffies, timeout)); + + dev_err(host-dev, Data busy (status %#x)\n, + mci_readl(slot-host, STATUS)); + + /* +* Data busy, this should not happend when mmc controller send command +* to update card clocks in non-volt-switch state. If it happends, we +* should reset controller to avoid getting Timeout sending command. +*/ + dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS); + Why you need to reset all blocks? may be CTRL_RESET is good enough here. I have tested on rk3288, if only reset ctroller, data busy bit will not be cleaned,and we will still get Timeout sending command. + /* Fail to reset controller or still data busy, WARN_ON! */ + WARN_ON(dw_mci_card_busy(slot-mmc)); +} + static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) { struct dw_mci *host = slot-host; @@ -899,6 +925,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* We must continue to set bit 28 in CMD until the change is complete */ if (host-state == STATE_WAITING_CMD11_DONE) sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; + else + dw_mci_wait_busy(slot); hmm...I would suggest you to call dw_mci_wait_busy() from inside mci_send_cmd(), seems like dw_mmc hangs while sending update clock cmd in multiple cases.see [1] [1]: http://permalink.gmane.org/gmane.linux.kernel.mmc/31140 I think this patch is more reasonable. So I will resend patches based on this patch. thank you! I have tested your patches instead [1] above and they do not solve my issue: Board: odroid-xu3/exynos5422/dw_mmc_250a. MMC card: absent, broken-cd quirk SD card: present I doubt $SUBJECT patch in current form can resolve you issue. I have already given comments on $subject patch. Can you try out below patch (I have not tested yet) on top of $SUBJECT patch? === diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b0b57e3..ea87844 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -101,6 +101,7 @@ struct idmac_desc { #endif /* CONFIG_MMC_DW_IDMAC */ static int dw_mci_card_busy(struct mmc_host *mmc); +static void dw_mci_wait_busy(struct dw_mci_slot *slot); static bool dw_mci_reset(struct dw_mci *host); static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); @@ -874,16 +875,22 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) struct dw_mci *host = slot-host; unsigned long timeout = jiffies + msecs_to_jiffies(500); unsigned int cmd_status = 0; + int re_try = 3; /* just random for now, 1 re-try should be ok */ - mci_writel(host, CMDARG, arg); - wmb(); - mci_writel(host, CMD, SDMMC_CMD_START
Re: [PATCH] mmc: dw_mmc: fix bug that cause mmc_test failture
On 2015/02月10日 17:34, Olof Johansson wrote: Hi Addy, On Mon, Jan 26, 2015 at 4:04 AM, Addy Ke addy...@rock-chips.com wrote: The STOP command can terminate a data transfer between a memory card and mmc controller. As show in Synopsys DesignWare Cores Mobile Stroage Host Databook: Data timeout and Data end-bit error will terminate further data transfer by mmc controller. So we should not send abort command to terminate a data transfer again if we got DRTO and EBE interrupt. After this patch, all mmc_test cases can pass on RK3288-Pink2 board. Signed-off-by: Addy Ke addy...@rock-chips.com The drawback of having so many people on your to: list on a patch is that it's unclear who you want to review and merge it. Sometimes less is more. I will remove some unnecessary mail address from list in the following patch. Thank you. In this case, it seems appropriate to have Ulf do so. Ulf, ping? This seems like a reasonable patch for 3.20 given that it fixes undesired behavior. -Olof -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 1/2] mmc: dw_mmc: fix bug that cause 'Timeout sending command'
On 2015/02/09 18:01, Jaehoon Chung wrote: Hi, Addy. On 02/09/2015 04:25 PM, Addy Ke wrote: Because of some uncertain factors, such as worse card or worse hardware, DAT[3:0](the data lines) may be pulled down by card, and mmc controller will be in busy state. This should not happend when mmc controller send command to update card clocks. If this happends, mci_send_cmd will be failed and we will get 'Timeout sending command', and then system will be blocked. To avoid this, we need reset mmc controller. Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 28 1 file changed, 28 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4d2e3c2..b0b57e3 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -100,6 +100,7 @@ struct idmac_desc { }; #endif /* CONFIG_MMC_DW_IDMAC */ +static int dw_mci_card_busy(struct mmc_host *mmc); static bool dw_mci_reset(struct dw_mci *host); static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); @@ -888,6 +889,31 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) cmd, arg, cmd_status); } +static void dw_mci_wait_busy(struct dw_mci_slot *slot) +{ + struct dw_mci *host = slot-host; + unsigned long timeout = jiffies + msecs_to_jiffies(500); + + do { + if (!dw_mci_card_busy(slot-mmc)) + return; + cpu_relax(); + } while (time_before(jiffies, timeout)); + + dev_err(host-dev, Data busy (status %#x)\n, + mci_readl(slot-host, STATUS)); + + /* +* Data busy, this should not happend when mmc controller send command +* to update card clocks in non-volt-switch state. If it happends, we +* should reset controller to avoid getting Timeout sending command. +*/ + dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS); If reset is failed, then dw_mci_ctrl_reset should return false. ret = dw_mci_ctrl_reset(); WARN_ON(!ret || dw_mci_card_busy(slot-mmc)); Is it right? you are right, and I will update it in my next version patch. thank you. In my experiment, if reset is failed or card is busy, eMMC can't work anymore..right? I think this patch is reasonable to prevent blocking issue. Best Regards, Jaehoon Chung + + /* Fail to reset controller or still data busy, WARN_ON! */ + WARN_ON(dw_mci_card_busy(slot-mmc)); +} + static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) { struct dw_mci *host = slot-host; @@ -899,6 +925,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* We must continue to set bit 28 in CMD until the change is complete */ if (host-state == STATE_WAITING_CMD11_DONE) sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; + else + dw_mci_wait_busy(slot); if (!clock) { mci_writel(host, CLKENA, 0); -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 1/2] mmc: dw_mmc: fix bug that cause 'Timeout sending command'
On 2015/02/10 23:22, Alim Akhtar wrote: Hi Addy, On Mon, Feb 9, 2015 at 12:55 PM, Addy Ke addy...@rock-chips.com wrote: Because of some uncertain factors, such as worse card or worse hardware, DAT[3:0](the data lines) may be pulled down by card, and mmc controller will be in busy state. This should not happend when mmc controller send command to update card clocks. If this happends, mci_send_cmd will be failed and we will get 'Timeout sending command', and then system will be blocked. To avoid this, we need reset mmc controller. Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 28 1 file changed, 28 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4d2e3c2..b0b57e3 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -100,6 +100,7 @@ struct idmac_desc { }; #endif /* CONFIG_MMC_DW_IDMAC */ +static int dw_mci_card_busy(struct mmc_host *mmc); static bool dw_mci_reset(struct dw_mci *host); static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); @@ -888,6 +889,31 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) cmd, arg, cmd_status); } +static void dw_mci_wait_busy(struct dw_mci_slot *slot) +{ + struct dw_mci *host = slot-host; + unsigned long timeout = jiffies + msecs_to_jiffies(500); + Why 500 msec? This timeout value is the same as mci_send_cmd: static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) { struct dw_mci *host = slot-host; unsigned long timeout = jiffies + msecs_to_jiffies(500); } I have not clear that which is suitable. Do you have any suggestion on it? + do { + if (!dw_mci_card_busy(slot-mmc)) + return; + cpu_relax(); + } while (time_before(jiffies, timeout)); + + dev_err(host-dev, Data busy (status %#x)\n, + mci_readl(slot-host, STATUS)); + + /* +* Data busy, this should not happend when mmc controller send command +* to update card clocks in non-volt-switch state. If it happends, we +* should reset controller to avoid getting Timeout sending command. +*/ + dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS); + Why you need to reset all blocks? may be CTRL_RESET is good enough here. I have tested on rk3288, if only reset ctroller, data busy bit will not be cleaned,and we will still get Timeout sending command. + /* Fail to reset controller or still data busy, WARN_ON! */ + WARN_ON(dw_mci_card_busy(slot-mmc)); +} + static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) { struct dw_mci *host = slot-host; @@ -899,6 +925,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* We must continue to set bit 28 in CMD until the change is complete */ if (host-state == STATE_WAITING_CMD11_DONE) sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; + else + dw_mci_wait_busy(slot); hmm...I would suggest you to call dw_mci_wait_busy() from inside mci_send_cmd(), seems like dw_mmc hangs while sending update clock cmd in multiple cases.see [1] [1]: http://permalink.gmane.org/gmane.linux.kernel.mmc/31140 I think this patch is more reasonable. So I will resend patches based on this patch. thank you! if (!clock) { mci_writel(host, CLKENA, 0); -- 1.8.3.2 ___ linux-arm-kernel mailing list linux-arm-ker...@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] mmc: dw_mmc: fix bug that cause 'Timeout sending command'
On 2015/2/9 15:04, Jaehoon Chung wrote: On 02/09/2015 03:56 PM, Addy wrote: On 2015.02.09 12:51, Ulf Hansson wrote: On 5 February 2015 at 12:13, Addy Ke addy...@rock-chips.com wrote: Because of some uncertain factors, such as worse card or worse hardware, DAT[3:0](the data lines) may be pulled down by card, and mmc controller will be in busy state. This should not happend when mmc controller send command to update card clocks. If this happends, mci_send_cmd will be failed and we will get 'Timeout sending command', and then system will be blocked. To avoid this, we need reset mmc controller. I know that it needs to check whether card is busy or not, before clock-off. This patch seems to related with it. right? Yes, it is. Best Regards, Jaehoon Chung Signed-off-by: Addy Ke addy...@rock-chips.com Hi Addy, Should I consider $subject patch as a better option to the one below? No: This patch fix the bug, which can be found by script: cd /sys/bus/platform/drivers/dwmmc_rockchip for i in $(seq 1 1); do echo $i echo ff0c.dwmmc unbind sleep .5 echo ff0c.dwmmc bind sleep 2 done [PATCH] mmc: dw_mmc: rockchip: Add DW_MCI_QUIRK_RETRY_DELAY This patch is for tuning issue: we should delay until card go to idle state, when the previous command return error. https://lkml.org/lkml/2015/1/13/562 Kind regards Uffe --- drivers/mmc/host/dw_mmc.c | 23 +++ 1 file changed, 23 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4d2e3c2..b1d6dfb 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -100,6 +100,7 @@ struct idmac_desc { }; #endif /* CONFIG_MMC_DW_IDMAC */ +static int dw_mci_card_busy(struct mmc_host *mmc); static bool dw_mci_reset(struct dw_mci *host); static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); @@ -888,6 +889,26 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) cmd, arg, cmd_status); } +static void dw_mci_wait_busy(struct dw_mci_slot *slot) +{ + struct dw_mci *host = slot-host; + unsigned long timeout = jiffies + msecs_to_jiffies(500); + + while (time_before(jiffies, timeout)) { + if (!dw_mci_card_busy(slot-mmc)) + return; + } + dev_err(host-dev, Data busy (status %#x)\n, + mci_readl(slot-host, STATUS)); + + /* +* Data busy, this should not happend when mmc controller send command +* to update card clocks in non-volt-switch state. If it happends, we +* should reset controller to avoid getting Timeout sending command. +*/ + dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS); +} + static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) { struct dw_mci *host = slot-host; @@ -899,6 +920,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* We must continue to set bit 28 in CMD until the change is complete */ if (host-state == STATE_WAITING_CMD11_DONE) sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; + else + dw_mci_wait_busy(slot); if (!clock) { mci_writel(host, CLKENA, 0); -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe linux-mmc in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] mmc: dw_mmc: fix bug that cause 'Timeout sending command'
On 2015.02.09 12:51, Ulf Hansson wrote: On 5 February 2015 at 12:13, Addy Ke addy...@rock-chips.com wrote: Because of some uncertain factors, such as worse card or worse hardware, DAT[3:0](the data lines) may be pulled down by card, and mmc controller will be in busy state. This should not happend when mmc controller send command to update card clocks. If this happends, mci_send_cmd will be failed and we will get 'Timeout sending command', and then system will be blocked. To avoid this, we need reset mmc controller. Signed-off-by: Addy Ke addy...@rock-chips.com Hi Addy, Should I consider $subject patch as a better option to the one below? No: This patch fix the bug, which can be found by script: cd /sys/bus/platform/drivers/dwmmc_rockchip for i in $(seq 1 1); do echo $i echo ff0c.dwmmc unbind sleep .5 echo ff0c.dwmmc bind sleep 2 done [PATCH] mmc: dw_mmc: rockchip: Add DW_MCI_QUIRK_RETRY_DELAY This patch is for tuning issue: we should delay until card go to idle state, when the previous command return error. https://lkml.org/lkml/2015/1/13/562 Kind regards Uffe --- drivers/mmc/host/dw_mmc.c | 23 +++ 1 file changed, 23 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4d2e3c2..b1d6dfb 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -100,6 +100,7 @@ struct idmac_desc { }; #endif /* CONFIG_MMC_DW_IDMAC */ +static int dw_mci_card_busy(struct mmc_host *mmc); static bool dw_mci_reset(struct dw_mci *host); static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); @@ -888,6 +889,26 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) cmd, arg, cmd_status); } +static void dw_mci_wait_busy(struct dw_mci_slot *slot) +{ + struct dw_mci *host = slot-host; + unsigned long timeout = jiffies + msecs_to_jiffies(500); + + while (time_before(jiffies, timeout)) { + if (!dw_mci_card_busy(slot-mmc)) + return; + } + dev_err(host-dev, Data busy (status %#x)\n, + mci_readl(slot-host, STATUS)); + + /* +* Data busy, this should not happend when mmc controller send command +* to update card clocks in non-volt-switch state. If it happends, we +* should reset controller to avoid getting Timeout sending command. +*/ + dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS); +} + static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) { struct dw_mci *host = slot-host; @@ -899,6 +920,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* We must continue to set bit 28 in CMD until the change is complete */ if (host-state == STATE_WAITING_CMD11_DONE) sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; + else + dw_mci_wait_busy(slot); if (!clock) { mci_writel(host, CLKENA, 0); -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe linux-mmc in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 0/2] about data busy
Addy Ke (2): mmc: dw_mmc: fix bug that cause 'Timeout sending command' mmc: dw_mmc: Don't start command while data busy drivers/mmc/host/dw_mmc.c | 35 +++ 1 file changed, 35 insertions(+) -- Changes in v2: - add new patch to handle data busy when start command - add cpu_relaxed, suggested by Daniel Kurtz djku...@chromium.org 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 2/2] mmc: dw_mmc: Don't start command while data busy
We should wait for data busy here in non-volt-switch state. This may happend when sdio sends CMD53. Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 7 +++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b0b57e3..b40080d 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1007,6 +1007,13 @@ static void __dw_mci_start_request(struct dw_mci *host, mci_writel(host, BLKSIZ, data-blksz); } + /* +* We should wait for data busy here in non-volt-switch state. +* This may happend when sdio sends CMD53. +*/ + if (host-state != STATE_WAITING_CMD11_DONE) + dw_mci_wait_busy(slot); + cmdflags = dw_mci_prepare_command(slot-mmc, cmd); /* this is the first command, send the initialization clock */ -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 1/2] mmc: dw_mmc: fix bug that cause 'Timeout sending command'
Because of some uncertain factors, such as worse card or worse hardware, DAT[3:0](the data lines) may be pulled down by card, and mmc controller will be in busy state. This should not happend when mmc controller send command to update card clocks. If this happends, mci_send_cmd will be failed and we will get 'Timeout sending command', and then system will be blocked. To avoid this, we need reset mmc controller. Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 28 1 file changed, 28 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4d2e3c2..b0b57e3 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -100,6 +100,7 @@ struct idmac_desc { }; #endif /* CONFIG_MMC_DW_IDMAC */ +static int dw_mci_card_busy(struct mmc_host *mmc); static bool dw_mci_reset(struct dw_mci *host); static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); @@ -888,6 +889,31 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) cmd, arg, cmd_status); } +static void dw_mci_wait_busy(struct dw_mci_slot *slot) +{ + struct dw_mci *host = slot-host; + unsigned long timeout = jiffies + msecs_to_jiffies(500); + + do { + if (!dw_mci_card_busy(slot-mmc)) + return; + cpu_relax(); + } while (time_before(jiffies, timeout)); + + dev_err(host-dev, Data busy (status %#x)\n, + mci_readl(slot-host, STATUS)); + + /* +* Data busy, this should not happend when mmc controller send command +* to update card clocks in non-volt-switch state. If it happends, we +* should reset controller to avoid getting Timeout sending command. +*/ + dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS); + + /* Fail to reset controller or still data busy, WARN_ON! */ + WARN_ON(dw_mci_card_busy(slot-mmc)); +} + static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) { struct dw_mci *host = slot-host; @@ -899,6 +925,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* We must continue to set bit 28 in CMD until the change is complete */ if (host-state == STATE_WAITING_CMD11_DONE) sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; + else + dw_mci_wait_busy(slot); if (!clock) { mci_writel(host, CLKENA, 0); -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] mmc: dw_mmc: fix bug that cause 'Timeout sending command'
Because of some uncertain factors, such as worse card or worse hardware, DAT[3:0](the data lines) may be pulled down by card, and mmc controller will be in busy state. This should not happend when mmc controller send command to update card clocks. If this happends, mci_send_cmd will be failed and we will get 'Timeout sending command', and then system will be blocked. To avoid this, we need reset mmc controller. Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 23 +++ 1 file changed, 23 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4d2e3c2..b1d6dfb 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -100,6 +100,7 @@ struct idmac_desc { }; #endif /* CONFIG_MMC_DW_IDMAC */ +static int dw_mci_card_busy(struct mmc_host *mmc); static bool dw_mci_reset(struct dw_mci *host); static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); @@ -888,6 +889,26 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) cmd, arg, cmd_status); } +static void dw_mci_wait_busy(struct dw_mci_slot *slot) +{ + struct dw_mci *host = slot-host; + unsigned long timeout = jiffies + msecs_to_jiffies(500); + + while (time_before(jiffies, timeout)) { + if (!dw_mci_card_busy(slot-mmc)) + return; + } + dev_err(host-dev, Data busy (status %#x)\n, + mci_readl(slot-host, STATUS)); + + /* +* Data busy, this should not happend when mmc controller send command +* to update card clocks in non-volt-switch state. If it happends, we +* should reset controller to avoid getting Timeout sending command. +*/ + dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS); +} + static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) { struct dw_mci *host = slot-host; @@ -899,6 +920,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* We must continue to set bit 28 in CMD until the change is complete */ if (host-state == STATE_WAITING_CMD11_DONE) sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; + else + dw_mci_wait_busy(slot); if (!clock) { mci_writel(host, CLKENA, 0); -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2] mmc: core: add runtime-resume caps to support resume at runtime_resume
Signed-off-by: Addy Ke addy...@rock-chips.com --- Changes in v2: - fix some typo Documentation/devicetree/bindings/mmc/mmc.txt | 1 + drivers/mmc/core/host.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index 438899e..a7b800e 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -40,6 +40,7 @@ Optional properties: - mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported - mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported - mmc-hs400-1_2v: eMMC HS400 mode(1.2V I/O) is supported +- runtime-resume: resume at runtime_resume is supported - dsr: Value the card's (optional) Driver Stage Register (DSR) should be programmed with. Valid range: [0 .. 0x]. diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 8be0df7..cb44c85 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -422,6 +422,8 @@ int mmc_of_parse(struct mmc_host *host) host-caps |= MMC_CAP_POWER_OFF_CARD; if (of_find_property(np, cap-sdio-irq, len)) host-caps |= MMC_CAP_SDIO_IRQ; + if (of_find_property(np, runtime-resume, len)) + host-caps |= MMC_CAP_RUNTIME_RESUME; if (of_find_property(np, full-pwr-cycle, len)) host-caps2 |= MMC_CAP2_FULL_PWR_CYCLE; if (of_find_property(np, keep-power-in-suspend, len)) -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] mmc: core: add runtime-resume caps to support resume at runtime_resume
Signed-off-by: Addy Ke addy...@rock-chips.com --- Documentation/devicetree/bindings/mmc/mmc.txt | 11 +++ drivers/mmc/core/host.c | 2 ++ 2 files changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index 438899e..303b448 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -40,9 +40,20 @@ Optional properties: - mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported - mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported - mmc-hs400-1_2v: eMMC HS400 mode(1.2V I/O) is supported +- runtime-resume: resume at runtime_resume is supported - dsr: Value the card's (optional) Driver Stage Register (DSR) should be programmed with. Valid range: [0 .. 0x]. +Card power and reset control: +The following properties can be specified for cases where the MMC +peripheral needs additional reset, regulator and clock lines. It is for +example common for WiFi/BT adapters to have these separate from the main +MMC bus: + - card-reset-gpios: Specify GPIOs for card reset (reset active low) + - card-external-vcc-supply: Regulator to drive (independent) card VCC + - clock with name card_ext_clock: External clock provided to the card + 0064e63... FROMLIST: mmc: core: add runtime-resume caps to support resume at runtime_resume + *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line polarity properties, we have to fix the meaning of the normal and inverted line levels. We choose to follow the SDHCI standard, which specifies both those diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 8be0df7..cb44c85 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -422,6 +422,8 @@ int mmc_of_parse(struct mmc_host *host) host-caps |= MMC_CAP_POWER_OFF_CARD; if (of_find_property(np, cap-sdio-irq, len)) host-caps |= MMC_CAP_SDIO_IRQ; + if (of_find_property(np, runtime-resume, len)) + host-caps |= MMC_CAP_RUNTIME_RESUME; if (of_find_property(np, full-pwr-cycle, len)) host-caps2 |= MMC_CAP2_FULL_PWR_CYCLE; if (of_find_property(np, keep-power-in-suspend, len)) -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 1/2] mmc: core: use card pointer as the first parameter of execute_tuning()
We need to take the card pointer in execute_tuning() for mmc_send_status(), but mmc-card is NULL in tuning state. So we need change the first parameter of execute_tuning() to card pointer(struct mmc_card * card). Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/core/core.c | 2 +- drivers/mmc/host/dw_mmc.c | 3 ++- drivers/mmc/host/rtsx_pci_sdmmc.c | 3 ++- drivers/mmc/host/rtsx_usb_sdmmc.c | 3 ++- include/linux/mmc/host.h | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 1be7055..271f024 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1101,7 +1101,7 @@ int mmc_execute_tuning(struct mmc_card *card) opcode = MMC_SEND_TUNING_BLOCK; mmc_host_clk_hold(host); - err = host-ops-execute_tuning(host, opcode); + err = host-ops-execute_tuning(card, opcode); mmc_host_clk_release(host); if (err) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4bd22af..e54e656 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1485,8 +1485,9 @@ free_blk_test: return ret; } -static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) +static int dw_mci_execute_tuning(struct mmc_card *card, u32 opcode) { + struct mmc_host *mmc = card-host; struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot-host; const struct dw_mci_drv_data *drv_data = host-drv_data; diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 1d3d6c4..230bd2f 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1270,8 +1270,9 @@ out: return err; } -static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) +static int sdmmc_execute_tuning(struct mmc_card *card, u32 opcode) { + struct mmc_host *mmc = card-host; struct realtek_pci_sdmmc *host = mmc_priv(mmc); struct rtsx_pcr *pcr = host-pcr; int err = 0; diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index 88af827..c494c06 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -1265,8 +1265,9 @@ out: return 0; } -static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) +static int sdmmc_execute_tuning(struct mmc_card *card, u32 opcode) { + struct mmc_host *mmc = card-host; struct rtsx_usb_sdmmc *host = mmc_priv(mmc); struct rtsx_ucr *ucr = host-ucr; int err = 0; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 0c8cbe5..ec4128e 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -133,7 +133,7 @@ struct mmc_host_ops { int (*card_busy)(struct mmc_host *host); /* The tuning command opcode value is different for SD and eMMC cards */ - int (*execute_tuning)(struct mmc_host *host, u32 opcode); + int (*execute_tuning)(struct mmc_card *card, u32 opcode); /* Prepare HS400 target operating frequency depending host driver */ int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios); -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 0/2] fix bug that cause tuning failure
Addy Ke (2): mmc: core: use card pointer as the first parameter of execute_tuning() mmc: dw_mmc: wait until card ready if tuning fails drivers/mmc/core/core.c | 2 +- drivers/mmc/core/mmc_ops.h| 1 - drivers/mmc/host/dw_mmc.c | 51 +-- drivers/mmc/host/rtsx_pci_sdmmc.c | 3 ++- drivers/mmc/host/rtsx_usb_sdmmc.c | 3 ++- include/linux/mmc/card.h | 2 ++ include/linux/mmc/host.h | 2 +- 7 files changed, 52 insertions(+), 12 deletions(-) -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 2/2] mmc: dw_mmc: wait until card ready if tuning fails
This patch based on Alex's patch: https://patchwork.kernel.org/patch/5516411/ Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/core/mmc_ops.h | 1 - drivers/mmc/host/dw_mmc.c | 48 -- include/linux/mmc/card.h | 2 ++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 6f4b00e..c5be9ce 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -20,7 +20,6 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); int mmc_all_send_cid(struct mmc_host *host, u32 *cid); int mmc_set_relative_addr(struct mmc_card *card); int mmc_send_csd(struct mmc_card *card, u32 *csd); -int mmc_send_status(struct mmc_card *card, u32 *status); int mmc_send_cid(struct mmc_host *host, u32 *cid); int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); int mmc_spi_set_crc(struct mmc_host *host, int use_crc); diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index e54e656..4a31a5e 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1317,10 +1317,38 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) spin_unlock_irqrestore(host-irq_lock, irqflags); } -static int dw_mci_tuning_test(struct dw_mci_slot *slot, u32 opcode, - struct dw_mci_tuning_data *tuning_data, - u8 *blk_test) +static int dw_mci_card_busy(u32 status) { + return !(status R1_READY_FOR_DATA) || + (R1_CURRENT_STATE(status) == R1_STATE_PRG); +} + +static int dw_mci_wait_for_card_ready(struct mmc_card *card) +{ + struct mmc_host *mmc = card-host; + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot-host; + int err = 0; + u32 status; + + do { + err = mmc_send_status(card, status); + if (err) { + dev_err(host-dev, + Get card status fail in tuning state\n); + break; + } + } while (dw_mci_card_busy(status)); + + return err; +} + +static int dw_mci_tuning_test(struct mmc_card *card, u32 opcode, + struct dw_mci_tuning_data *tuning_data, + u8 *blk_test) +{ + struct mmc_host *mmc = card-host; + struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot-host; struct mmc_host *mmc = slot-mmc; const u8 *blk_pattern = tuning_data-blk_pattern; @@ -1330,6 +1358,7 @@ static int dw_mci_tuning_test(struct dw_mci_slot *slot, u32 opcode, struct mmc_command stop = {0}; struct mmc_data data = {0}; struct scatterlist sg; + int err; memset(blk_test, 0, blksz); @@ -1365,6 +1394,11 @@ static int dw_mci_tuning_test(struct dw_mci_slot *slot, u32 opcode, dev_dbg(host-dev, Tuning error: cmd.error:%d, data.error:%d\n, cmd.error, data.error); + + err = dw_mci_wait_for_card_ready(card); + if (err) + return err; + if (cmd.error) return cmd.error; else @@ -1372,9 +1406,11 @@ static int dw_mci_tuning_test(struct dw_mci_slot *slot, u32 opcode, } } -static int dw_mci_execute_generic_tuning(struct dw_mci_slot *slot, u32 opcode, +static int dw_mci_execute_generic_tuning(struct mmc_card *card, u32 opcode, struct dw_mci_tuning_data *tuning_data) { + struct mmc_host *mmc = card-host; + struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot-host; unsigned int blksz = tuning_data-blksz; u8 *blk_test; @@ -1412,7 +1448,7 @@ static int dw_mci_execute_generic_tuning(struct dw_mci_slot *slot, u32 opcode, for (i = 0; i NUM_PHASES; i++) { clk_set_phase(host-sample_clk, i * PHASE_INCREMENT); - v = !dw_mci_tuning_test(slot, opcode, tuning_data, blk_test); + v = !dw_mci_tuning_test(card, opcode, tuning_data, blk_test); if ((!prev_v) v) { range_count++; @@ -1496,7 +1532,7 @@ static int dw_mci_execute_tuning(struct mmc_card *card, u32 opcode) if (drv_data drv_data-execute_tuning) err = drv_data-execute_tuning(slot, opcode, tuning_data); else - err = dw_mci_execute_generic_tuning(slot, opcode, tuning_data); + err = dw_mci_execute_generic_tuning(card, opcode, tuning_data); return err; } diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 4d69c00..40d90ae 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -518,4 +518,6 @@ extern void mmc_unregister_driver(struct device_driver *); extern void
[PATCH] mmc: dw_mmc: fix bug that cause mmc_test failture
The STOP command can terminate a data transfer between a memory card and mmc controller. As show in Synopsys DesignWare Cores Mobile Stroage Host Databook: Data timeout and Data end-bit error will terminate further data transfer by mmc controller. So we should not send abort command to terminate a data transfer again if we got DRTO and EBE interrupt. After this patch, all mmc_test cases can pass on RK3288-Pink2 board. Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 10 -- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4d2e3c2..4bd7df1 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1520,7 +1520,10 @@ static void dw_mci_tasklet_func(unsigned long priv) if (test_and_clear_bit(EVENT_DATA_ERROR, host-pending_events)) { dw_mci_stop_dma(host); - send_stop_abort(host, data); + if (data-stop || + !(host-data_status SDMMC_INT_DRTO) || + !(host-data_status SDMMC_INT_EBE)) + send_stop_abort(host, data); state = STATE_DATA_ERROR; break; } @@ -1547,7 +1550,10 @@ static void dw_mci_tasklet_func(unsigned long priv) if (test_and_clear_bit(EVENT_DATA_ERROR, host-pending_events)) { dw_mci_stop_dma(host); - send_stop_abort(host, data); + if (data-stop || + !(host-data_status SDMMC_INT_DRTO) || + !(host-data_status SDMMC_INT_EBE)) + send_stop_abort(host, data); state = STATE_DATA_ERROR; break; } -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v5] mmc: dw_mmc: add quirk for broken data transfer over scheme
This patch add a new quirk to add a s/w timer to notify the driver to terminate current transfer and report a data timeout to the core, if DTO interrupt does NOT come within the given time. dw_mmc call mmc_request_done func to finish transfer depends on DTO interrupt. If DTO interrupt does not come in sending data state, the current transfer will be blocked. We got the reply from synopsys: There are two counters but both use the same value of [31:8] bits. Data timeout counter doesn't wait for stop clock and you should get DRTO even when the clock is not stopped. Host Starvation timeout counter is triggered with stop clock condition. This means that host should get DRTO and DTO interrupt. But this case really exists, when driver reads tuning data from card on RK3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state. There are two possibility that data over interrupt doesn't come in reading data state on RK3X SoCs: - get command done interrupt, but doesn't get any data-related interrupt. - get data error interrupt, but doesn't get data over interrupt. We don't know why we have this problem, but we need it to fix this problem now. And I will post a follow up change when we find the root cause. Signed-off-by: Addy Ke addy...@rock-chips.com --- Changes in v2: - fix some typo. - remove extra timeout value (250ms). - remove dw_mci_dto_start_monitor func. - use broken-dto for new quirk and change Subject for it. Changes in v3: - Remove dts for broken-dto, just add this quirk in dw_mci_rockchip_init Changes in v4: - fix bug that may cause 32 bit overflow by (drto_clks * 1000). - doesn't call mod_timer in writing data state, becase TMOUT register only for reading data. Changes in v5: - fix some typo. - add a buffer for drto_ms. - move drto_ms related code to a helper function. drivers/mmc/host/dw_mmc-rockchip.c | 3 ++ drivers/mmc/host/dw_mmc.c | 71 -- include/linux/mmc/dw_mmc.h | 5 +++ 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 5650ac4..ba92ebd 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -73,6 +73,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host) /* It is slot 8 on Rockchip SoCs */ host-sdio_id0 = 8; + /* It needs this quirk on all Rockchip SoCs */ + host-pdata-quirks |= DW_MCI_QUIRK_BROKEN_DTO; + return 0; } diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 6e4d864..ace4b40 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1468,6 +1468,20 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) return data-error; } +static unsigned int dw_mci_get_drto_ms(struct dw_mci *host) +{ + unsigned int drto_clks; + unsigned int drto_ms; + + drto_clks = mci_readl(host, TMOUT) 8; + drto_ms = DIV_ROUND_UP(drto_clks, host-bus_hz / 1000); + + /* add a buffer */ + drto_ms += 10; + + return drto_ms; +} + static void dw_mci_tasklet_func(unsigned long priv) { struct dw_mci *host = (struct dw_mci *)priv; @@ -1477,6 +1491,7 @@ static void dw_mci_tasklet_func(unsigned long priv) enum dw_mci_state state; enum dw_mci_state prev_state; unsigned int err; + unsigned int drto_ms; spin_lock(host-lock); @@ -1542,8 +1557,19 @@ static void dw_mci_tasklet_func(unsigned long priv) } if (!test_and_clear_bit(EVENT_XFER_COMPLETE, - host-pending_events)) + host-pending_events)) { + /* +* If all data-related interrupts don't come +* within the given time in reading data state. +*/ + if ((host-quirks DW_MCI_QUIRK_BROKEN_DTO) + (host-dir_status == DW_MCI_RECV_STATUS)) { + drto_ms = dw_mci_get_drto_ms(host); + mod_timer(host-dto_timer, jiffies + + msecs_to_jiffies(drto_ms)); + } break; + } set_bit(EVENT_XFER_COMPLETE, host-completed_events); @@ -1573,8 +1599,20 @@ static void dw_mci_tasklet_func(unsigned long priv) case STATE_DATA_BUSY: if (!test_and_clear_bit(EVENT_DATA_COMPLETE, - host-pending_events)) + host
[PATCH v4] mmc: dw_mmc: add quirk for broken data transfer over scheme
This patch add a new quirk to add a s/w timer to notify the driver to terminate current transfer and report a data timeout to the core, if DTO interrupt does NOT come within the given time. dw_mmc call mmc_request_done func to finish transfer depends on DTO interrupt. If DTO interrupt does not come in sending data state, the current transfer will be blocked. We got the reply from synopsys: There are two counters but both use the same value of [31:8] bits. Data timeout counter doesn't wait for stop clock and you should get DRTO even when the clock is not stopped. Host Starvation timeout counter is triggered with stop clock condition. This means that host should get DRTO and DTO interrupt. But this case really exists, when driver reads tuning data from card on RK3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state. There are two possibility that data over interrupt doesn't come in reading data state on RK3X SoCs: - get command done interrupt, but doesn't get any data-related interrupt. - get data error interrupt, but doesn't get data over interrupt. We don't know why we have this problem, but we need it to fix this problem now. And I will post a follow up change when we find the root cause. Signed-off-by: Addy Ke addy...@rock-chips.com --- Changes in v2: - fix some typo. - remove extra timeout value (250ms). - remove dw_mci_dto_start_monitor func. - use broken-dto for new quirk and change Subject for it. Changes in v3: - Remove dts for broken-dto, just add this quirk in dw_mci_rockchip_init Changes in v4: - fix bug that may cause 32 bit overflow by (drto_clks * 1000). - doesn't call mod_timer in writing data state, becase TMOUT register only for reading data. drivers/mmc/host/dw_mmc-rockchip.c | 3 ++ drivers/mmc/host/dw_mmc.c | 64 -- include/linux/mmc/dw_mmc.h | 5 +++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 5650ac4..ba92ebd 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -73,6 +73,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host) /* It is slot 8 on Rockchip SoCs */ host-sdio_id0 = 8; + /* It needs this quirk on all Rockchip SoCs */ + host-pdata-quirks |= DW_MCI_QUIRK_BROKEN_DTO; + return 0; } diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 6e4d864..385dc0f 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1477,6 +1477,8 @@ static void dw_mci_tasklet_func(unsigned long priv) enum dw_mci_state state; enum dw_mci_state prev_state; unsigned int err; + unsigned int drto_clks; + unsigned int drto_ms; spin_lock(host-lock); @@ -1542,8 +1544,22 @@ static void dw_mci_tasklet_func(unsigned long priv) } if (!test_and_clear_bit(EVENT_XFER_COMPLETE, - host-pending_events)) + host-pending_events)) { + drto_clks = mci_readl(host, TMOUT) 8; + drto_ms = DIV_ROUND_UP(drto_clks, + host-bus_hz / 1000); + + /* +* If all data-related interrupts don't come +* within the given time in reading data state. +* */ + if ((host-quirks DW_MCI_QUIRK_BROKEN_DTO) + (host-dir_status == DW_MCI_RECV_STATUS)) { + mod_timer(host-dto_timer, jiffies + + msecs_to_jiffies(drto_ms)); + } break; + } set_bit(EVENT_XFER_COMPLETE, host-completed_events); @@ -1573,8 +1589,22 @@ static void dw_mci_tasklet_func(unsigned long priv) case STATE_DATA_BUSY: if (!test_and_clear_bit(EVENT_DATA_COMPLETE, - host-pending_events)) + host-pending_events)) { + drto_clks = mci_readl(host, TMOUT) 8; + drto_ms = DIV_ROUND_UP(drto_clks, + host-bus_hz / 1000); + /* +* If data error interrupt comes but data over +* interrupt doesn't come within the given time. +* in reading data state
[PATCH] ARM: dts: rockchip: set dw_mmc max-freq 150Mhz
All of mmc controllers include SDMMC, SDIO0, SDIO1, and EMMC on RK3288 are limited to 150Mhz. It was mainly caused by two reasons: - RK3288's IO pad(except DDR IO pad) is generic, which can only support the max of 150Mhz. - Mmc controller was designed at 150Mhz, and the pressure test by IC team was based on this freequency point. Signed-off-by: Addy Ke addy...@rock-chips.com --- arch/arm/boot/dts/rk3288.dtsi | 4 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi index acb6a2f..9c35a1d 100644 --- a/arch/arm/boot/dts/rk3288.dtsi +++ b/arch/arm/boot/dts/rk3288.dtsi @@ -149,6 +149,7 @@ sdmmc: dwmmc@ff0c { compatible = rockchip,rk3288-dw-mshc; + clock-freq-min-max = 40 15000; clocks = cru HCLK_SDMMC, cru SCLK_SDMMC; clock-names = biu, ciu; fifo-depth = 0x100; @@ -159,6 +160,7 @@ sdio0: dwmmc@ff0d { compatible = rockchip,rk3288-dw-mshc; + clock-freq-min-max = 40 15000; clocks = cru HCLK_SDIO0, cru SCLK_SDIO0; clock-names = biu, ciu; fifo-depth = 0x100; @@ -169,6 +171,7 @@ sdio1: dwmmc@ff0e { compatible = rockchip,rk3288-dw-mshc; + clock-freq-min-max = 40 15000; clocks = cru HCLK_SDIO1, cru SCLK_SDIO1; clock-names = biu, ciu; fifo-depth = 0x100; @@ -179,6 +182,7 @@ emmc: dwmmc@ff0f { compatible = rockchip,rk3288-dw-mshc; + clock-freq-min-max = 40 15000; clocks = cru HCLK_EMMC, cru SCLK_EMMC; clock-names = biu, ciu; fifo-depth = 0x100; -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v3] mmc: dw_mmc: add quirk for broken data transfer over scheme
This patch add a new quirk to add a s/w timer to notify the driver to terminate current transfer and report a data timeout to the core, if DTO interrupt does NOT come within the given time. dw_mmc call mmc_request_done func to finish transfer depends on DTO interrupt. If DTO interrupt does not come in sending data state, the current transfer will be blocked. But this case really exists, when driver reads tuning data from card on RK3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state. We got the reply from synopsys: There are two counters but both use the same value of [31:8] bits. Data timeout counter doesn't wait for stop clock and you should get DRTO even when the clock is not stopped. Host Starvation timeout counter is triggered with stop clock condition. This means that host should get DRTO and DTO interrupt. But we really don't get any data-related interrupt in RK3X SoCs. And driver can't get data transfer state, it can do nothing but wait for. We don't know why we have this problem, but we need it to fix this problem now. And I will post a follow up change when we find the root cause. Signed-off-by: Addy Ke addy...@rock-chips.com --- Changes in v2: - fix some typo. - remove extra timeout value (250ms). - remove dw_mci_dto_start_monitor func. - use broken-dto for new quirk and change Subject for it. Changes in v3: - Remove dts for broken-dto, just add this quirk in dw_mci_rockchip_init drivers/mmc/host/dw_mmc-rockchip.c | 3 +++ drivers/mmc/host/dw_mmc.c | 41 +- include/linux/mmc/dw_mmc.h | 5 + 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 5650ac4..ba92ebd 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -73,6 +73,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host) /* It is slot 8 on Rockchip SoCs */ host-sdio_id0 = 8; + /* It needs this quirk on all Rockchip SoCs */ + host-pdata-quirks |= DW_MCI_QUIRK_BROKEN_DTO; + return 0; } diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 67c0451..e222122 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1464,6 +1464,8 @@ static void dw_mci_tasklet_func(unsigned long priv) enum dw_mci_state state; enum dw_mci_state prev_state; unsigned int err; + unsigned int drto_clks; + unsigned int drto_ms; spin_lock(host-lock); @@ -1529,8 +1531,17 @@ static void dw_mci_tasklet_func(unsigned long priv) } if (!test_and_clear_bit(EVENT_XFER_COMPLETE, - host-pending_events)) + host-pending_events)) { + if (host-quirks DW_MCI_QUIRK_BROKEN_DTO) { + drto_clks = mci_readl(host, TMOUT) 8; + drto_ms = DIV_ROUND_UP(drto_clks * 1000, + host-bus_hz); + + mod_timer(host-dto_timer, jiffies + + msecs_to_jiffies(drto_ms)); + } break; + } set_bit(EVENT_XFER_COMPLETE, host-completed_events); @@ -2122,6 +2133,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } if (pending SDMMC_INT_DATA_OVER) { + if (host-quirks DW_MCI_QUIRK_BROKEN_DTO) + del_timer(host-dto_timer); + mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host-data_status) host-data_status = pending; @@ -2509,6 +2523,28 @@ ciu_out: return ret; } +static void dw_mci_dto_timer(unsigned long arg) +{ + struct dw_mci *host = (struct dw_mci *)arg; + + switch (host-state) { + case STATE_SENDING_DATA: + case STATE_DATA_BUSY: + /* + * If DTO interrupt does NOT come in sending data state, + * we should notify the driver to terminate current transfer + * and report a data timeout to the core. + */ + host-data_status = SDMMC_INT_DRTO; + set_bit(EVENT_DATA_ERROR, host-pending_events); + set_bit(EVENT_DATA_COMPLETE, host-pending_events); + tasklet_schedule(host-tasklet); + break; + default: + break; + } +} + #ifdef CONFIG_OF static struct dw_mci_of_quirks { char *quirk; @@ -2661,6 +2697,9 @@ int dw_mci_probe
Re: [PATCH v2] mmc: dw_mmc: add quirk for broken data transfer over scheme
Hi, On 2014/11/27 06:46, Doug Anderson wrote: Hi, On Tue, Nov 25, 2014 at 12:10 AM, Addy Ke addy...@rock-chips.com wrote: This patch add a new quirk to add a s/w timer to notify the driver to terminate current transfer and report a data timeout to the core, if DTO interrupt does NOT come within the given time. dw_mmc call mmc_request_done func to finish transfer depends on DTO interrupt. If DTO interrupt does not come in sending data state, the current transfer will be blocked. But this case really exists, when driver reads tuning data from card on RK3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state. We got the reply from synopsys: There are two counters but both use the same value of [31:8] bits. Data timeout counter doesn't wait for stop clock and you should get DRTO even when the clock is not stopped. Host Starvation timeout counter is triggered with stop clock condition. This means that host should get DRTO and DTO interrupt. But we really don't get any data-related interrupt in RK3X SoCs. And driver can't get data transfer state, it can do nothing but wait for. Have you asked someone on your IC team to confirm this is an SoC errata on your SoC? ...or is there something else we could be doing wrong (overclocking? jitter in the clock? bad dividers?) that could be causing this problem? #ifdef CONFIG_OF static struct dw_mci_of_quirks { char *quirk; @@ -2513,6 +2549,9 @@ static struct dw_mci_of_quirks { }, { .quirk = disable-wp, .id = DW_MCI_QUIRK_NO_WRITE_PROTECT, + }, { + .quirk = broken-dto, + .id = DW_MCI_QUIRK_BROKEN_DTO, You're adding a device tree property without any binding. If you need to add this please send a patch before this one modifying the device tree bindings. ...but that brings up the question: do you _really_ need to add a property? You already know that all rk3288 SoCs need this and you already know that you're an rk3288 SoC. Just add this quirk in the rk3288 code always and be done with it. ...and if this is also needed on other Rockchip parts, add it there too. -Doug We don't know why we have this problem, but this problem is really exist, and we need patch to fix this problem now. I will post a follow up change when we find the root cause. And there is a little probability of this problem on RK SoC, such as RK3188, RK3066, when worse card inserted in. Maybe the other SoCs have the similar problem. So I will add this quirk in rockchip code(dw_mmc-rockchip.c) as follows: static int dw_mci_rockchip_parse_dt(struct dw_mci *host) { host-quirk |= DW_MCI_QUIRK_BROKEN_DTO; return 0; } .. .parse_dt = dw_mci_rockchip_parse_dt, .. is right? -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2] mmc: dw_mmc: add quirk for broken data transfer over scheme
This patch add a new quirk to add a s/w timer to notify the driver to terminate current transfer and report a data timeout to the core, if DTO interrupt does NOT come within the given time. dw_mmc call mmc_request_done func to finish transfer depends on DTO interrupt. If DTO interrupt does not come in sending data state, the current transfer will be blocked. But this case really exists, when driver reads tuning data from card on RK3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state. We got the reply from synopsys: There are two counters but both use the same value of [31:8] bits. Data timeout counter doesn't wait for stop clock and you should get DRTO even when the clock is not stopped. Host Starvation timeout counter is triggered with stop clock condition. This means that host should get DRTO and DTO interrupt. But we really don't get any data-related interrupt in RK3X SoCs. And driver can't get data transfer state, it can do nothing but wait for. Signed-off-by: Addy Ke addy...@rock-chips.com --- - fix some typo. - remove extra timeout value (250ms). - remove dw_mci_dto_start_monitor func. - use broken-dto for new quirk and change Subject for it. drivers/mmc/host/dw_mmc.c | 44 +++- include/linux/mmc/dw_mmc.h | 5 + 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b4c3044..bc09f50 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1457,6 +1457,8 @@ static void dw_mci_tasklet_func(unsigned long priv) enum dw_mci_state state; enum dw_mci_state prev_state; unsigned int err; + unsigned int drto_clks; + unsigned int drto_ms; spin_lock(host-lock); @@ -1522,8 +1524,17 @@ static void dw_mci_tasklet_func(unsigned long priv) } if (!test_and_clear_bit(EVENT_XFER_COMPLETE, - host-pending_events)) + host-pending_events)) { + if (host-quirks DW_MCI_QUIRK_BROKEN_DTO) { + drto_clks = mci_readl(host, TMOUT) 8; + drto_ms = DIV_ROUND_UP(drto_clks * 1000, + host-bus_hz); + + mod_timer(host-dto_timer, jiffies + + msecs_to_jiffies(drto_ms)); + } break; + } set_bit(EVENT_XFER_COMPLETE, host-completed_events); @@ -2115,6 +2126,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } if (pending SDMMC_INT_DATA_OVER) { + if (host-quirks DW_MCI_QUIRK_BROKEN_DTO) + del_timer(host-dto_timer); + mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host-data_status) host-data_status = pending; @@ -2502,6 +2516,28 @@ ciu_out: return ret; } +static void dw_mci_dto_timer(unsigned long arg) +{ + struct dw_mci *host = (struct dw_mci *)arg; + + switch (host-state) { + case STATE_SENDING_DATA: + case STATE_DATA_BUSY: + /* + * If DTO interrupt does NOT come in sending data state, + * we should notify the driver to terminate current transfer + * and report a data timeout to the core. + */ + host-data_status = SDMMC_INT_DRTO; + set_bit(EVENT_DATA_ERROR, host-pending_events); + set_bit(EVENT_DATA_COMPLETE, host-pending_events); + tasklet_schedule(host-tasklet); + break; + default: + break; + } +} + #ifdef CONFIG_OF static struct dw_mci_of_quirks { char *quirk; @@ -2513,6 +2549,9 @@ static struct dw_mci_of_quirks { }, { .quirk = disable-wp, .id = DW_MCI_QUIRK_NO_WRITE_PROTECT, + }, { + .quirk = broken-dto, + .id = DW_MCI_QUIRK_BROKEN_DTO, }, }; @@ -2654,6 +2693,9 @@ int dw_mci_probe(struct dw_mci *host) spin_lock_init(host-lock); INIT_LIST_HEAD(host-queue); + if (host-quirks DW_MCI_QUIRK_BROKEN_DTO) + setup_timer(host-dto_timer, + dw_mci_dto_timer, (unsigned long)host); /* * Get the host data width - this assumes that HCON has been set with diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 42b724e..ff9bd1e 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux
Re: [PATCH] mmc: dw_mmc: try pick the exact same voltage as vmmc for vqmmc
On Fri, Nov 24, 2014 at 9:29 PM, Ulf Hansson ulf.hans...@linaro.org wrote: On 21 November 2014 at 22:04, Doug Anderson diand...@chromium.org wrote: Hi, On Fri, Nov 21, 2014 at 9:42 AM, Doug Anderson diand...@chromium.org wrote: Ulf, On Fri, Nov 21, 2014 at 4:06 AM, Ulf Hansson ulf.hans...@linaro.org wrote: [...] Sure If the first card is sd2.0 since startup, dw_mci_switch_voltage will not be called, That can't be right. mmc_power_up() should trigger dw_mci_switch_voltage() to be invoked. Hmmm, I think you're right. Addy: can you double check if it's only the 2nd card for you? I was thinking that if a regulator is currently 3.3V and you request 2.7 - 3.3V the regulator framework will treat that as a noop. ...but that definitely doesn't appear to be the case. When I boot up the first time even with no SD card plugged in, I see this at bootup: [3.042234] vccio_sd: 1800 -- 3300 mV at 3300 mV ...showing that it started at 3.3V. Then I see: $ grep /sys/class/regulator/regulator.16/{name,microvolts} /sys/class/regulator/regulator.16/name:vccio_sd /sys/class/regulator/regulator.16/microvolts:270 ...so it is certainly getting changed even with no card plugged in. BTW: I don't actually have one of these failing cards--all of mine work. Addy, do you know the make and model of the card you have that fails? Just as a bit of a followup, I did some more digging... 1. It looks as if we now have a bit of opposite logic for vmmc vs. vqmmc. In mmc_power_up() I see that it sets the initial voltage as: host-ios.vdd = fls(ocr) - 1; That's because we would like to supports as many cards as possible. The policy is based upon that some cards may not support lower voltages, but most will support higher. That actually means that we're going to pick the maximum voltage for vmmc (of the supported voltages). For vqmmc dw_mmc is using the regulator framework which (as described in my previous message) will pick the minimum. Correct. I have thought this has been inside spec and choosing the lower value would be preferred to lower power consumption. Maybe we needs to re-visit this one more time. Here are some of the interesting sections in the eMMC spec: 10.3.3 Power supply Voltages The VCCQ must be defined at equal to or less than VCC. 10.5 Bus signal levels Push-pull mode: Voh = 0.75 * VCCQ. (Do note, its VCCQ not VCC). Summary eMMC: VCCQ must be less and VCC, we should be inside spec. From SD spec: 6.6.1 Threshold Level for High Voltage Range Voh = 0.75 * VDD. In worst case scenario, VDD = 3.6V and VIO = 2.7V. That gives as the factor of 0.75, thus we are inside spec but without margins. * From eMMC4.5 spec: 1. (VDDF)vcc: Supply voltage for flash memory, which is 2.7v -- 3.3v 2. (VDD)vccq: Supply voltage for memory controller, which is 1.7v -- 1.95v and 2,7v -- 3.6v * And from RK3288 datasheet: Digtial GPIO Power(SDMMC0_VDD -- vccq) is 3.0v -- 3.6v and 1.62v - 1.98v So I think: 3.3v: (2.7v vccq 3.6v) (3.0v vccq 3.6v) == (3.0v vccq 3.6v) 1.8v: (1.7v vccq 1.95v) (1.62v vccq 1.98v) == (1.7v vccq 1.95v) and (2.7v vcc 3.3v) * And according to our hardware engineer: All of supply voltage must have +/- 10% cushion. * And we have found in some worse card that there is 200mv voltage collapse when these card is insert. So I think the best resolution is that vcc and vccq is configurable int dt table. 2. Several people I've talked to have expressed concerns that our minimum value is 2.7V. Apparently that's really on the edge and makes EEs a little nervous. The quick sample of cards sitting on my desk shows that they seem to claim 0x00ff8000, which doesn't include 2.7V. 0x00ff8000 states what values of VDD levels the device supports. Not VIO. Both of the above make me feel like dw_mmc should try its best to pick a value for vqmmc that is closest to the value of vmmc (and = 2.7V). That also happens to make us work exactly like hosts where vmmc and vqmmc are supplied by the same supply. I do see your point. And I agree that it would be nice to achieve something like this. The question is how to do this. For sure, we need to involve the mmc core to handle this correctly. Kind regards Uffe -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] mmc: dw_mmc: add quirk for data over interrupt timeout
Hi, Jaehoon On 2014/11/20 18:01, Jaehoon Chung wrote: Hi, Addy. On 11/20/2014 06:33 PM, addy ke wrote: Hi, Jaehoon On 2014/11/19 13:56, addy ke wrote: Hi Jaehoon On 2014/11/19 09:22, Jaehoon Chung Wrote: Hi, Addy. On 11/18/2014 09:32 AM, Addy wrote: On 2014年11月14日 21:18, Jaehoon Chung wrote: Hi, Addy. Did you use the DW_MCI_QUIRK_IDMAC_DTO? I'm not sure, but i wonder if you get what result when you use above quirk. DW_MCI_QUIRK_IDMAC_DTO is only for version2.0 or below. /* * DTO fix - version 2.10a and below, and only if internal DMA * is configured. */ if (host-quirks DW_MCI_QUIRK_IDMAC_DTO) { if (!pending ((mci_readl(host, STATUS) 17) 0x1fff)) pending |= SDMMC_INT_DATA_OVER; } It meams that if interrupt comes, but pending = 0 FIFO_COUNT(bit17-29) !=0, then force to set SDMMC_INT_DATA_OVER. But in our case, FIFO_COUNT = 0 (STATUS register value is 0xad06). This is because that the card does not send data to host. So there is no interrupts come, and interrupt handle function(dw_mci_interrupt) will not be called. So we need a timer to handle this case. So I think SDMMC_INT_DATA_OVER is not suitable for this case, and we need a new quirk. And i will check more this patch at next week. Thanks for your efforts. Best Regards, Jaehoon Chung On 11/14/2014 10:05 PM, Addy Ke wrote: From: Addy addy...@rock-chips.com This patch add a new quirk to notify the driver to teminate current transfer and report a data timeout to the core, if data over interrupt does NOT come within the given time. dw_mmc call mmc_request_done func to finish transfer depends on data over interrupt. If data over interrupt does not come in sending data state, the current transfer will be blocked. But this case really exists, when driver reads tuning data from card on rk3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state. This is the cause that card does NOT send data to host. According to synopsys designware databook, the timeout counter is started only after the card clock is stopped. So if card clock is always on, data read timeout interrupt will NOT come, and if data lines are always holded high level, all data-related interrupt such as start-bit error, data crc error, data over interrupt, end-bit error, and so on, will NOT come too. So driver can't get the current state, it can do nothing but wait for. This patch is based on https://patchwork.kernel.org/patch/5227941/ Signed-off-by: Addy addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 47 +- include/linux/mmc/dw_mmc.h | 5 + 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b4c3044..3960fc3 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1448,6 +1448,17 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) return data-error; } +static inline void dw_mci_dto_start_monitor(struct dw_mci *host) +{ +unsigned int data_tmout_clks; +unsigned int data_tmout_ms; + +data_tmout_clks = (mci_readl(host, TMOUT) 8); +data_tmout_ms = (data_tmout_clks * 1000 / host-bus_hz) + 250; What's 250? And how about using the DIV_ROUND_UP? 250ms is only for more timeout. maybe data timeout read from TMOUT register is enough. So, I will remove 250. new code: data_tmout_clks = (mci_readl(host, TMOUT) 8); data_tmout_ms = DIV_ROUND_UP(data_tmout_clks * 100, host-bus_hz); Is right? + +mod_timer(host-dto_timer, jiffies + msecs_to_jiffies(data_tmout_ms)); +} + static void dw_mci_tasklet_func(unsigned long priv) { struct dw_mci *host = (struct dw_mci *)priv; @@ -1522,8 +1533,11 @@ static void dw_mci_tasklet_func(unsigned long priv) } if (!test_and_clear_bit(EVENT_XFER_COMPLETE, -host-pending_events)) +host-pending_events)) { +if (host-quirks DW_MCI_QUIRK_DTO_TIMER) +dw_mci_dto_start_monitor(host); if timer is starting at only here, dw_mci_dto_start_monitor() doesn't need. Ok, I will change it in the next patch. break; +} set_bit(EVENT_XFER_COMPLETE, host-completed_events); @@ -2115,6 +2129,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } if (pending SDMMC_INT_DATA_OVER) { +if (host-quirks DW_MCI_QUIRK_DTO_TIMER) +del_timer(host-dto_timer); + mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host-data_status) host-data_status = pending; @@ -2502,6 +2519,28 @@ ciu_out: return ret; } +static void dw_mci_dto_timer(unsigned
Re: [PATCH] mmc: dw_mmc: add quirk for data over interrupt timeout
Hi, Jaehoon On 2014/11/19 13:56, addy ke wrote: Hi Jaehoon On 2014/11/19 09:22, Jaehoon Chung Wrote: Hi, Addy. On 11/18/2014 09:32 AM, Addy wrote: On 2014年11月14日 21:18, Jaehoon Chung wrote: Hi, Addy. Did you use the DW_MCI_QUIRK_IDMAC_DTO? I'm not sure, but i wonder if you get what result when you use above quirk. DW_MCI_QUIRK_IDMAC_DTO is only for version2.0 or below. /* * DTO fix - version 2.10a and below, and only if internal DMA * is configured. */ if (host-quirks DW_MCI_QUIRK_IDMAC_DTO) { if (!pending ((mci_readl(host, STATUS) 17) 0x1fff)) pending |= SDMMC_INT_DATA_OVER; } It meams that if interrupt comes, but pending = 0 FIFO_COUNT(bit17-29) !=0, then force to set SDMMC_INT_DATA_OVER. But in our case, FIFO_COUNT = 0 (STATUS register value is 0xad06). This is because that the card does not send data to host. So there is no interrupts come, and interrupt handle function(dw_mci_interrupt) will not be called. So we need a timer to handle this case. So I think SDMMC_INT_DATA_OVER is not suitable for this case, and we need a new quirk. And i will check more this patch at next week. Thanks for your efforts. Best Regards, Jaehoon Chung On 11/14/2014 10:05 PM, Addy Ke wrote: From: Addy addy...@rock-chips.com This patch add a new quirk to notify the driver to teminate current transfer and report a data timeout to the core, if data over interrupt does NOT come within the given time. dw_mmc call mmc_request_done func to finish transfer depends on data over interrupt. If data over interrupt does not come in sending data state, the current transfer will be blocked. But this case really exists, when driver reads tuning data from card on rk3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state. This is the cause that card does NOT send data to host. According to synopsys designware databook, the timeout counter is started only after the card clock is stopped. So if card clock is always on, data read timeout interrupt will NOT come, and if data lines are always holded high level, all data-related interrupt such as start-bit error, data crc error, data over interrupt, end-bit error, and so on, will NOT come too. So driver can't get the current state, it can do nothing but wait for. This patch is based on https://patchwork.kernel.org/patch/5227941/ Signed-off-by: Addy addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 47 +- include/linux/mmc/dw_mmc.h | 5 + 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b4c3044..3960fc3 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1448,6 +1448,17 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) return data-error; } +static inline void dw_mci_dto_start_monitor(struct dw_mci *host) +{ +unsigned int data_tmout_clks; +unsigned int data_tmout_ms; + +data_tmout_clks = (mci_readl(host, TMOUT) 8); +data_tmout_ms = (data_tmout_clks * 1000 / host-bus_hz) + 250; What's 250? And how about using the DIV_ROUND_UP? 250ms is only for more timeout. maybe data timeout read from TMOUT register is enough. So, I will remove 250. new code: data_tmout_clks = (mci_readl(host, TMOUT) 8); data_tmout_ms = DIV_ROUND_UP(data_tmout_clks * 100, host-bus_hz); Is right? + +mod_timer(host-dto_timer, jiffies + msecs_to_jiffies(data_tmout_ms)); +} + static void dw_mci_tasklet_func(unsigned long priv) { struct dw_mci *host = (struct dw_mci *)priv; @@ -1522,8 +1533,11 @@ static void dw_mci_tasklet_func(unsigned long priv) } if (!test_and_clear_bit(EVENT_XFER_COMPLETE, -host-pending_events)) +host-pending_events)) { +if (host-quirks DW_MCI_QUIRK_DTO_TIMER) +dw_mci_dto_start_monitor(host); if timer is starting at only here, dw_mci_dto_start_monitor() doesn't need. Ok, I will change it in the next patch. break; +} set_bit(EVENT_XFER_COMPLETE, host-completed_events); @@ -2115,6 +2129,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } if (pending SDMMC_INT_DATA_OVER) { +if (host-quirks DW_MCI_QUIRK_DTO_TIMER) +del_timer(host-dto_timer); + mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host-data_status) host-data_status = pending; @@ -2502,6 +2519,28 @@ ciu_out: return ret; } +static void dw_mci_dto_timer(unsigned long arg
Re: [PATCH] mmc: dw_mmc: add quirk for data over interrupt timeout
Hi Jaehoon On 2014/11/19 09:22, Jaehoon Chung Wrote: Hi, Addy. On 11/18/2014 09:32 AM, Addy wrote: On 2014年11月14日 21:18, Jaehoon Chung wrote: Hi, Addy. Did you use the DW_MCI_QUIRK_IDMAC_DTO? I'm not sure, but i wonder if you get what result when you use above quirk. DW_MCI_QUIRK_IDMAC_DTO is only for version2.0 or below. /* * DTO fix - version 2.10a and below, and only if internal DMA * is configured. */ if (host-quirks DW_MCI_QUIRK_IDMAC_DTO) { if (!pending ((mci_readl(host, STATUS) 17) 0x1fff)) pending |= SDMMC_INT_DATA_OVER; } It meams that if interrupt comes, but pending = 0 FIFO_COUNT(bit17-29) !=0, then force to set SDMMC_INT_DATA_OVER. But in our case, FIFO_COUNT = 0 (STATUS register value is 0xad06). This is because that the card does not send data to host. So there is no interrupts come, and interrupt handle function(dw_mci_interrupt) will not be called. So we need a timer to handle this case. So I think SDMMC_INT_DATA_OVER is not suitable for this case, and we need a new quirk. And i will check more this patch at next week. Thanks for your efforts. Best Regards, Jaehoon Chung On 11/14/2014 10:05 PM, Addy Ke wrote: From: Addy addy...@rock-chips.com This patch add a new quirk to notify the driver to teminate current transfer and report a data timeout to the core, if data over interrupt does NOT come within the given time. dw_mmc call mmc_request_done func to finish transfer depends on data over interrupt. If data over interrupt does not come in sending data state, the current transfer will be blocked. But this case really exists, when driver reads tuning data from card on rk3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state. This is the cause that card does NOT send data to host. According to synopsys designware databook, the timeout counter is started only after the card clock is stopped. So if card clock is always on, data read timeout interrupt will NOT come, and if data lines are always holded high level, all data-related interrupt such as start-bit error, data crc error, data over interrupt, end-bit error, and so on, will NOT come too. So driver can't get the current state, it can do nothing but wait for. This patch is based on https://patchwork.kernel.org/patch/5227941/ Signed-off-by: Addy addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 47 +- include/linux/mmc/dw_mmc.h | 5 + 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b4c3044..3960fc3 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1448,6 +1448,17 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) return data-error; } +static inline void dw_mci_dto_start_monitor(struct dw_mci *host) +{ +unsigned int data_tmout_clks; +unsigned int data_tmout_ms; + +data_tmout_clks = (mci_readl(host, TMOUT) 8); +data_tmout_ms = (data_tmout_clks * 1000 / host-bus_hz) + 250; What's 250? And how about using the DIV_ROUND_UP? 250ms is only for more timeout. maybe data timeout read from TMOUT register is enough. So, I will remove 250. new code: data_tmout_clks = (mci_readl(host, TMOUT) 8); data_tmout_ms = DIV_ROUND_UP(data_tmout_clks * 100, host-bus_hz); Is right? + +mod_timer(host-dto_timer, jiffies + msecs_to_jiffies(data_tmout_ms)); +} + static void dw_mci_tasklet_func(unsigned long priv) { struct dw_mci *host = (struct dw_mci *)priv; @@ -1522,8 +1533,11 @@ static void dw_mci_tasklet_func(unsigned long priv) } if (!test_and_clear_bit(EVENT_XFER_COMPLETE, -host-pending_events)) +host-pending_events)) { +if (host-quirks DW_MCI_QUIRK_DTO_TIMER) +dw_mci_dto_start_monitor(host); if timer is starting at only here, dw_mci_dto_start_monitor() doesn't need. Ok, I will change it in the next patch. break; +} set_bit(EVENT_XFER_COMPLETE, host-completed_events); @@ -2115,6 +2129,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } if (pending SDMMC_INT_DATA_OVER) { +if (host-quirks DW_MCI_QUIRK_DTO_TIMER) +del_timer(host-dto_timer); + mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host-data_status) host-data_status = pending; @@ -2502,6 +2519,28 @@ ciu_out: return ret; } +static void dw_mci_dto_timer(unsigned long arg) +{ +struct dw_mci *host = (struct dw_mci *)arg; I prefer
Re: [PATCH] mmc: dw_mmc: add quirk for data over interrupt timeout
On 2014年11月14日 21:18, Jaehoon Chung wrote: Hi, Addy. Did you use the DW_MCI_QUIRK_IDMAC_DTO? I'm not sure, but i wonder if you get what result when you use above quirk. DW_MCI_QUIRK_IDMAC_DTO is only for version2.0 or below. /* * DTO fix - version 2.10a and below, and only if internal DMA * is configured. */ if (host-quirks DW_MCI_QUIRK_IDMAC_DTO) { if (!pending ((mci_readl(host, STATUS) 17) 0x1fff)) pending |= SDMMC_INT_DATA_OVER; } It meams that if interrupt comes, but pending = 0 FIFO_COUNT(bit17-29) !=0, then force to set SDMMC_INT_DATA_OVER. But in our case, FIFO_COUNT = 0 (STATUS register value is 0xad06). This is because that the card does not send data to host. So there is no interrupts come, and interrupt handle function(dw_mci_interrupt) will not be called. So we need a timer to handle this case. So I think SDMMC_INT_DATA_OVER is not suitable for this case, and we need a new quirk. And i will check more this patch at next week. Thanks for your efforts. Best Regards, Jaehoon Chung On 11/14/2014 10:05 PM, Addy Ke wrote: From: Addy addy...@rock-chips.com This patch add a new quirk to notify the driver to teminate current transfer and report a data timeout to the core, if data over interrupt does NOT come within the given time. dw_mmc call mmc_request_done func to finish transfer depends on data over interrupt. If data over interrupt does not come in sending data state, the current transfer will be blocked. But this case really exists, when driver reads tuning data from card on rk3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state. This is the cause that card does NOT send data to host. According to synopsys designware databook, the timeout counter is started only after the card clock is stopped. So if card clock is always on, data read timeout interrupt will NOT come, and if data lines are always holded high level, all data-related interrupt such as start-bit error, data crc error, data over interrupt, end-bit error, and so on, will NOT come too. So driver can't get the current state, it can do nothing but wait for. This patch is based on https://patchwork.kernel.org/patch/5227941/ Signed-off-by: Addy addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 47 +- include/linux/mmc/dw_mmc.h | 5 + 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b4c3044..3960fc3 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1448,6 +1448,17 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) return data-error; } +static inline void dw_mci_dto_start_monitor(struct dw_mci *host) +{ + unsigned int data_tmout_clks; + unsigned int data_tmout_ms; + + data_tmout_clks = (mci_readl(host, TMOUT) 8); + data_tmout_ms = (data_tmout_clks * 1000 / host-bus_hz) + 250; + + mod_timer(host-dto_timer, jiffies + msecs_to_jiffies(data_tmout_ms)); +} + static void dw_mci_tasklet_func(unsigned long priv) { struct dw_mci *host = (struct dw_mci *)priv; @@ -1522,8 +1533,11 @@ static void dw_mci_tasklet_func(unsigned long priv) } if (!test_and_clear_bit(EVENT_XFER_COMPLETE, - host-pending_events)) + host-pending_events)) { + if (host-quirks DW_MCI_QUIRK_DTO_TIMER) + dw_mci_dto_start_monitor(host); break; + } set_bit(EVENT_XFER_COMPLETE, host-completed_events); @@ -2115,6 +2129,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } if (pending SDMMC_INT_DATA_OVER) { + if (host-quirks DW_MCI_QUIRK_DTO_TIMER) + del_timer(host-dto_timer); + mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host-data_status) host-data_status = pending; @@ -2502,6 +2519,28 @@ ciu_out: return ret; } +static void dw_mci_dto_timer(unsigned long arg) +{ + struct dw_mci *host = (struct dw_mci *)arg; + + switch (host-state) { + case STATE_SENDING_DATA: + case STATE_DATA_BUSY: + /* + * If data over interrupt does NOT come in sending data state, + * we should notify the driver to teminate current transfer + * and report a data timeout to the core. + */ + host-data_status = SDMMC_INT_DRTO; + set_bit(EVENT_DATA_ERROR, host-pending_events
[PATCH] mmc: dw_mmc: add quirk for data over interrupt timeout
From: Addy addy...@rock-chips.com This patch add a new quirk to notify the driver to teminate current transfer and report a data timeout to the core, if data over interrupt does NOT come within the given time. dw_mmc call mmc_request_done func to finish transfer depends on data over interrupt. If data over interrupt does not come in sending data state, the current transfer will be blocked. But this case really exists, when driver reads tuning data from card on rk3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state. This is the cause that card does NOT send data to host. According to synopsys designware databook, the timeout counter is started only after the card clock is stopped. So if card clock is always on, data read timeout interrupt will NOT come, and if data lines are always holded high level, all data-related interrupt such as start-bit error, data crc error, data over interrupt, end-bit error, and so on, will NOT come too. So driver can't get the current state, it can do nothing but wait for. This patch is based on https://patchwork.kernel.org/patch/5227941/ Signed-off-by: Addy addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 47 +- include/linux/mmc/dw_mmc.h | 5 + 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b4c3044..3960fc3 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1448,6 +1448,17 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) return data-error; } +static inline void dw_mci_dto_start_monitor(struct dw_mci *host) +{ + unsigned int data_tmout_clks; + unsigned int data_tmout_ms; + + data_tmout_clks = (mci_readl(host, TMOUT) 8); + data_tmout_ms = (data_tmout_clks * 1000 / host-bus_hz) + 250; + + mod_timer(host-dto_timer, jiffies + msecs_to_jiffies(data_tmout_ms)); +} + static void dw_mci_tasklet_func(unsigned long priv) { struct dw_mci *host = (struct dw_mci *)priv; @@ -1522,8 +1533,11 @@ static void dw_mci_tasklet_func(unsigned long priv) } if (!test_and_clear_bit(EVENT_XFER_COMPLETE, - host-pending_events)) + host-pending_events)) { + if (host-quirks DW_MCI_QUIRK_DTO_TIMER) + dw_mci_dto_start_monitor(host); break; + } set_bit(EVENT_XFER_COMPLETE, host-completed_events); @@ -2115,6 +2129,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } if (pending SDMMC_INT_DATA_OVER) { + if (host-quirks DW_MCI_QUIRK_DTO_TIMER) + del_timer(host-dto_timer); + mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host-data_status) host-data_status = pending; @@ -2502,6 +2519,28 @@ ciu_out: return ret; } +static void dw_mci_dto_timer(unsigned long arg) +{ + struct dw_mci *host = (struct dw_mci *)arg; + + switch (host-state) { + case STATE_SENDING_DATA: + case STATE_DATA_BUSY: + /* + * If data over interrupt does NOT come in sending data state, + * we should notify the driver to teminate current transfer + * and report a data timeout to the core. + */ + host-data_status = SDMMC_INT_DRTO; + set_bit(EVENT_DATA_ERROR, host-pending_events); + set_bit(EVENT_DATA_COMPLETE, host-pending_events); + tasklet_schedule(host-tasklet); + break; + default: + break; + } +} + #ifdef CONFIG_OF static struct dw_mci_of_quirks { char *quirk; @@ -2513,6 +2552,9 @@ static struct dw_mci_of_quirks { }, { .quirk = disable-wp, .id = DW_MCI_QUIRK_NO_WRITE_PROTECT, + }, { + .quirk = dto-timer, + .id = DW_MCI_QUIRK_DTO_TIMER, }, }; @@ -2654,6 +2696,9 @@ int dw_mci_probe(struct dw_mci *host) spin_lock_init(host-lock); INIT_LIST_HEAD(host-queue); + if (host-quirks DW_MCI_QUIRK_DTO_TIMER) + setup_timer(host-dto_timer, + dw_mci_dto_timer, (unsigned long)host); /* * Get the host data width - this assumes that HCON has been set with diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 42b724e..2477813 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -98,6 +98,7 @@ struct mmc_data
Re: [PATCH] mmc: dw_mmc: try pick the exact same voltage as vmmc for vqmmc
On 2014/11/13 02:04, Doug Anderson wrote: Ulf, On Tue, Nov 11, 2014 at 12:52 AM, Ulf Hansson ulf.hans...@linaro.org wrote: On 11 November 2014 05:02, Addy Ke addy...@rock-chips.com wrote: SD2.0 cards need vqmmc and vmmc to be the same. No, that's not correct. If I remember the spec correctly, the bus signal threshold is 0.75 * VDD. As usual, I will first state my utter lack of knowledge of all things mmc. Now that's out of the way, on two separate board with two separate SoCs I've heard stories of cards that don't work when there's a big gap between vmmc and vqmmc. If my memory serves, previously I heard of problems with vmmc=3.3V and vqmmc=2.8V. That means there were problems with .85 * VDD. Certainly Addy seems to have a card that has problems with vmmc=3.3V and vqmmc=2.7V (but worked with vmmc=3.3V and vqmmc=2.8V). That is .82 * VDD. I have no idea if these old cards are to spec, but they exist and it would be nice to support them. It seems like the absolute safest thing would be to try to keep vmmc and vqmmc matching if possible, especially during card probe. Once voltage negotiation happened then the vqmmc could go down. But vqmmc call regulator_set_voltage to set min_uv(2.7v) as far as possible. I guess you want to do that to save as much power as possible. I don't think it's Addy wanting it, I think it's the regulator framework. If a regulator is current 1.8V and you request 2.7 - 3.3V, the framework needs to pick one of those voltages. I believe it will pick 2.7V. ...so I think we get into trouble only when the 2.0 card is plugged in after a UHS card has negotiated down the voltage, but I could be wrong. Maybe Addy can clarify. Sure If the first card is sd2.0 since startup, dw_mci_switch_voltage will not be called, and card can be identified. But if UHS card is pulgged in first, the vqmmc will be down to 1.8v. when sd2.0 card is pulgged in, mmc core will call dw_mci_switch_voltage to change vqmmc to 3.3v (MMC_SINGLE_VOTAGE_330). So vqmmc will be set 2.7v, if we request 2.7-3.6v. But vmmc is always 3.3v,becuase it be set min_volt = max_volt = 3.3v in dt tables. So the result: vmmc = 3.3v and vqmmc = 2.7v, and sd2.0 card is failed to identify in my test. @@ -1163,8 +1163,14 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) */ uhs = mci_readl(host, UHS_REG); if (ios-signal_voltage == MMC_SIGNAL_VOLTAGE_330) { - min_uv = 270; - max_uv = 360; + /* try pick the exact same voltage as vmmc for vqmmc */ This seems like a generic SD protocol issue. Should we maybe provide some helper function from the mmc core, which in principle take the negotiated card-ocr into account while calculating the signal voltage level. Typically min_uv should be 0.75 x (card-ocr), for these cases. Yes, if there are ways to make the solution more generic I would certainly support that. -Doug -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] mmc: dw_mmc: try pick the exact same voltage as vmmc for vqmmc
SD2.0 cards need vqmmc and vmmc to be the same. But vqmmc call regulator_set_voltage to set min_uv(2.7v) as far as possible. So if we set vmmc 3.3V in dt table, we will get error information as follows: [ 17.785398] mmc_host mmc1: Bus speed (slot 0) = 5000Hz (slot req 5000Hz, actual 5000HZ div = 0) [ 17.795175] mmc1: new high speed SDHC card at address e624 [ 17.801283] mmcblk1: mmc1:e624 SU08G 7.40 GiB [ 17.816033] mmcblk1: p1 [ 17.839318] mmcblk1: error -110 sending status command, retrying [ 17.845363] mmcblk1: error -115 sending stop command, original cmd response 0x900, card status 0x800b00 [ 17.854758] mmcblk1: error -84 transferring data, sector 32, nr 24, cmd response 0x900, card status 0xb00 [ 17.864328] mmcblk1: retrying using single block read [ 17.873647] mmcblk1: error -110 sending status command, retrying [ 17.879660] mmcblk1: error -84 transferring data, sector 44, nr 12, cmd response 0x900, card status 0x0 [ 17.889051] end_request: I/O error, dev mmcblk1, sector 44 [ 17.895594] Buffer I/O error on device mmcblk1, logical block 5 [ 17.902484] mmcblk1: error -110 sending status command, retrying [ 17.908498] mmcblk1: error -84 transferring data, sector 50, nr 6, cmd response 0x900, card status 0x0 [ 17.917802] end_request: I/O error, dev mmcblk1, sector 50 [ 17.924984] Buffer I/O error on device mmcblk1, logical block 6 [ 18.431258] mmc_host mmc1: Timeout sending command (cmd 0x20 arg 0x0 status 0x8020) Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 10 -- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b4c3044..a8b70b5 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1163,8 +1163,14 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) */ uhs = mci_readl(host, UHS_REG); if (ios-signal_voltage == MMC_SIGNAL_VOLTAGE_330) { - min_uv = 270; - max_uv = 360; + /* try pick the exact same voltage as vmmc for vqmmc */ + if (!IS_ERR(mmc-supply.vmmc)) { + min_uv = regulator_get_voltage(mmc-supply.vmmc); + max_uv = min_uv; + } else { + min_uv = 270; + max_uv = 360; + } uhs = ~v18; } else { min_uv = 170; -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v4] mmc: dw_mmc: add support for the other bit of sdio interrupt
The bit of sdio interrupt is 16 in designware implementation, but it is 24 on Rockchip SoCs.This patch add sdio_id0 for the number of slot0 in the SDIO interrupt registers. Signed-off-by: Addy Ke addy...@rock-chips.com --- Changes in v2: - rebase on http://git.linaro.org/git/people/ulf.hansson/mmc.git, next branch Changes in v3: - Remove dts for sdio_id0, just replace this with 8, suggested by Doug - Change to support all Rockchip Socs, suggested by Heiko Changes in v4: - use init-hook to set sdio_id0, suggested by Jaehoon drivers/mmc/host/dw_mmc-rockchip.c | 10 ++ drivers/mmc/host/dw_mmc.c | 12 +++- drivers/mmc/host/dw_mmc.h | 2 ++ include/linux/mmc/dw_mmc.h | 3 +++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index bbb4ec3..5650ac4 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -68,14 +68,24 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) } } +static int dw_mci_rockchip_init(struct dw_mci *host) +{ + /* It is slot 8 on Rockchip SoCs */ + host-sdio_id0 = 8; + + return 0; +} + static const struct dw_mci_drv_data rk2928_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, + .init = dw_mci_rockchip_init, }; static const struct dw_mci_drv_data rk3288_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, .set_ios= dw_mci_rk3288_set_ios, .setup_clock= dw_mci_rk3288_setup_clock, + .init = dw_mci_rockchip_init, }; static const struct of_device_id dw_mci_rockchip_match[] = { diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index bb46b1b..a633b58 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -823,7 +823,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE slot-id; - if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-id))) + if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-sdio_id))) clk_en_a |= SDMMC_CLKEN_LOW_PWR slot-id; mci_writel(host, CLKENA, clk_en_a); @@ -1184,10 +1184,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) dw_mci_disable_low_power(slot); mci_writel(host, INTMASK, - (int_mask | SDMMC_INT_SDIO(slot-id))); + (int_mask | SDMMC_INT_SDIO(slot-sdio_id))); } else { mci_writel(host, INTMASK, - (int_mask ~SDMMC_INT_SDIO(slot-id))); + (int_mask ~SDMMC_INT_SDIO(slot-sdio_id))); } } @@ -2056,8 +2056,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) /* Handle SDIO Interrupts */ for (i = 0; i host-num_slots; i++) { struct dw_mci_slot *slot = host-slot[i]; - if (pending SDMMC_INT_SDIO(i)) { - mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); + if (pending SDMMC_INT_SDIO(slot-sdio_id)) { + mci_writel(host, RINTSTS, + SDMMC_INT_SDIO(slot-sdio_id)); mmc_signal_sdio_irq(slot-mmc); } } @@ -2145,6 +2146,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot = mmc_priv(mmc); slot-id = id; + slot-sdio_id = host-sdio_id0 + id; slot-mmc = mmc; slot-host = host; host-slot[id] = slot; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 71d4995..0562f10 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -214,6 +214,7 @@ extern int dw_mci_resume(struct dw_mci *host); * with CONFIG_MMC_CLKGATE. * @flags: Random state bits associated with the slot. * @id: Number of this slot. + * @sdio_id: Number of this slot in the SDIO interrupt registers. */ struct dw_mci_slot { struct mmc_host *mmc; @@ -233,6 +234,7 @@ struct dw_mci_slot { #define DW_MMC_CARD_PRESENT0 #define DW_MMC_CARD_NEED_INIT 1 int id; + int sdio_id; }; struct dw_mci_tuning_data { diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 69d0814..72c319f 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -96,6 +96,7 @@ struct mmc_data; * @quirks: Set of quirks that apply to specific versions of the IP. * @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq
Re: [PATCH v3] mmc: dw_mmc: add support for the other bit of sdio interrupt
Hi, Jaehoo On 2014/11/3 16:59, Jaehoon Chung wrote: Hi, Addy. On 11/03/2014 10:20 AM, Addy Ke wrote: The bit of sdio interrupt is 16 in designware implementation, but it is 24 on Rockchip SoCs.This patch add sdio_id0 for the number of slot0 in the SDIO interrupt registers. Signed-off-by: Addy Ke addy...@rock-chips.com --- Changes in v2: - rebase on http://git.linaro.org/git/people/ulf.hansson/mmc.git, next branch Changes in v3: - Remove dts for sdio_id0, just replace this with 8, suggested by Doug - Change to support all Rockchip Socs, suggested by Heiko drivers/mmc/host/dw_mmc-rockchip.c | 10 ++ drivers/mmc/host/dw_mmc.c | 12 +++- drivers/mmc/host/dw_mmc.h | 2 ++ include/linux/mmc/dw_mmc.h | 3 +++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index bbb4ec3..b997c8f 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -68,14 +68,24 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) } } +static int dw_mci_rockchip_parse_dt(struct dw_mci *host) +{ +/* It is slot 8 on Rockchip SoCs */ +host-sdio_id0 = 8; + +return 0; +} Well, function is __parse_dt__, but this function don't parse anything. If All rockchip soc is supported, i think that it can be located to other place. Can add it in init function? like this: int dw_mci_rockchip_init(struct dw_mci *host) { /* It is slot 8 on Rockchip SoCs */ host-sdio_id0 = 8; return 0; } static const struct dw_mci_drv_data { .init = dw_mci_rockchip_init, }; Best Regards, Jaehoon Chung + static const struct dw_mci_drv_data rk2928_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, +.parse_dt = dw_mci_rockchip_parse_dt, }; static const struct dw_mci_drv_data rk3288_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, .set_ios= dw_mci_rk3288_set_ios, .setup_clock= dw_mci_rk3288_setup_clock, +.parse_dt = dw_mci_rockchip_parse_dt, }; static const struct of_device_id dw_mci_rockchip_match[] = { diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index bb46b1b..a633b58 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -823,7 +823,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE slot-id; -if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-id))) +if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-sdio_id))) clk_en_a |= SDMMC_CLKEN_LOW_PWR slot-id; mci_writel(host, CLKENA, clk_en_a); @@ -1184,10 +1184,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) dw_mci_disable_low_power(slot); mci_writel(host, INTMASK, - (int_mask | SDMMC_INT_SDIO(slot-id))); + (int_mask | SDMMC_INT_SDIO(slot-sdio_id))); } else { mci_writel(host, INTMASK, - (int_mask ~SDMMC_INT_SDIO(slot-id))); + (int_mask ~SDMMC_INT_SDIO(slot-sdio_id))); } } @@ -2056,8 +2056,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) /* Handle SDIO Interrupts */ for (i = 0; i host-num_slots; i++) { struct dw_mci_slot *slot = host-slot[i]; -if (pending SDMMC_INT_SDIO(i)) { -mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); +if (pending SDMMC_INT_SDIO(slot-sdio_id)) { +mci_writel(host, RINTSTS, + SDMMC_INT_SDIO(slot-sdio_id)); mmc_signal_sdio_irq(slot-mmc); } } @@ -2145,6 +2146,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot = mmc_priv(mmc); slot-id = id; +slot-sdio_id = host-sdio_id0 + id; slot-mmc = mmc; slot-host = host; host-slot[id] = slot; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 71d4995..0562f10 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -214,6 +214,7 @@ extern int dw_mci_resume(struct dw_mci *host); * with CONFIG_MMC_CLKGATE. * @flags: Random state bits associated with the slot. * @id: Number of this slot. + * @sdio_id: Number of this slot in the SDIO interrupt registers. */ struct dw_mci_slot { struct mmc_host *mmc; @@ -233,6 +234,7 @@ struct dw_mci_slot { #define DW_MMC_CARD_PRESENT 0 #define DW_MMC_CARD_NEED_INIT 1 int
Re: [PATCH v2] mmc: dw_mmc: add support for the other bit of sdio interrupt
On 2014/10/31 18:43, Heiko Stübner wrote: Am Freitag, 31. Oktober 2014, 11:50:09 schrieb Addy Ke: The bit of sdio interrupt is 16 in designware implementation, but it is 24 in RK3288. This patch add sdio_id0 for the number of slot0 in the SDIO interrupt registers, which can be set in platform DT table, such as: - rockchip,sdio-interrupt-slot0 = 8; I just checked the manuals of rk3066 and rk3188 - and it seems the sdio interrupt is in bit 24 on all of them. Addy, could you check this and maybe enable this for all two variants we currently support? OK, I will do so in my next patch. thank you! Thanks Heiko Signed-off-by: Addy Ke addy...@rock-chips.com --- Changes in v2: - rebase on http://git.linaro.org/git/people/ulf.hansson/mmc.git, next branch drivers/mmc/host/dw_mmc-rockchip.c | 13 + drivers/mmc/host/dw_mmc.c | 12 +++- drivers/mmc/host/dw_mmc.h | 2 ++ include/linux/mmc/dw_mmc.h | 3 +++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index bbb4ec3..1cb3bc6 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -68,6 +68,18 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) } } +static int dw_mci_rk3288_parse_dt(struct dw_mci *host) +{ +struct device_node *np = host-dev-of_node; +int sdio_id0; + +if (!of_property_read_u32(np, rockchip,sdio-interrupt-slot0, + sdio_id0)) +host-sdio_id0 = sdio_id0; + +return 0; +} + static const struct dw_mci_drv_data rk2928_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, }; @@ -76,6 +88,7 @@ static const struct dw_mci_drv_data rk3288_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, .set_ios= dw_mci_rk3288_set_ios, .setup_clock= dw_mci_rk3288_setup_clock, +.parse_dt = dw_mci_rk3288_parse_dt, }; static const struct of_device_id dw_mci_rockchip_match[] = { diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index bb46b1b..a633b58 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -823,7 +823,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE slot-id; -if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-id))) +if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-sdio_id))) clk_en_a |= SDMMC_CLKEN_LOW_PWR slot-id; mci_writel(host, CLKENA, clk_en_a); @@ -1184,10 +1184,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) dw_mci_disable_low_power(slot); mci_writel(host, INTMASK, - (int_mask | SDMMC_INT_SDIO(slot-id))); + (int_mask | SDMMC_INT_SDIO(slot-sdio_id))); } else { mci_writel(host, INTMASK, - (int_mask ~SDMMC_INT_SDIO(slot-id))); + (int_mask ~SDMMC_INT_SDIO(slot-sdio_id))); } } @@ -2056,8 +2056,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) /* Handle SDIO Interrupts */ for (i = 0; i host-num_slots; i++) { struct dw_mci_slot *slot = host-slot[i]; -if (pending SDMMC_INT_SDIO(i)) { -mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); +if (pending SDMMC_INT_SDIO(slot-sdio_id)) { +mci_writel(host, RINTSTS, + SDMMC_INT_SDIO(slot-sdio_id)); mmc_signal_sdio_irq(slot-mmc); } } @@ -2145,6 +2146,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot = mmc_priv(mmc); slot-id = id; +slot-sdio_id = host-sdio_id0 + id; slot-mmc = mmc; slot-host = host; host-slot[id] = slot; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 71d4995..0562f10 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -214,6 +214,7 @@ extern int dw_mci_resume(struct dw_mci *host); * with CONFIG_MMC_CLKGATE. * @flags: Random state bits associated with the slot. * @id: Number of this slot. + * @sdio_id: Number of this slot in the SDIO interrupt registers. */ struct dw_mci_slot { struct mmc_host *mmc; @@ -233,6 +234,7 @@ struct dw_mci_slot { #define DW_MMC_CARD_PRESENT 0 #define DW_MMC_CARD_NEED_INIT 1 int id; +int sdio_id; }; struct dw_mci_tuning_data { diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 69d0814..72c319f
[PATCH v3] mmc: dw_mmc: add support for the other bit of sdio interrupt
The bit of sdio interrupt is 16 in designware implementation, but it is 24 on Rockchip SoCs.This patch add sdio_id0 for the number of slot0 in the SDIO interrupt registers. Signed-off-by: Addy Ke addy...@rock-chips.com --- Changes in v2: - rebase on http://git.linaro.org/git/people/ulf.hansson/mmc.git, next branch Changes in v3: - Remove dts for sdio_id0, just replace this with 8, suggested by Doug - Change to support all Rockchip Socs, suggested by Heiko drivers/mmc/host/dw_mmc-rockchip.c | 10 ++ drivers/mmc/host/dw_mmc.c | 12 +++- drivers/mmc/host/dw_mmc.h | 2 ++ include/linux/mmc/dw_mmc.h | 3 +++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index bbb4ec3..b997c8f 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -68,14 +68,24 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) } } +static int dw_mci_rockchip_parse_dt(struct dw_mci *host) +{ + /* It is slot 8 on Rockchip SoCs */ + host-sdio_id0 = 8; + + return 0; +} + static const struct dw_mci_drv_data rk2928_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, + .parse_dt = dw_mci_rockchip_parse_dt, }; static const struct dw_mci_drv_data rk3288_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, .set_ios= dw_mci_rk3288_set_ios, .setup_clock= dw_mci_rk3288_setup_clock, + .parse_dt = dw_mci_rockchip_parse_dt, }; static const struct of_device_id dw_mci_rockchip_match[] = { diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index bb46b1b..a633b58 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -823,7 +823,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE slot-id; - if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-id))) + if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-sdio_id))) clk_en_a |= SDMMC_CLKEN_LOW_PWR slot-id; mci_writel(host, CLKENA, clk_en_a); @@ -1184,10 +1184,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) dw_mci_disable_low_power(slot); mci_writel(host, INTMASK, - (int_mask | SDMMC_INT_SDIO(slot-id))); + (int_mask | SDMMC_INT_SDIO(slot-sdio_id))); } else { mci_writel(host, INTMASK, - (int_mask ~SDMMC_INT_SDIO(slot-id))); + (int_mask ~SDMMC_INT_SDIO(slot-sdio_id))); } } @@ -2056,8 +2056,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) /* Handle SDIO Interrupts */ for (i = 0; i host-num_slots; i++) { struct dw_mci_slot *slot = host-slot[i]; - if (pending SDMMC_INT_SDIO(i)) { - mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); + if (pending SDMMC_INT_SDIO(slot-sdio_id)) { + mci_writel(host, RINTSTS, + SDMMC_INT_SDIO(slot-sdio_id)); mmc_signal_sdio_irq(slot-mmc); } } @@ -2145,6 +2146,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot = mmc_priv(mmc); slot-id = id; + slot-sdio_id = host-sdio_id0 + id; slot-mmc = mmc; slot-host = host; host-slot[id] = slot; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 71d4995..0562f10 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -214,6 +214,7 @@ extern int dw_mci_resume(struct dw_mci *host); * with CONFIG_MMC_CLKGATE. * @flags: Random state bits associated with the slot. * @id: Number of this slot. + * @sdio_id: Number of this slot in the SDIO interrupt registers. */ struct dw_mci_slot { struct mmc_host *mmc; @@ -233,6 +234,7 @@ struct dw_mci_slot { #define DW_MMC_CARD_PRESENT0 #define DW_MMC_CARD_NEED_INIT 1 int id; + int sdio_id; }; struct dw_mci_tuning_data { diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 69d0814..72c319f 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -96,6 +96,7 @@ struct mmc_data; * @quirks: Set of quirks that apply to specific versions of the IP. * @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. + * @sdio_id0: Number of slot0 in the SDIO interrupt registers
Re: [PATCH] mmc: dw_mmc: add a quirk for the defferent bit of sdio interrupt
Hi, Doug, On 2014/10/30 12:49, Doug Anderson wrote: Addy, On Wed, Oct 29, 2014 at 9:41 PM, Doug Anderson diand...@chromium.org wrote: You can avoid a lot of if tests if you just add a new sdio-id Whoops, I mean slot-sdio_id To use slot-sdio_id, I think the subject must be changed. So I will drop this patch ,and send new patch to use slot-sdio_id for this difference. thank you! -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] mmc: dw_mmc: add support for the other bit of sdio interrupt
The bit of sdio interrupt is 16 in designware implementation, but it is 24 in RK3288. This patch add sdio_id0 for the number of slot0 in the SDIO interrupt registers, which can be set in platform DT table, such as: - rockchip,sdio-interrupt-slot0 = 8; Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/host/dw_mmc-rockchip.c | 13 + drivers/mmc/host/dw_mmc.c | 12 +++- drivers/mmc/host/dw_mmc.h | 2 ++ include/linux/mmc/dw_mmc.h | 3 +++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index f0c2cb1..54655e7 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -65,6 +65,18 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) } } +static int dw_mci_rk3288_parse_dt(struct dw_mci *host) +{ + struct device_node *np = host-dev-of_node; + int sdio_id0; + + if (!of_property_read_u32(np, rockchip,sdio-interrupt-slot0, + sdio_id0)) + host-sdio_id0 = sdio_id0; + + return 0; +} + static const struct dw_mci_drv_data rk2928_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, }; @@ -73,6 +85,7 @@ static const struct dw_mci_drv_data rk3288_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, .set_ios= dw_mci_rk3288_set_ios, .setup_clock= dw_mci_rk3288_setup_clock, + .parse_dt = dw_mci_rk3288_parse_dt, }; static const struct of_device_id dw_mci_rockchip_match[] = { diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 69f0cc6..2ea7467 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -819,7 +819,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE slot-id; - if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-id))) + if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-sdio_id))) clk_en_a |= SDMMC_CLKEN_LOW_PWR slot-id; mci_writel(host, CLKENA, clk_en_a); @@ -1180,10 +1180,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) dw_mci_disable_low_power(slot); mci_writel(host, INTMASK, - (int_mask | SDMMC_INT_SDIO(slot-id))); + (int_mask | SDMMC_INT_SDIO(slot-sdio_id))); } else { mci_writel(host, INTMASK, - (int_mask ~SDMMC_INT_SDIO(slot-id))); + (int_mask ~SDMMC_INT_SDIO(slot-sdio_id))); } } @@ -2035,8 +2035,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) /* Handle SDIO Interrupts */ for (i = 0; i host-num_slots; i++) { struct dw_mci_slot *slot = host-slot[i]; - if (pending SDMMC_INT_SDIO(i)) { - mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); + if (pending SDMMC_INT_SDIO(slot-sdio_id)) { + mci_writel(host, RINTSTS, + SDMMC_INT_SDIO(slot-sdio_id)); mmc_signal_sdio_irq(slot-mmc); } } @@ -2206,6 +2207,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot = mmc_priv(mmc); slot-id = id; + slot-sdio_id = host-sdio_id0 + id; slot-mmc = mmc; slot-host = host; host-slot[id] = slot; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 01b99e8..3e966a9 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -214,6 +214,7 @@ extern int dw_mci_resume(struct dw_mci *host); * with CONFIG_MMC_CLKGATE. * @flags: Random state bits associated with the slot. * @id: Number of this slot. + * @sdio_id: Number of this slot in the SDIO interrupt registers. * @last_detect_state: Most recently observed card detect state. */ struct dw_mci_slot { @@ -234,6 +235,7 @@ struct dw_mci_slot { #define DW_MMC_CARD_PRESENT0 #define DW_MMC_CARD_NEED_INIT 1 int id; + int sdio_id; int last_detect_state; }; diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 0013669..4c0d3f2 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -96,6 +96,7 @@ struct mmc_data; * @quirks: Set of quirks that apply to specific versions of the IP. * @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. + * @sdio_id0: Number of slot0 in the SDIO interrupt
Re: [PATCH] mmc: dw_mmc: add support for the other bit of sdio interrupt
Hi, Jaehoon On 2014/10/30 19:02, Jaehoon Chung wrote: Hi, Addy. This patch is conflicted..Could you rebase on latest Ulf's tree? I have not found ulf's tree in git.kernel.org. I can't 'git clone git://git.kernel.org/pub/scm/linux/kernel/git/ulf/xxx.git'. So my patch is based on kernel-3.18. I will send patch v2 for this. Would you please give me a git url for it? Thank you. Best Regards, Jaehoon Chung On 10/30/2014 07:50 PM, Addy Ke wrote: The bit of sdio interrupt is 16 in designware implementation, but it is 24 in RK3288. This patch add sdio_id0 for the number of slot0 in the SDIO interrupt registers, which can be set in platform DT table, such as: - rockchip,sdio-interrupt-slot0 = 8; Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/host/dw_mmc-rockchip.c | 13 + drivers/mmc/host/dw_mmc.c | 12 +++- drivers/mmc/host/dw_mmc.h | 2 ++ include/linux/mmc/dw_mmc.h | 3 +++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index f0c2cb1..54655e7 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -65,6 +65,18 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) } } +static int dw_mci_rk3288_parse_dt(struct dw_mci *host) +{ +struct device_node *np = host-dev-of_node; +int sdio_id0; + +if (!of_property_read_u32(np, rockchip,sdio-interrupt-slot0, + sdio_id0)) +host-sdio_id0 = sdio_id0; + +return 0; +} + static const struct dw_mci_drv_data rk2928_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, }; @@ -73,6 +85,7 @@ static const struct dw_mci_drv_data rk3288_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, .set_ios= dw_mci_rk3288_set_ios, .setup_clock= dw_mci_rk3288_setup_clock, +.parse_dt = dw_mci_rk3288_parse_dt, }; static const struct of_device_id dw_mci_rockchip_match[] = { diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 69f0cc6..2ea7467 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -819,7 +819,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE slot-id; -if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-id))) +if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-sdio_id))) clk_en_a |= SDMMC_CLKEN_LOW_PWR slot-id; mci_writel(host, CLKENA, clk_en_a); @@ -1180,10 +1180,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) dw_mci_disable_low_power(slot); mci_writel(host, INTMASK, - (int_mask | SDMMC_INT_SDIO(slot-id))); + (int_mask | SDMMC_INT_SDIO(slot-sdio_id))); } else { mci_writel(host, INTMASK, - (int_mask ~SDMMC_INT_SDIO(slot-id))); + (int_mask ~SDMMC_INT_SDIO(slot-sdio_id))); } } @@ -2035,8 +2035,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) /* Handle SDIO Interrupts */ for (i = 0; i host-num_slots; i++) { struct dw_mci_slot *slot = host-slot[i]; -if (pending SDMMC_INT_SDIO(i)) { -mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); +if (pending SDMMC_INT_SDIO(slot-sdio_id)) { +mci_writel(host, RINTSTS, + SDMMC_INT_SDIO(slot-sdio_id)); mmc_signal_sdio_irq(slot-mmc); } } @@ -2206,6 +2207,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot = mmc_priv(mmc); slot-id = id; +slot-sdio_id = host-sdio_id0 + id; slot-mmc = mmc; slot-host = host; host-slot[id] = slot; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 01b99e8..3e966a9 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -214,6 +214,7 @@ extern int dw_mci_resume(struct dw_mci *host); * with CONFIG_MMC_CLKGATE. * @flags: Random state bits associated with the slot. * @id: Number of this slot. + * @sdio_id: Number of this slot in the SDIO interrupt registers. * @last_detect_state: Most recently observed card detect state. */ struct dw_mci_slot { @@ -234,6 +235,7 @@ struct dw_mci_slot { #define DW_MMC_CARD_PRESENT 0 #define DW_MMC_CARD_NEED_INIT 1 int id; +int sdio_id; int last_detect_state; }; diff
Re: [PATCH] mmc: dw_mmc: add support for the other bit of sdio interrupt
On 2014/10/30 19:17, Jaehoon Chung wrote: On 10/30/2014 08:11 PM, Ulf Hansson wrote: On 30 October 2014 11:50, Addy Ke addy...@rock-chips.com wrote: The bit of sdio interrupt is 16 in designware implementation, but it is 24 in RK3288. This patch add sdio_id0 for the number of slot0 in the SDIO interrupt registers, which can be set in platform DT table, such as: - rockchip,sdio-interrupt-slot0 = 8; No, this shouldn't be information in DT. Instead this can be kept in the driver, depending on what version of the mmc controller that is being used. Right!? sdio-interrupt slot doesn't depend on IP version. maybe it depends on rock-chip board. sure, it is denpends on rockchip soc, not IP version. As far as I know, only our socs(such as RK3288) have this difference. Best Regards, Jaehoon Chung Kind regards Uffe Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/host/dw_mmc-rockchip.c | 13 + drivers/mmc/host/dw_mmc.c | 12 +++- drivers/mmc/host/dw_mmc.h | 2 ++ include/linux/mmc/dw_mmc.h | 3 +++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index f0c2cb1..54655e7 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -65,6 +65,18 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) } } +static int dw_mci_rk3288_parse_dt(struct dw_mci *host) +{ + struct device_node *np = host-dev-of_node; + int sdio_id0; + + if (!of_property_read_u32(np, rockchip,sdio-interrupt-slot0, + sdio_id0)) + host-sdio_id0 = sdio_id0; + + return 0; +} + static const struct dw_mci_drv_data rk2928_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, }; @@ -73,6 +85,7 @@ static const struct dw_mci_drv_data rk3288_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, .set_ios= dw_mci_rk3288_set_ios, .setup_clock= dw_mci_rk3288_setup_clock, + .parse_dt = dw_mci_rk3288_parse_dt, }; static const struct of_device_id dw_mci_rockchip_match[] = { diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 69f0cc6..2ea7467 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -819,7 +819,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE slot-id; - if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-id))) + if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-sdio_id))) clk_en_a |= SDMMC_CLKEN_LOW_PWR slot-id; mci_writel(host, CLKENA, clk_en_a); @@ -1180,10 +1180,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) dw_mci_disable_low_power(slot); mci_writel(host, INTMASK, - (int_mask | SDMMC_INT_SDIO(slot-id))); + (int_mask | SDMMC_INT_SDIO(slot-sdio_id))); } else { mci_writel(host, INTMASK, - (int_mask ~SDMMC_INT_SDIO(slot-id))); + (int_mask ~SDMMC_INT_SDIO(slot-sdio_id))); } } @@ -2035,8 +2035,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) /* Handle SDIO Interrupts */ for (i = 0; i host-num_slots; i++) { struct dw_mci_slot *slot = host-slot[i]; - if (pending SDMMC_INT_SDIO(i)) { - mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); + if (pending SDMMC_INT_SDIO(slot-sdio_id)) { + mci_writel(host, RINTSTS, + SDMMC_INT_SDIO(slot-sdio_id)); mmc_signal_sdio_irq(slot-mmc); } } @@ -2206,6 +2207,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot = mmc_priv(mmc); slot-id = id; + slot-sdio_id = host-sdio_id0 + id; slot-mmc = mmc; slot-host = host; host-slot[id] = slot; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 01b99e8..3e966a9 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -214,6 +214,7 @@ extern int dw_mci_resume(struct dw_mci *host); * with CONFIG_MMC_CLKGATE. * @flags: Random state bits associated with the slot. * @id: Number of this slot. + * @sdio_id: Number of this slot in the SDIO interrupt registers. * @last_detect_state: Most recently observed card detect state. */ struct dw_mci_slot
[PATCH v2] mmc: dw_mmc: add support for the other bit of sdio interrupt
The bit of sdio interrupt is 16 in designware implementation, but it is 24 in RK3288. This patch add sdio_id0 for the number of slot0 in the SDIO interrupt registers, which can be set in platform DT table, such as: - rockchip,sdio-interrupt-slot0 = 8; Signed-off-by: Addy Ke addy...@rock-chips.com --- Changes in v2: - rebase on http://git.linaro.org/git/people/ulf.hansson/mmc.git, next branch drivers/mmc/host/dw_mmc-rockchip.c | 13 + drivers/mmc/host/dw_mmc.c | 12 +++- drivers/mmc/host/dw_mmc.h | 2 ++ include/linux/mmc/dw_mmc.h | 3 +++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index bbb4ec3..1cb3bc6 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -68,6 +68,18 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) } } +static int dw_mci_rk3288_parse_dt(struct dw_mci *host) +{ + struct device_node *np = host-dev-of_node; + int sdio_id0; + + if (!of_property_read_u32(np, rockchip,sdio-interrupt-slot0, + sdio_id0)) + host-sdio_id0 = sdio_id0; + + return 0; +} + static const struct dw_mci_drv_data rk2928_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, }; @@ -76,6 +88,7 @@ static const struct dw_mci_drv_data rk3288_drv_data = { .prepare_command= dw_mci_rockchip_prepare_command, .set_ios= dw_mci_rk3288_set_ios, .setup_clock= dw_mci_rk3288_setup_clock, + .parse_dt = dw_mci_rk3288_parse_dt, }; static const struct of_device_id dw_mci_rockchip_match[] = { diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index bb46b1b..a633b58 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -823,7 +823,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE slot-id; - if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-id))) + if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-sdio_id))) clk_en_a |= SDMMC_CLKEN_LOW_PWR slot-id; mci_writel(host, CLKENA, clk_en_a); @@ -1184,10 +1184,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) dw_mci_disable_low_power(slot); mci_writel(host, INTMASK, - (int_mask | SDMMC_INT_SDIO(slot-id))); + (int_mask | SDMMC_INT_SDIO(slot-sdio_id))); } else { mci_writel(host, INTMASK, - (int_mask ~SDMMC_INT_SDIO(slot-id))); + (int_mask ~SDMMC_INT_SDIO(slot-sdio_id))); } } @@ -2056,8 +2056,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) /* Handle SDIO Interrupts */ for (i = 0; i host-num_slots; i++) { struct dw_mci_slot *slot = host-slot[i]; - if (pending SDMMC_INT_SDIO(i)) { - mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); + if (pending SDMMC_INT_SDIO(slot-sdio_id)) { + mci_writel(host, RINTSTS, + SDMMC_INT_SDIO(slot-sdio_id)); mmc_signal_sdio_irq(slot-mmc); } } @@ -2145,6 +2146,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot = mmc_priv(mmc); slot-id = id; + slot-sdio_id = host-sdio_id0 + id; slot-mmc = mmc; slot-host = host; host-slot[id] = slot; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 71d4995..0562f10 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -214,6 +214,7 @@ extern int dw_mci_resume(struct dw_mci *host); * with CONFIG_MMC_CLKGATE. * @flags: Random state bits associated with the slot. * @id: Number of this slot. + * @sdio_id: Number of this slot in the SDIO interrupt registers. */ struct dw_mci_slot { struct mmc_host *mmc; @@ -233,6 +234,7 @@ struct dw_mci_slot { #define DW_MMC_CARD_PRESENT0 #define DW_MMC_CARD_NEED_INIT 1 int id; + int sdio_id; }; struct dw_mci_tuning_data { diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 69d0814..72c319f 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -96,6 +96,7 @@ struct mmc_data; * @quirks: Set of quirks that apply to specific versions of the IP. * @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. + * @sdio_id0
[PATCH] mmc: dw_mmc: add a quirk for the defferent bit of sdio interrupt
This patch add a quirk: DW_MCI_QUIRK_SDIO_INT_24BIT. The bit of sdio interrupt is 16 in designware implementation, but is 24 in RK3288. To support RK3288 mmc controller, we need add a quirk for it. Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/host/dw_mmc.c | 32 +++- drivers/mmc/host/dw_mmc.h | 1 + include/linux/mmc/dw_mmc.h | 2 ++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 69f0cc6..db29621 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -778,6 +778,12 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) u32 div; u32 clk_en_a; u32 sdmmc_cmd_bits = SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT; + u32 sdio_int_bit; + + if (host-quirks DW_MCI_QUIRK_SDIO_INT_24BIT) + sdio_int_bit = SDMMC_INT_SDIO_24BIT(slot-id); + else + sdio_int_bit = SDMMC_INT_SDIO(slot-id); /* We must continue to set bit 28 in CMD until the change is complete */ if (host-state == STATE_WAITING_CMD11_DONE) @@ -819,7 +825,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE slot-id; - if (!(mci_readl(host, INTMASK) SDMMC_INT_SDIO(slot-id))) + if (!(mci_readl(host, INTMASK) sdio_int_bit)) clk_en_a |= SDMMC_CLKEN_LOW_PWR slot-id; mci_writel(host, CLKENA, clk_en_a); @@ -1167,6 +1173,12 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot-host; u32 int_mask; + u32 sdio_int_bit; + + if (host-quirks DW_MCI_QUIRK_SDIO_INT_24BIT) + sdio_int_bit = SDMMC_INT_SDIO_24BIT(slot-id); + else + sdio_int_bit = SDMMC_INT_SDIO(slot-id); /* Enable/disable Slot Specific SDIO interrupt */ int_mask = mci_readl(host, INTMASK); @@ -1180,10 +1192,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) dw_mci_disable_low_power(slot); mci_writel(host, INTMASK, - (int_mask | SDMMC_INT_SDIO(slot-id))); + (int_mask | sdio_int_bit)); } else { mci_writel(host, INTMASK, - (int_mask ~SDMMC_INT_SDIO(slot-id))); + (int_mask ~sdio_int_bit)); } } @@ -2035,8 +2047,15 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) /* Handle SDIO Interrupts */ for (i = 0; i host-num_slots; i++) { struct dw_mci_slot *slot = host-slot[i]; - if (pending SDMMC_INT_SDIO(i)) { - mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); + u32 sdio_int_bit; + + if (host-quirks DW_MCI_QUIRK_SDIO_INT_24BIT) + sdio_int_bit = SDMMC_INT_SDIO_24BIT(i); + else + sdio_int_bit = SDMMC_INT_SDIO(i); + + if (pending sdio_int_bit) { + mci_writel(host, RINTSTS, sdio_int_bit); mmc_signal_sdio_irq(slot-mmc); } } @@ -2452,6 +2471,9 @@ static struct dw_mci_of_quirks { }, { .quirk = disable-wp, .id = DW_MCI_QUIRK_NO_WRITE_PROTECT, + }, { + .quirk = sdio-int-24bit, + .id = DW_MCI_QUIRK_SDIO_INT_24BIT, }, }; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 01b99e8..6a48015 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -92,6 +92,7 @@ #define SDMMC_CTYPE_4BIT BIT(0) #define SDMMC_CTYPE_1BIT 0 /* Interrupt status mask register defines */ +#define SDMMC_INT_SDIO_24BIT(n)BIT(24 + (n)) #define SDMMC_INT_SDIO(n) BIT(16 + (n)) #define SDMMC_INT_EBE BIT(15) #define SDMMC_INT_ACD BIT(14) diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 0013669..6d4669e 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -217,6 +217,8 @@ struct dw_mci_dma_ops { #define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3) /* No write protect */ #define DW_MCI_QUIRK_NO_WRITE_PROTECT BIT(4) +/* In RK3288, the bit of sdio interrupt is 24 */ +#define DW_MCI_QUIRK_SDIO_INT_24BITBIT(5) /* Slot level quirks */ /* This slot has no write protect */ -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord
Re: [PATCH 1/2] spi/rockchip: fix bug that case spi can't go as fast as slave request
On 2014/10/16 17:34, Mark Brown wrote: On Thu, Oct 16, 2014 at 05:16:02PM +0800, addy ke wrote: On 2014/10/15 21:04, Mark Brown wrote: On Wed, Oct 15, 2014 at 07:25:49PM +0800, Addy Ke wrote: + if (WARN_ON(rs-speed MAX_SCLK_OUT)) + rs-speed = MAX_SCLK_OUT; + /* the minimum divsor is 2 */ + if (rs-max_freq 2 * rs-speed) { + clk_set_rate(rs-spiclk, 2 * rs-speed); + rs-max_freq = clk_get_rate(rs-spiclk); + } I'll apply this but you should be checking the return code from clk_set_rate() here, please send a followup patch doing that. It might If clk_set_rate return error, do I only put dev_warn here or return error value to spi core? It'd be better to return an error if we need to set the rate and can't do it. also be worth consdering just setting the rate unconditionally here, it seems like it should make things simpler. I think we need. If we set the rate unconditionally here, clk_set_rate() will be executed in each spi transfer. Is that really such a high cost? Not high cost, but I think if the default spi_clk is enough, we do not need to set spi_clk again. Maybe we can only set spi_clk as (2 * MAX_SCLK_OUT) in probe(). -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] spi/rockchip: spi controller must be disabled in tx callback too
Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/spi/spi-rockchip.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 153269b..87bc16f 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -418,8 +418,10 @@ static void rockchip_spi_dma_txcb(void *data) spin_lock_irqsave(rs-lock, flags); rs-state = ~TXBUSY; - if (!(rs-state RXBUSY)) + if (!(rs-state RXBUSY)) { + spi_enable_chip(rs, 0); spi_finalize_current_transfer(rs-master); + } spin_unlock_irqrestore(rs-lock, flags); } -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 0/2] spi: fix two bugs
Patch 1: fix bug that case spi can't go as fast as slave request Patch 2: fix bug that cause spi transfer timed out in DMA duplex mode Tested on rk3288-pinky-version2 board. Addy Ke (2): spi/rockchip: fix bug that case spi can't go as fast as slave request spi/rockchip: fix bug that cause spi transfer timed out in DMA duplex mode drivers/spi/spi-rockchip.c | 46 +++--- 1 file changed, 35 insertions(+), 11 deletions(-) -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 1/2] spi/rockchip: fix bug that case spi can't go as fast as slave request
Because the minimum divisor in rk3x's spi controller is 2, if spi_clk is less than 2 * sclk_out, we can't get the right divisor. So we must set spi_clk again to match slave request. Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/spi/spi-rockchip.c | 12 1 file changed, 12 insertions(+) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index f96ea8a..3044c6c 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -145,6 +145,9 @@ #define RXBUSY (1 0) #define TXBUSY (1 1) +/* sclk_out: spi master internal logic in rk3x can support 50Mhz */ +#define MAX_SCLK_OUT 5000 + enum rockchip_ssi_type { SSI_MOTO_SPI = 0, SSI_TI_SSP, @@ -496,6 +499,15 @@ static void rockchip_spi_config(struct rockchip_spi *rs) dmacr |= RF_DMA_EN; } + if (WARN_ON(rs-speed MAX_SCLK_OUT)) + rs-speed = MAX_SCLK_OUT; + + /* the minimum divsor is 2 */ + if (rs-max_freq 2 * rs-speed) { + clk_set_rate(rs-spiclk, 2 * rs-speed); + rs-max_freq = clk_get_rate(rs-spiclk); + } + /* div doesn't support odd number */ div = max_t(u32, rs-max_freq / rs-speed, 1); div = (div + 1) 0xfffe; -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 2/2] spi/rockchip: fix bug that cause spi transfer timed out in DMA duplex mode
In rx mode, dma must be prepared before spi is enabled. But in tx and tr mode, spi must be enabled first. Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/spi/spi-rockchip.c | 34 +++--- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 3044c6c..153269b 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -328,6 +328,8 @@ static int rockchip_spi_unprepare_message(struct spi_master *master, spin_unlock_irqrestore(rs-lock, flags); + spi_enable_chip(rs, 0); + return 0; } @@ -384,6 +386,8 @@ static int rockchip_spi_pio_transfer(struct rockchip_spi *rs) if (rs-tx) wait_for_idle(rs); + spi_enable_chip(rs, 0); + return 0; } @@ -395,8 +399,10 @@ static void rockchip_spi_dma_rxcb(void *data) spin_lock_irqsave(rs-lock, flags); rs-state = ~RXBUSY; - if (!(rs-state TXBUSY)) + if (!(rs-state TXBUSY)) { + spi_enable_chip(rs, 0); spi_finalize_current_transfer(rs-master); + } spin_unlock_irqrestore(rs-lock, flags); } @@ -512,8 +518,6 @@ static void rockchip_spi_config(struct rockchip_spi *rs) div = max_t(u32, rs-max_freq / rs-speed, 1); div = (div + 1) 0xfffe; - spi_enable_chip(rs, 0); - writel_relaxed(cr0, rs-regs + ROCKCHIP_SPI_CTRLR0); writel_relaxed(rs-len - 1, rs-regs + ROCKCHIP_SPI_CTRLR1); @@ -527,8 +531,6 @@ static void rockchip_spi_config(struct rockchip_spi *rs) spi_set_clk(rs, div); dev_dbg(rs-dev, cr0 0x%x, div %d\n, cr0, div); - - spi_enable_chip(rs, 1); } static int rockchip_spi_transfer_one( @@ -536,7 +538,7 @@ static int rockchip_spi_transfer_one( struct spi_device *spi, struct spi_transfer *xfer) { - int ret = 0; + int ret = 1; struct rockchip_spi *rs = spi_master_get_devdata(master); WARN_ON(readl_relaxed(rs-regs + ROCKCHIP_SPI_SSIENR) @@ -568,17 +570,27 @@ static int rockchip_spi_transfer_one( rs-tmode = CR0_XFM_RO; /* we need prepare dma before spi was enabled */ - if (master-can_dma master-can_dma(master, spi, xfer)) { + if (master-can_dma master-can_dma(master, spi, xfer)) rs-use_dma = 1; - rockchip_spi_prepare_dma(rs); - } else { + else rs-use_dma = 0; - } rockchip_spi_config(rs); - if (!rs-use_dma) + if (rs-use_dma) { + if (rs-tmode == CR0_XFM_RO) { + /* rx: dma must be prepared first */ + rockchip_spi_prepare_dma(rs); + spi_enable_chip(rs, 1); + } else { + /* tx or tr: spi must be enabled first */ + spi_enable_chip(rs, 1); + rockchip_spi_prepare_dma(rs); + } + } else { + spi_enable_chip(rs, 1); ret = rockchip_spi_pio_transfer(rs); + } return ret; } -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] spi/rockchip: fix bug that cause the failture to read data in DMA mode
In my test on RK3288-pinky board, if spi is enabled, it will begin to read data from slave regardless of whether the DMA is ready. So we need prepare DMA before spi is enable. Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/spi/spi-rockchip.c | 15 +++ 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 3afc266..f96ea8a 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -415,7 +415,7 @@ static void rockchip_spi_dma_txcb(void *data) spin_unlock_irqrestore(rs-lock, flags); } -static int rockchip_spi_dma_transfer(struct rockchip_spi *rs) +static void rockchip_spi_prepare_dma(struct rockchip_spi *rs) { unsigned long flags; struct dma_slave_config rxconf, txconf; @@ -474,8 +474,6 @@ static int rockchip_spi_dma_transfer(struct rockchip_spi *rs) dmaengine_submit(txdesc); dma_async_issue_pending(rs-dma_tx.ch); } - - return 1; } static void rockchip_spi_config(struct rockchip_spi *rs) @@ -557,16 +555,17 @@ static int rockchip_spi_transfer_one( else if (rs-rx) rs-tmode = CR0_XFM_RO; - if (master-can_dma master-can_dma(master, spi, xfer)) + /* we need prepare dma before spi was enabled */ + if (master-can_dma master-can_dma(master, spi, xfer)) { rs-use_dma = 1; - else + rockchip_spi_prepare_dma(rs); + } else { rs-use_dma = 0; + } rockchip_spi_config(rs); - if (rs-use_dma) - ret = rockchip_spi_dma_transfer(rs); - else + if (!rs-use_dma) ret = rockchip_spi_pio_transfer(rs); return ret; -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] ARM: dts: Switch i2c0 to 400kHz on rk3288-evb-rk808
On 11 Sep 2014 12:30, Doug Anderson writes: We should be able to talk to the PMIC at 400kHz. No need to talk at the slow 100kHz. As measured by ftrace (with a bunch of extra patches, since cpufreq for rk808 hasn't landed yet): before this change: cpu0_set_target() = ~500us after this change: cpu0_set_target() = ~300us Signed-off-by: Doug Anderson diand...@chromium.org --- arch/arm/boot/dts/rk3288-evb-rk808.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/rk3288-evb-rk808.dts b/arch/arm/boot/dts/rk3288-evb-rk808.dts index 36db177..ff522f8 100644 --- a/arch/arm/boot/dts/rk3288-evb-rk808.dts +++ b/arch/arm/boot/dts/rk3288-evb-rk808.dts @@ -18,6 +18,7 @@ }; i2c0 { + clock-frequency = 40; status = okay; rk808: pmic@1b { I have tested on rk3288-pinky board, it work well. so Reviewed-by Addy Ke addy...@rock-chips.com Tested-by Addy Ke addy...@rock-chips.com -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v3] ARM: dts: Add sdio0 and sdio1 to the rk3288
This patch requires that https://patchwork.kernel.org/patch/4701721/ land in order to compile. Signed-off-by: Addy Ke addy...@rock-chips.com --- Changes in v2: - repost patch to match what's in Heiko's wip/v3.18-next/dts tree for the other dwmmc controllers - add cd and int line, suggested by Doug Anderson - fix up sdio1 configuration error Changes in v3: - sort sdio0 and sdio1 by pin number, suggested by Doug Anderson - add ro and bkpwr line, suggested by Doug Anderson arch/arm/boot/dts/rk3288.dtsi | 102 ++ 1 file changed, 102 insertions(+) diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi index 36be7bb..12c0297 100644 --- a/arch/arm/boot/dts/rk3288.dtsi +++ b/arch/arm/boot/dts/rk3288.dtsi @@ -88,6 +88,26 @@ status = disabled; }; + sdio0: dwmmc@ff0d { + compatible = rockchip,rk3288-dw-mshc; + clocks = cru HCLK_SDIO0, cru SCLK_SDIO0; + clock-names = biu, ciu; + fifo-depth = 0x100; + interrupts = GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH; + reg = 0xff0d 0x4000; + status = disabled; + }; + + sdio1: dwmmc@ff0e { + compatible = rockchip,rk3288-dw-mshc; + clocks = cru HCLK_SDIO1, cru SCLK_SDIO1; + clock-names = biu, ciu; + fifo-depth = 0x100; + interrupts = GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH; + reg = 0xff0e 0x4000; + status = disabled; + }; + emmc: dwmmc@ff0f { compatible = rockchip,rk3288-dw-mshc; clocks = cru HCLK_EMMC, cru SCLK_EMMC; @@ -508,6 +528,88 @@ }; }; + sdio0 { + sdio0_bus1: sdio0-bus1 { + rockchip,pins = 4 20 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_bus4: sdio0-bus4 { + rockchip,pins = 4 20 RK_FUNC_1 pcfg_pull_up, + 4 21 RK_FUNC_1 pcfg_pull_up, + 4 22 RK_FUNC_1 pcfg_pull_up, + 4 23 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_cmd: sdio0-cmd { + rockchip,pins = 4 24 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_clk: sdio0-clk { + rockchip,pins = 4 25 RK_FUNC_1 pcfg_pull_none; + }; + + sdio0_cd: sdio0-cd { + rockchip,pins = 4 26 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_ro: sdio0-ro { + rockchip,pins = 4 27 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_pwr: sdio0-pwr { + rockchip,pins = 4 28 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_bkpwr: sdio0-bkpwr { + rockchip,pins = 4 29 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_int: sdio0-int { + rockchip,pins = 4 30 RK_FUNC_1 pcfg_pull_up; + }; + }; + + sdio1 { + sdio1_bus1: sdio1-bus1 { + rockchip,pins = 3 24 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_bus4: sdio1-bus4 { + rockchip,pins = 3 24 RK_FUNC_4 pcfg_pull_up, + 3 25 RK_FUNC_4 pcfg_pull_up, + 3 26 RK_FUNC_4 pcfg_pull_up, + 3 27 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_cd: sdio1-cd { + rockchip,pins = 3 28 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_ro: sdio1-ro { + rockchip,pins = 3 29 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_bkpwr: sdio1-bkpwr { + rockchip,pins = 3 30 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_int: sdio1-int { + rockchip,pins = 3 31 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_cmd: sdio1-cmd { + rockchip,pins = 4 6 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_clk: sdio1-clk { + rockchip,pins = 4 7 RK_FUNC_4 pcfg_pull_none; + }; + + sdio1_pwr: sdio1-pwr
[PATCH] spi/rockchip: fixup incorrect dma direction setting
Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/spi/spi-rockchip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index cb8fd6f..4ef3fd3 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -679,7 +679,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) rs-dma_tx.addr = (dma_addr_t)(mem-start + ROCKCHIP_SPI_TXDR); rs-dma_rx.addr = (dma_addr_t)(mem-start + ROCKCHIP_SPI_RXDR); rs-dma_tx.direction = DMA_MEM_TO_DEV; - rs-dma_tx.direction = DMA_DEV_TO_MEM; + rs-dma_rx.direction = DMA_DEV_TO_MEM; master-can_dma = rockchip_spi_can_dma; master-dma_tx = rs-dma_tx.ch; -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2] ARM: dts: Add sdio0 and sdio1 to the rk3288
This patch requires that https://patchwork.kernel.org/patch/4701721/ land in order to compile. Signed-off-by: Addy Ke addy...@rock-chips.com --- Changes in v2: - repost patch to match what's in Heiko's wip/v3.18-next/dts tree for the other dwmmc controllers - add cd and int line, suggested by Doug Anderson - fix up sdio1 configuration error arch/arm/boot/dts/rk3288.dtsi | 86 +++ 1 file changed, 86 insertions(+) diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi index 36be7bb..91576ae 100644 --- a/arch/arm/boot/dts/rk3288.dtsi +++ b/arch/arm/boot/dts/rk3288.dtsi @@ -88,6 +88,26 @@ status = disabled; }; + sdio0: dwmmc@ff0d { + compatible = rockchip,rk3288-dw-mshc; + clocks = cru HCLK_SDIO0, cru SCLK_SDIO0; + clock-names = biu, ciu; + fifo-depth = 0x100; + interrupts = GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH; + reg = 0xff0d 0x4000; + status = disabled; + }; + + sdio1: dwmmc@ff0e { + compatible = rockchip,rk3288-dw-mshc; + clocks = cru HCLK_SDIO1, cru SCLK_SDIO1; + clock-names = biu, ciu; + fifo-depth = 0x100; + interrupts = GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH; + reg = 0xff0e 0x4000; + status = disabled; + }; + emmc: dwmmc@ff0f { compatible = rockchip,rk3288-dw-mshc; clocks = cru HCLK_EMMC, cru SCLK_EMMC; @@ -508,6 +528,72 @@ }; }; + sdio0 { + sdio0_clk: sdio0-clk { + rockchip,pins = 4 25 RK_FUNC_1 pcfg_pull_none; + }; + + sdio0_cmd: sdio0-cmd { + rockchip,pins = 4 24 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_cd: sdio0-cd { + rockchip,pins = 4 26 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_pwr: sdio0-pwr { + rockchip,pins = 4 28 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_int: sdio0-int { + rockchip,pins = 4 30 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_bus1: sdio0-bus1 { + rockchip,pins = 4 20 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_bus4: sdio0-bus4 { + rockchip,pins = 4 20 RK_FUNC_1 pcfg_pull_up, + 4 21 RK_FUNC_1 pcfg_pull_up, + 4 22 RK_FUNC_1 pcfg_pull_up, + 4 23 RK_FUNC_1 pcfg_pull_up; + }; + }; + + sdio1 { + sdio1_clk: sdio1-clk { + rockchip,pins = 4 7 RK_FUNC_4 pcfg_pull_none; + }; + + sdio1_cmd: sdio1-cmd { + rockchip,pins = 4 6 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_cd: sdio1-cd { + rockchip,pins = 3 28 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_pwr: sdio1-pwr { + rockchip,pins = 4 9 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_int: sdio1-int { + rockchip,pins = 3 31 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_bus1: sdio1-bus1 { + rockchip,pins = 3 24 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_bus4: sdio1-bus4 { + rockchip,pins = 3 24 RK_FUNC_4 pcfg_pull_up, + 3 25 RK_FUNC_4 pcfg_pull_up, + 3 26 RK_FUNC_4 pcfg_pull_up, + 3 27 RK_FUNC_4 pcfg_pull_up; + }; + }; + emmc { emmc_clk: emmc-clk { rockchip,pins = 3 18 RK_FUNC_2 pcfg_pull_none; -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2] mmc: dw_mmc: move rockchip related code to a separate file
To support HS200 and UHS-1, we need add a big hunk of code, as shown in the following patches. So a separate file for rockchip SOCs is suitable. Signed-off-by: Addy Ke addy...@rock-chips.com --- Changes in v2: - Kconfig: depend on ARCH_ROCKCHIP, suggested by Bartlomiej Zolnierkiewicz - Kconfig: depend on OF, suggested by Doug Anderson - Not change suspend/resume code, suggested by Doug Anderson - If pdev-dev.of_node is NULL, then return -ENODEV, suggested by Heiko Stübner drivers/mmc/host/Kconfig | 9 +++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/dw_mmc-pltfm.c| 57 drivers/mmc/host/dw_mmc-rockchip.c | 136 + 4 files changed, 146 insertions(+), 57 deletions(-) create mode 100644 drivers/mmc/host/dw_mmc-rockchip.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index a565254..f6095f6 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -621,6 +621,15 @@ config MMC_DW_PCI If unsure, say N. +config MMC_DW_ROCKCHIP + tristate Rockchip specific extensions for Synopsys DW Memory Card Interface + depends on MMC_DW ARCH_ROCKCHIP OF + select MMC_DW_PLTFM + help + This selects support for Rockchip SoC specific extensions to the + Synopsys DesignWare Memory Card Interface driver. Select this option + for platforms based on RK3066, RK3188 and RK3288 SoC's. + config MMC_SH_MMCIF tristate SuperH Internal MMCIF support depends on MMC_BLOCK diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 7f81ddf..5fce465 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_MMC_DW_PLTFM)+= dw_mmc-pltfm.o obj-$(CONFIG_MMC_DW_EXYNOS)+= dw_mmc-exynos.o obj-$(CONFIG_MMC_DW_K3)+= dw_mmc-k3.o obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o +obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_VUB300) += vub300.o diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index b547f7a..0c56c41 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -26,64 +26,11 @@ #include dw_mmc.h #include dw_mmc-pltfm.h -#define RK3288_CLKGEN_DIV 2 - static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr) { *cmdr |= SDMMC_CMD_USE_HOLD_REG; } -static int dw_mci_rk3288_setup_clock(struct dw_mci *host) -{ - host-bus_hz /= RK3288_CLKGEN_DIV; - - return 0; -} - -static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) -{ - int ret; - unsigned int cclkin; - u32 bus_hz; - - /* -* cclkin: source clock of mmc controller. -* bus_hz: card interface clock generated by CLKGEN. -* bus_hz = cclkin / RK3288_CLKGEN_DIV; -* ios-clock = (div == 0) ? bus_hz : (bus_hz / (2 * div)) -* -* Note: div can only be 0 or 1 -* if DDR50 8bit mode(only emmc work in 8bit mode), -* div must be set 1 -*/ - if ((ios-bus_width == MMC_BUS_WIDTH_8) - (ios-timing == MMC_TIMING_MMC_DDR52)) - cclkin = 2 * ios-clock * RK3288_CLKGEN_DIV; - else - cclkin = ios-clock * RK3288_CLKGEN_DIV; - - ret = clk_set_rate(host-ciu_clk, cclkin); - if (ret) - dev_warn(host-dev, failed to set rate %uHz\n, ios-clock); - - bus_hz = clk_get_rate(host-ciu_clk) / RK3288_CLKGEN_DIV; - if (bus_hz != host-bus_hz) { - host-bus_hz = bus_hz; - /* force dw_mci_setup_bus() */ - host-current_speed = 0; - } -} - -static const struct dw_mci_drv_data rk2928_drv_data = { - .prepare_command= dw_mci_pltfm_prepare_command, -}; - -static const struct dw_mci_drv_data rk3288_drv_data = { - .prepare_command= dw_mci_pltfm_prepare_command, - .set_ios= dw_mci_rk3288_set_ios, - .setup_clock= dw_mci_rk3288_setup_clock, -}; - static const struct dw_mci_drv_data socfpga_drv_data = { .prepare_command= dw_mci_pltfm_prepare_command, }; @@ -144,10 +91,6 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); static const struct of_device_id dw_mci_pltfm_match[] = { { .compatible = snps,dw-mshc, }, - { .compatible = rockchip,rk2928-dw-mshc, - .data = rk2928_drv_data }, - { .compatible = rockchip,rk3288-dw-mshc, - .data = rk3288_drv_data }, { .compatible = altr,socfpga-dw-mshc, .data = socfpga_drv_data }, {}, diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c new file mode 100644 index 000..f0c2cb1 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2014
[PATCH] mmc: dw_mmc: move rockchip related code to a separate file
To support HS200 and UHS-1, we need add a big hunk of code, as shown in the following patches. So a separate file for rockchip SOCs is suitable. Signed-off-by: Addy Ke addy...@rock-chips.com --- drivers/mmc/host/Kconfig | 9 +++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/dw_mmc-pltfm.c| 57 --- drivers/mmc/host/dw_mmc-rockchip.c | 146 + 4 files changed, 156 insertions(+), 57 deletions(-) create mode 100644 drivers/mmc/host/dw_mmc-rockchip.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index a565254..ae61df6 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -621,6 +621,15 @@ config MMC_DW_PCI If unsure, say N. +config MMC_DW_ROCKCHIP + tristate Rockchip specific extensions for Synopsys DW Memory Card Interface + depends on MMC_DW + select MMC_DW_PLTFM + help + This selects support for Rockchip SoC specific extensions to the + Synopsys DesignWare Memory Card Interface driver. Select this option + for platforms based on RK3066, RK3188 and RK3288 SoC's. + config MMC_SH_MMCIF tristate SuperH Internal MMCIF support depends on MMC_BLOCK diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 7f81ddf..5fce465 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_MMC_DW_PLTFM)+= dw_mmc-pltfm.o obj-$(CONFIG_MMC_DW_EXYNOS)+= dw_mmc-exynos.o obj-$(CONFIG_MMC_DW_K3)+= dw_mmc-k3.o obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o +obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_VUB300) += vub300.o diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index b547f7a..0c56c41 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -26,64 +26,11 @@ #include dw_mmc.h #include dw_mmc-pltfm.h -#define RK3288_CLKGEN_DIV 2 - static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr) { *cmdr |= SDMMC_CMD_USE_HOLD_REG; } -static int dw_mci_rk3288_setup_clock(struct dw_mci *host) -{ - host-bus_hz /= RK3288_CLKGEN_DIV; - - return 0; -} - -static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) -{ - int ret; - unsigned int cclkin; - u32 bus_hz; - - /* -* cclkin: source clock of mmc controller. -* bus_hz: card interface clock generated by CLKGEN. -* bus_hz = cclkin / RK3288_CLKGEN_DIV; -* ios-clock = (div == 0) ? bus_hz : (bus_hz / (2 * div)) -* -* Note: div can only be 0 or 1 -* if DDR50 8bit mode(only emmc work in 8bit mode), -* div must be set 1 -*/ - if ((ios-bus_width == MMC_BUS_WIDTH_8) - (ios-timing == MMC_TIMING_MMC_DDR52)) - cclkin = 2 * ios-clock * RK3288_CLKGEN_DIV; - else - cclkin = ios-clock * RK3288_CLKGEN_DIV; - - ret = clk_set_rate(host-ciu_clk, cclkin); - if (ret) - dev_warn(host-dev, failed to set rate %uHz\n, ios-clock); - - bus_hz = clk_get_rate(host-ciu_clk) / RK3288_CLKGEN_DIV; - if (bus_hz != host-bus_hz) { - host-bus_hz = bus_hz; - /* force dw_mci_setup_bus() */ - host-current_speed = 0; - } -} - -static const struct dw_mci_drv_data rk2928_drv_data = { - .prepare_command= dw_mci_pltfm_prepare_command, -}; - -static const struct dw_mci_drv_data rk3288_drv_data = { - .prepare_command= dw_mci_pltfm_prepare_command, - .set_ios= dw_mci_rk3288_set_ios, - .setup_clock= dw_mci_rk3288_setup_clock, -}; - static const struct dw_mci_drv_data socfpga_drv_data = { .prepare_command= dw_mci_pltfm_prepare_command, }; @@ -144,10 +91,6 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); static const struct of_device_id dw_mci_pltfm_match[] = { { .compatible = snps,dw-mshc, }, - { .compatible = rockchip,rk2928-dw-mshc, - .data = rk2928_drv_data }, - { .compatible = rockchip,rk3288-dw-mshc, - .data = rk3288_drv_data }, { .compatible = altr,socfpga-dw-mshc, .data = socfpga_drv_data }, {}, diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c new file mode 100644 index 000..3d86ef3 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd + * Author: addy ke addy...@rock-chips.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License
Re: [PATCH] ARM: dts: Add mmc0 and mmc1 aliases for rk3288
It's convenient (and less confusing to people reading logs) if the eMMC port on rk3288 is consistenly marked with mmc0 and the sdmmc port on rk3288 is consistently marked with mmc1. Add the appropriate aliases. These aliases only actually do something if a patch like (https://patchwork.kernel.org/patch/3925551/) lands, but they don't hurt even before that patch. Signed-off-by: Doug Anderson diand...@chromium.org Reviewed-by: Sonny Rao sonny...@chromium.org --- arch/arm/boot/dts/rk3288.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi index 36be7bb..0b54b0d 100644 --- a/arch/arm/boot/dts/rk3288.dtsi +++ b/arch/arm/boot/dts/rk3288.dtsi @@ -29,6 +29,8 @@ i2c3 = i2c3; i2c4 = i2c4; i2c5 = i2c5; + mmc0 = emmc; + mmc1 = sdmmc; There are 8 registers can be configured for clock tunning(see chapter 3, page 133): sdmmc: CRU_SDMMC_CON0(offset: 0x200) CRU_SDMMC_CON1(offset: 0x204) sdio0: CRU_SDMMC_CON2(offset: 0x208) CRU_SDMMC_CON3(offset: 0x20c) sdio1: CRU_SDMMC_CON4(offset: 0x210) CRU_SDMMC_CON5(offset: 0x214) emmc: CRU_SDMMC_CON6(offset: 0x218) CRU_SDMMC_CON7(offset: 0x21c) I think maybe it is suitable as follows: mmc0 = sdmmc mmc1 = sdio0 mmc2 = sdio1 mmc3 = emmc So we can get ctrl_id: ctrl_id = of_alias_get_id(host-dev-of_node, mshc); and can get offset of registers: offset = 0x200 + ctrl_id * 8 + 4 * drive_or_sample serial0 = uart0; serial1 = uart1; serial2 = uart2; -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] ARM: dts: Add mmc0 and mmc1 aliases for rk3288
Addy, On Wed, Aug 13, 2014 at 6:57 PM, Addy addy...@rock-chips.com wrote: I think maybe it is suitable as follows: mmc0 = sdmmc mmc1 = sdio0 mmc2 = sdio1 mmc3 = emmc Right, except the only ones that have landed in Heiko's tree are sdmmc and emmc, so we can't do sdio0 and sdio1 yet. You could post support for sdio0 and adio1? yes, I will post it today. Also: it's really handy if emmc is 0. See below: I don't think it's great to use the ID to find the sysconfig registers. So we can get ctrl_id: ctrl_id = of_alias_get_id(host-dev-of_node, mshc); Somehow I hadn't realized that was there. I guess we could use that too. I'd vote to remove that and use the standard mmc numbering (and get some momentum to land those patches). If you want I'll repost using the mshc stuff, though. and can get offset of registers: offset = 0x200 + ctrl_id * 8 + 4 * drive_or_sample I thought the plan was to actually implement the phase stuff as a clock driver. ...even if we didn't, I'd rather not rely on ID like this to find the right address. It's really non-obvious. -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] ARM: dts: Add sdio0 and sdio1 to the rk3288
Signed-off-by: Addy Ke addy...@rock-chips.com --- arch/arm/boot/dts/rk3288.dtsi | 76 +++ 1 file changed, 76 insertions(+) diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi index 7a9173d..a440869 100644 --- a/arch/arm/boot/dts/rk3288.dtsi +++ b/arch/arm/boot/dts/rk3288.dtsi @@ -120,6 +120,32 @@ //fifo-depth = 0x100; }; + sdio0: dwmmc@ff0d { + compatible = rockchip,rk3288-dw-mshc; + reg = 0xff0d 0x4000; + interrupts = GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH; + #address-cells = 1; + #size-cells = 0; + + clocks = cru HCLK_SDIO0, cru SCLK_SDIO0; + clock-names = biu, ciu; + + fifo-depth = 0x100; + }; + + sdio1: dwmmc@ff0e { + compatible = rockchip,rk3288-dw-mshc; + reg = 0xff0e 0x4000; + interrupts = GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH; + #address-cells = 1; + #size-cells = 0; + + clocks = cru HCLK_SDIO1, cru SCLK_SDIO1; + clock-names = biu, ciu; + + fifo-depth = 0x100; + }; + emmc: dwmmc@ff0f { compatible = rockchip,rk3288-dw-mshc; reg = 0xff0f 0x4000; @@ -589,6 +615,56 @@ }; }; + sdio0 { + sdio0_clk: sdio0-clk { + rockchip,pins = 4 25 RK_FUNC_1 pcfg_pull_none; + }; + + sdio0_cmd: sdio0-cmd { + rockchip,pins = 4 24 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_pwr: sdio0-pwr { + rockchip,pins = 4 28 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_bus1: sdio0-bus1 { + rockchip,pins = 4 20 RK_FUNC_1 pcfg_pull_up; + }; + + sdio0_bus4: sdio0-bus4 { + rockchip,pins = 4 20 RK_FUNC_1 pcfg_pull_up, + 4 21 RK_FUNC_1 pcfg_pull_up, + 4 22 RK_FUNC_1 pcfg_pull_up, + 4 23 RK_FUNC_1 pcfg_pull_up; + }; + }; + + sdio1 { + sdio1_clk: sdio1-clk { + rockchip,pins = 4 7 RK_FUNC_4 pcfg_pull_none; + }; + + sdio1_cmd: sdio1-cmd { + rockchip,pins = 4 6 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_pwr: sdio1-pwr { + rockchip,pins = 4 9 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_bus1: sdio1-bus1 { + rockchip,pins = 4 24 RK_FUNC_4 pcfg_pull_up; + }; + + sdio1_bus4: sdio1-bus4 { + rockchip,pins = 3 24 RK_FUNC_4 pcfg_pull_up, + 3 25 RK_FUNC_4 pcfg_pull_up, + 3 26 RK_FUNC_4 pcfg_pull_up, + 3 27 RK_FUNC_4 pcfg_pull_up; + }; + }; + emmc { emmc_clk: emmc-clk { rockchip,pins = 3 18 RK_FUNC_2 pcfg_pull_none; -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 0/4] patches for rockchip spi driver
From: Addy Ke addy...@rockchip.com These patches based on: - git: kernel/git/broonie/spi.git - branch: topic/rockchip - commit: c15369087ae5c7db7f3e3604822eac6ab87429bd Addy Ke (4): spi/rockchip: cleanup some coding issues and uncessary output spi/rockchip: call wait_for_idle() for the transfer to complete spi/rockchip: master-mode_bits: remove SPI_CS_HIGH bit spi/rockchip: add compatible strings for RK3188 and RK3288 drivers/spi/spi-rockchip.c | 74 -- 1 file changed, 45 insertions(+), 29 deletions(-) -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 1/4] spi/rockchip: cleanup some coding issues and uncessary output
From: Addy Ke addy...@rockchip.com Suggested-by: Mark Brown broo...@kernel.org Signed-off-by: Addy Ke addy...@rockchip.com --- drivers/spi/spi-rockchip.c | 41 +++-- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 72fb287..8c24708 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd - * Author: addy ke addy...@rock-chips.com + * Author: Addy Ke addy...@rock-chips.com * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -186,7 +186,7 @@ struct rockchip_spi { void *rx_end; u32 state; - + /* protect state */ spinlock_t lock; struct completion xfer_completion; @@ -278,7 +278,7 @@ static void rockchip_spi_set_cs(struct spi_device *spi, bool enable) } static int rockchip_spi_prepare_message(struct spi_master *master, - struct spi_message *msg) + struct spi_message *msg) { struct rockchip_spi *rs = spi_master_get_devdata(master); struct spi_device *spi = msg-spi; @@ -294,13 +294,19 @@ static int rockchip_spi_prepare_message(struct spi_master *master, } static int rockchip_spi_unprepare_message(struct spi_master *master, - struct spi_message *msg) + struct spi_message *msg) { unsigned long flags; struct rockchip_spi *rs = spi_master_get_devdata(master); spin_lock_irqsave(rs-lock, flags); + /* +* For DMA mode, we need terminate DMA channel and flush +* fifo for the next transfer if DMA thansfer timeout. +* unprepare_message() was called by core if transfer complete +* or timeout. Maybe it is reasonable for error handling here. +*/ if (rs-use_dma) { if (rs-state RXBUSY) { dmaengine_terminate_all(rs-dma_rx.ch); @@ -344,7 +350,7 @@ static void rockchip_spi_pio_reader(struct rockchip_spi *rs) else *(u16 *)(rs-rx) = (u16)rxw; rs-rx += rs-n_bytes; - }; + } } static int rockchip_spi_pio_transfer(struct rockchip_spi *rs) @@ -414,7 +420,8 @@ static int rockchip_spi_dma_transfer(struct rockchip_spi *rs) rxconf.src_maxburst = rs-n_bytes; dmaengine_slave_config(rs-dma_rx.ch, rxconf); - rxdesc = dmaengine_prep_slave_sg(rs-dma_rx.ch, + rxdesc = dmaengine_prep_slave_sg( + rs-dma_rx.ch, rs-rx_sg.sgl, rs-rx_sg.nents, rs-dma_rx.direction, DMA_PREP_INTERRUPT); @@ -429,7 +436,8 @@ static int rockchip_spi_dma_transfer(struct rockchip_spi *rs) txconf.dst_maxburst = rs-n_bytes; dmaengine_slave_config(rs-dma_tx.ch, txconf); - txdesc = dmaengine_prep_slave_sg(rs-dma_tx.ch, + txdesc = dmaengine_prep_slave_sg( + rs-dma_tx.ch, rs-tx_sg.sgl, rs-tx_sg.nents, rs-dma_tx.direction, DMA_PREP_INTERRUPT); @@ -495,13 +503,13 @@ static void rockchip_spi_config(struct rockchip_spi *rs) spi_set_clk(rs, div); - dev_dbg(rs-dev, cr0 0x%x, div %d\n, - cr0, div); + dev_dbg(rs-dev, cr0 0x%x, div %d\n, cr0, div); spi_enable_chip(rs, 1); } -static int rockchip_spi_transfer_one(struct spi_master *master, +static int rockchip_spi_transfer_one( + struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { @@ -556,8 +564,8 @@ static int rockchip_spi_transfer_one(struct spi_master *master, } static bool rockchip_spi_can_dma(struct spi_master *master, - struct spi_device *spi, - struct spi_transfer *xfer) +struct spi_device *spi, +struct spi_transfer *xfer) { struct rockchip_spi *rs = spi_master_get_devdata(master); @@ -572,10 +580,9 @@ static int rockchip_spi_probe(struct platform_device *pdev) struct resource *mem; master = spi_alloc_master(pdev-dev, sizeof(struct rockchip_spi)); - if (!master) { - dev_err(pdev-dev, No memory for spi_master\n); + if (!master) return -ENOMEM; - } + platform_set_drvdata(pdev, master); rs = spi_master_get_devdata(master); @@ -676,8 +683,6 @@ static int rockchip_spi_probe(struct platform_device *pdev) goto err_register_master; } - dev_info(pdev-dev, Rockchip SPI controller initialized\n); - return
[PATCH 3/4] spi/rockchip: master-mode_bits: remove SPI_CS_HIGH bit
From: Addy Ke addy...@rockchip.com Suggested-by: Jonas Gorski j...@openwrt.org Signed-off-by: Addy Ke addy...@rockchip.com --- drivers/spi/spi-rockchip.c | 7 +-- 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 09c690c..a8866c9 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -295,11 +295,6 @@ static int rockchip_spi_prepare_message(struct spi_master *master, struct rockchip_spi *rs = spi_master_get_devdata(master); struct spi_device *spi = msg-spi; - if (spi-mode SPI_CS_HIGH) { - dev_err(rs-dev, spi_cs_hign: not support\n); - return -EINVAL; - } - rs-mode = spi-mode; return 0; @@ -657,7 +652,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) master-auto_runtime_pm = true; master-bus_num = pdev-id; - master-mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; + master-mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; master-num_chipselect = 2; master-dev.of_node = pdev-dev.of_node; master-bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8); -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 4/4] spi/rockchip: add compatible strings for RK3188 and RK3288
From: Addy Ke addy...@rockchip.com Suggested-by: Mark Brown broo...@kernel.org Signed-off-by: Addy Ke addy...@rockchip.com --- drivers/spi/spi-rockchip.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index a8866c9..cb8fd6f 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -814,6 +814,8 @@ static const struct dev_pm_ops rockchip_spi_pm = { static const struct of_device_id rockchip_spi_dt_match[] = { { .compatible = rockchip,rk3066-spi, }, + { .compatible = rockchip,rk3188-spi, }, + { .compatible = rockchip,rk3288-spi, }, { }, }; MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match); -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2] mmc: dw_mmc: add support for RK3288
This patch focuses on clock setting for RK3288 mmc controller. In RK3288 mmc controller, CLKDIV register can only be set 0 or 1, and if DDR 8bit mode, CLKDIV register must be set 1. Signed-off-by: Addy Ke addy...@rock-chips.com --- changes since v1: - dw_mci_rk3288_setup_clock: do not call clk_get_rate(), just use the host-bus_hz which is already called by dw_mmc.c, suggested by Jaehoon Chung .../devicetree/bindings/mmc/rockchip-dw-mshc.txt | 4 +- drivers/mmc/host/dw_mmc-pltfm.c| 50 +- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt index c559f3f..e3f95cd 100644 --- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt @@ -10,7 +10,9 @@ extensions to the Synopsys Designware Mobile Storage Host Controller. Required Properties: * compatible: should be - - rockchip,rk2928-dw-mshc: for Rockchip RK2928 and following + - rockchip,rk2928-dw-mshc: for Rockchip RK2928 and following, + before RK3288 + - rockchip,rk3288-dw-mshc: for Rockchip RK3288 Example: diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index d4a47a9..809c28b 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -21,17 +21,61 @@ #include linux/mmc/mmc.h #include linux/mmc/dw_mmc.h #include linux/of.h +#include linux/clk.h #include dw_mmc.h #include dw_mmc-pltfm.h +#define RK3288_CLKGEN_DIV 2 + static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr) { *cmdr |= SDMMC_CMD_USE_HOLD_REG; } -static const struct dw_mci_drv_data rockchip_drv_data = { +static int dw_mci_rk3288_setup_clock(struct dw_mci *host) +{ + host-bus_hz /= RK3288_CLKGEN_DIV; + + return 0; +} + +static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + int ret; + unsigned int cclkin; + + /* +* cclkin: source clock of mmc controller. +* bus_hz: card interface clock generated by CLKGEN. +* bus_hz = cclkin / RK3288_CLKGEN_DIV; +* ios-clock = (div == 0) ? bus_hz : (bus_hz / (2 * div)) +* +* Note: div can only be 0 or 1 +* if DDR50 8bit mode, div must be set 1 +*/ + if ((ios-bus_width == MMC_BUS_WIDTH_8) + (ios-timing == MMC_TIMING_UHS_DDR50 || +ios-timing == MMC_TIMING_MMC_DDR52)) + cclkin = 2 * ios-clock * RK3288_CLKGEN_DIV; + else + cclkin = ios-clock * RK3288_CLKGEN_DIV; + + ret = clk_set_rate(host-ciu_clk, cclkin); + if (ret) + dev_warn(host-dev, failed to set rate %uHz\n, ios-clock); + + host-bus_hz = clk_get_rate(host-ciu_clk) / RK3288_CLKGEN_DIV; +} + +static const struct dw_mci_drv_data rk2928_drv_data = { + .prepare_command= dw_mci_pltfm_prepare_command, +}; + +static const struct dw_mci_drv_data rk3288_drv_data = { .prepare_command= dw_mci_pltfm_prepare_command, + .set_ios= dw_mci_rk3288_set_ios, + .setup_clock= dw_mci_rk3288_setup_clock, }; static const struct dw_mci_drv_data socfpga_drv_data = { @@ -95,7 +139,9 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); static const struct of_device_id dw_mci_pltfm_match[] = { { .compatible = snps,dw-mshc, }, { .compatible = rockchip,rk2928-dw-mshc, - .data = rockchip_drv_data }, + .data = rk2928_drv_data }, + { .compatible = rockchip,rk3288-dw-mshc, + .data = rk3288_drv_data }, { .compatible = altr,socfpga-dw-mshc, .data = socfpga_drv_data }, {}, -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v3 2/2] spi: add driver for Rockchip RK3xxx SoCs integrated SPI
In order to facilitate understanding, rockchip SPI controller IP design looks similar in its registers to designware. But IC implementation is different from designware, So we need a dedicated driver for Rockchip RK3XXX SoCs integrated SPI. The main differences: - dma request line: rockchip SPI controller have two DMA request line for tx and rx. - Register offset: RK3288DW SPI_CTRLR0 0x0x SPI_CTRLR1 0x00040x0004 SPI_SSIENR 0x00080x0008 SPI_MWCRNONE 0x000c SPI_SER 0x000c0x0010 SPI_BAUDR 0x00100x0014 SPI_TXFTLR 0x00140x0018 SPI_RXFTLR 0x00180x001c SPI_TXFLR 0x001c0x0020 SPI_RXFLR 0x00200x0024 SPI_SR 0x00240x0028 SPI_IPR 0x0028NONE SPI_IMR 0x002c0x002c SPI_ISR 0x00300x0030 SPI_RISR0x00340x0034 SPI_TXOICR NONE 0x0038 SPI_RXOICR NONE 0x003c SPI_RXUICR NONE 0x0040 SPI_MSTICR NONE 0x0044 SPI_ICR 0x00380x0048 SPI_DMACR 0x003c0x004c SPI_DMATDLR 0x00400x0050 SPI_DMARDLR 0x00440x0054 SPI_TXDR0x0400NONE SPI_RXDR0x0800NONE SPI_IDR NONE 0x0058 SPI_VERSION NONE 0x005c SPI_DR NONE 0x0060 - register configuration: RK3288 DW - SPI_CTROL0: control frame sizebit[5:2]bit[15:12] shift register loop NONEbit[11] transfer mode bit[19:18] bit[9:8] frame format bit[17:16] bit[5:4] data frame size bit[1:0]bit[3:0] For more information, see RK3288 chip manual. - Wait for idle: Must ensure that the FIFO data has been sent out before the next transfer. Signed-off-by: Addy Ke addy...@rock-chips.com --- changes since v1: - more specific about the differences according to comments from Mark Brown - not to cast away const according to comments from Mark Brown - add set_cs() operation provided to the core - change rockchip_spi_hw_init() to get_fifo_len() - remove spi_setup() and spi_clean() operations - remove wait_for_dma(), replaced by calling spi_finalize_current_transfer() when done - remove wait_for_not_busy(), replaced by setting xfer-delay_usecs - remove interrupt handler - call clk_disable_unprepare() if spi_master_resume() failed changes since v2: - Kconfig: add depends on (ARCH_ROCKCHIP || COMPILE_TEST) for SPI_ROCKCHIP - use wait_for_idle and do not set xfer-delay_uses suggested by Mark Brown - master-mode_bits: remove SPI_CS_HIGH bit, according to comments from Jonas Gorski - remove some log information suggested by Mark Brown - remove Unneeded semicolon - add compatible strings for RK3188 and RK3288 - improve differences between RK3288 and DW, provided by IC engineer. - fixed some warning checked by ./scripts/checkpatch.pl --strict drivers/spi/Kconfig| 12 + drivers/spi/Makefile | 1 + drivers/spi/spi-rockchip.c | 837 + 3 files changed, 850 insertions(+) create mode 100644 drivers/spi/spi-rockchip.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 60f2b41..c4e307b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -375,6 +375,18 @@ config SPI_PXA2XX config SPI_PXA2XX_PCI def_tristate SPI_PXA2XX PCI +config SPI_ROCKCHIP + tristate Rockchip SPI controller driver + depends on (ARCH_ROCKCHIP || COMPILE_TEST) + help + This selects a driver for Rockchip SPI controller. + + If you say yes to this option, support will be included for + RK3066, RK3188 and RK3288 families of SPI controller. + Rockchip SPI controller support DMA transport and PIO mode. + The main usecase of this controller is to use spi flash as boot + device. + config SPI_RSPI tristate Renesas RSPI/QSPI controller depends on (SUPERH SH_DMAE_BASE) || ARCH_SHMOBILE diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index bd79266..361fbf3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -60,6 +60,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o obj-$(CONFIG_SPI_QUP) += spi-qup.o +obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o spi-s3c24xx-hw-y := spi-s3c24xx.o diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c new file mode 100644 index 000..488a7b8 --- /dev/null +++ b/drivers/spi/spi-rockchip.c @@ -0,0 +1,837 @@ +/* + * Copyright (c
Re: [PATCH v2 2/2] spi: add driver for Rockchip RK3xxx SoCs integrated SPI
On Tue, Jul 01, 2014 at 09:03:59AM +0800, addy ke wrote: In order to facilitate understanding, rockchip SPI controller IP design looks similar in its registers to designware. But IC implementation is different from designware, So we need a dedicated driver for Rockchip RK3XXX SoCs integrated SPI. The main differences: This looks good overall, a nice clean driver. I've applied it but there are a few small issues that need fixing up which I've noted below, can you please send followup patches dealing with these? + * static void spi_set_cs(struct spi_device *spi, bool enable) + * { + * if (spi-mode SPI_CS_HIGH) + * enable = !enable; + * + * if (spi-cs_gpio = 0) + * gpio_set_value(spi-cs_gpio, !enable); + * else if (spi-master-set_cs) + * spi-master-set_cs(spi, !enable); + * } + * + * Note: enable(rockchip_spi_set_cs) = !enable(spi_set_cs) + */ So, the point here is that chip select is an active low signal by default which means that if chip select is asserted we have a low logic level and the parameter means asserted not logic level for the output. It doesn't really matter but it might be clearer to say so directly. +if (spi-mode SPI_CS_HIGH) { +dev_err(rs-dev, spi_cs_hign: not support\n); +return -EINVAL; Typo here (high). +static int rockchip_spi_unprepare_message(struct spi_master *master, +struct spi_message *msg) +{ +unsigned long flags; +struct rockchip_spi *rs = spi_master_get_devdata(master); + +spin_lock_irqsave(rs-lock, flags); + +if (rs-use_dma) { +if (rs-state RXBUSY) { +dmaengine_terminate_all(rs-dma_rx.ch); +flush_fifo(rs); +} + +if (rs-state TXBUSY) +dmaengine_terminate_all(rs-dma_tx.ch); +} This initially looks wrong - the DMA should all be quiesced by the time that we get to unpreparing the hardware, otherwise the transfer might be ongoing while the chip select is deasserted. However this is really just error handling in case something went wrong which is sensible and reasonable, a comment explaining this would help so can you please send a followup patch adding one. The error handling here is actually a good point - we should probably add a callback for the core to use when it times out since the issue also applies if there are further transactions queued with the hardware. I'll look into that later unless someone does it first. +/* Delay until the FIFO data completely */ +if (xfer-tx_buf) +xfer-delay_usecs += rs-fifo_len * rs-bpw * 100 / rs-speed; The driver shouldn't be doing this, if it needs a delay it needs to implement it itself. delay_usecs can be set by devices if they need a delay between transfers, it should be in addition to the time taken for the transfer to complete. Please send a followup patch fixing this. Are the following modifications reasonable? +static inline void wait_for_idle(struct rockchip_spi *rs) +{ +unsigned long timeout = jiffies + msecs_to_jiffies(5); + +while (time_before(jiffies, timeout)) { +if (!(readl_relaxed(rs-regs + ROCKCHIP_SPI_SR) SR_BUSY)) +return; +} + +dev_warn(rs-dev, spi controller is in busy state!\n); +} static int rockchip_spi_pio_transfer(struct rockchip_spi *rs) { int remain = 0; do { if (rs-tx) { remain = rs-tx_end - rs-tx; rockchip_spi_pio_writer(rs); } if (rs-rx) { remain = rs-rx_end - rs-rx; rockchip_spi_pio_reader(rs); } cpu_relax(); } while (remain); +/* If tx, wait until the FIFO data completely. */ +if (rs-tx) +wait_for_idle(rs); return 0; } static void rockchip_spi_dma_txcb(void *data) { unsigned long flags; struct rockchip_spi *rs = data; +/* Wait until the FIFO data completely. */ +wait_for_idle(rs); spin_lock_irqsave(rs-lock, flags); rs-state = ~TXBUSY; if (!(rs-state RXBUSY)) spi_finalize_current_transfer(rs-master); spin_unlock_irqrestore(rs-lock, flags); } +static bool rockchip_spi_can_dma(struct spi_master *master, +struct spi_device *spi, +struct spi_transfer *xfer) +{ +struct rockchip_spi *rs = spi_master_get_devdata(master); + +return (xfer-len rs-fifo_len); +} We should factor this out into the core as well, just let the driver set the minimum size for DMA since it's such a common pattern. I'll look into this as well. +master
Re: [PATCH] mmc: dw_mmc: add support for RK3288.
Hi, Addy, On 07/05/2014 09:59 PM, addy ke wrote: This patch focuses on clock setting for RK3288 mmc controller. In RK3288 mmc controller, CLKDIV register can only be set 0 or 1, and if DDR 8bit mode, CLKDIV register must be set 1. Signed-off-by: addy ke addy...@rock-chips.com --- .../devicetree/bindings/mmc/rockchip-dw-mshc.txt | 4 +- drivers/mmc/host/dw_mmc-pltfm.c| 50 +- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt index c559f3f..e3f95cd 100644 --- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt @@ -10,7 +10,9 @@ extensions to the Synopsys Designware Mobile Storage Host Controller. Required Properties: * compatible: should be -- rockchip,rk2928-dw-mshc: for Rockchip RK2928 and following +- rockchip,rk2928-dw-mshc: for Rockchip RK2928 and following, +before RK3288 +- rockchip,rk3288-dw-mshc: for Rockchip RK3288 Example: diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index d4a47a9..15d796e 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -21,17 +21,61 @@ #include linux/mmc/mmc.h #include linux/mmc/dw_mmc.h #include linux/of.h +#include linux/clk.h #include dw_mmc.h #include dw_mmc-pltfm.h +#define RK3288_CLKGEN_DIV 2 2 is used to the general div value at rockchip? Yes, In RK3288, the div generated by CLKGEN is 2 and can not be changed by software. + static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr) { *cmdr |= SDMMC_CMD_USE_HOLD_REG; } -static const struct dw_mci_drv_data rockchip_drv_data = { +static int dw_mci_rk3288_setup_clock(struct dw_mci *host) +{ +host-bus_hz = clk_get_rate(host-ciu_clk) / RK3288_CLKGEN_DIV; I knew that you need not to call clk_get_rate(). In dw-mmc.c, it's already called. So you can just use the host-bus_hz. host-bus_hz /= RK3288_CLKGEN_DIV; Best Regards, Jaehoon Chung + +return 0; +} + +static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ +int ret; +unsigned int cclkin; + +/* + * cclkin: source clock of mmc controller. + * bus_hz: card interface clock generated by CLKGEN. + * bus_hz = cclkin / RK3288_CLKGEN_DIV; + * ios-clock = (div == 0) ? bus_hz : (bus_hz / (2 * div)) + * + * Note: div can only be 0 or 1 + * if DDR50 8bit mode, div must be set 1 + */ +if ((ios-bus_width == MMC_BUS_WIDTH_8) +(ios-timing == MMC_TIMING_UHS_DDR50 || + ios-timing == MMC_TIMING_MMC_DDR52)) +cclkin = 2 * ios-clock * RK3288_CLKGEN_DIV; +else +cclkin = ios-clock * RK3288_CLKGEN_DIV; + +ret = clk_set_rate(host-ciu_clk, cclkin); +if (ret) +dev_warn(host-dev, failed to set rate %uHz\n, ios-clock); + +host-bus_hz = clk_get_rate(host-ciu_clk) / RK3288_CLKGEN_DIV; +} + +static const struct dw_mci_drv_data rk2928_drv_data = { +.prepare_command= dw_mci_pltfm_prepare_command, +}; + +static const struct dw_mci_drv_data rk3288_drv_data = { .prepare_command= dw_mci_pltfm_prepare_command, +.set_ios= dw_mci_rk3288_set_ios, +.setup_clock= dw_mci_rk3288_setup_clock, }; static const struct dw_mci_drv_data socfpga_drv_data = { @@ -95,7 +139,9 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); static const struct of_device_id dw_mci_pltfm_match[] = { { .compatible = snps,dw-mshc, }, { .compatible = rockchip,rk2928-dw-mshc, -.data = rockchip_drv_data }, +.data = rk2928_drv_data }, +{ .compatible = rockchip,rk3288-dw-mshc, +.data = rk3288_drv_data }, { .compatible = altr,socfpga-dw-mshc, .data = socfpga_drv_data }, {}, -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] mmc: dw_mmc: add support for RK3288.
This patch focuses on clock setting for RK3288 mmc controller. In RK3288 mmc controller, CLKDIV register can only be set 0 or 1, and if DDR 8bit mode, CLKDIV register must be set 1. Signed-off-by: addy ke addy...@rock-chips.com --- .../devicetree/bindings/mmc/rockchip-dw-mshc.txt | 4 +- drivers/mmc/host/dw_mmc-pltfm.c| 50 +- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt index c559f3f..e3f95cd 100644 --- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt @@ -10,7 +10,9 @@ extensions to the Synopsys Designware Mobile Storage Host Controller. Required Properties: * compatible: should be - - rockchip,rk2928-dw-mshc: for Rockchip RK2928 and following + - rockchip,rk2928-dw-mshc: for Rockchip RK2928 and following, + before RK3288 + - rockchip,rk3288-dw-mshc: for Rockchip RK3288 Example: diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index d4a47a9..15d796e 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -21,17 +21,61 @@ #include linux/mmc/mmc.h #include linux/mmc/dw_mmc.h #include linux/of.h +#include linux/clk.h #include dw_mmc.h #include dw_mmc-pltfm.h +#define RK3288_CLKGEN_DIV 2 + static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr) { *cmdr |= SDMMC_CMD_USE_HOLD_REG; } -static const struct dw_mci_drv_data rockchip_drv_data = { +static int dw_mci_rk3288_setup_clock(struct dw_mci *host) +{ + host-bus_hz = clk_get_rate(host-ciu_clk) / RK3288_CLKGEN_DIV; + + return 0; +} + +static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + int ret; + unsigned int cclkin; + + /* +* cclkin: source clock of mmc controller. +* bus_hz: card interface clock generated by CLKGEN. +* bus_hz = cclkin / RK3288_CLKGEN_DIV; +* ios-clock = (div == 0) ? bus_hz : (bus_hz / (2 * div)) +* +* Note: div can only be 0 or 1 +* if DDR50 8bit mode, div must be set 1 +*/ + if ((ios-bus_width == MMC_BUS_WIDTH_8) + (ios-timing == MMC_TIMING_UHS_DDR50 || +ios-timing == MMC_TIMING_MMC_DDR52)) + cclkin = 2 * ios-clock * RK3288_CLKGEN_DIV; + else + cclkin = ios-clock * RK3288_CLKGEN_DIV; + + ret = clk_set_rate(host-ciu_clk, cclkin); + if (ret) + dev_warn(host-dev, failed to set rate %uHz\n, ios-clock); + + host-bus_hz = clk_get_rate(host-ciu_clk) / RK3288_CLKGEN_DIV; +} + +static const struct dw_mci_drv_data rk2928_drv_data = { + .prepare_command= dw_mci_pltfm_prepare_command, +}; + +static const struct dw_mci_drv_data rk3288_drv_data = { .prepare_command= dw_mci_pltfm_prepare_command, + .set_ios= dw_mci_rk3288_set_ios, + .setup_clock= dw_mci_rk3288_setup_clock, }; static const struct dw_mci_drv_data socfpga_drv_data = { @@ -95,7 +139,9 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); static const struct of_device_id dw_mci_pltfm_match[] = { { .compatible = snps,dw-mshc, }, { .compatible = rockchip,rk2928-dw-mshc, - .data = rockchip_drv_data }, + .data = rk2928_drv_data }, + { .compatible = rockchip,rk3288-dw-mshc, + .data = rk3288_drv_data }, { .compatible = altr,socfpga-dw-mshc, .data = socfpga_drv_data }, {}, -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 1/2] documentation: add rockchip spi documentation
Signed-off-by: addy ke addy...@rock-chips.com --- changes since v1: - fix binding document according to comments from Mark Rutland - fix address according to comments from Mark Brown - combine all properties into Required Properties suggested by Heiko Stübner - remove Board Specific Portion suggested by Heiko Stübner .../devicetree/bindings/spi/spi-rockchip.txt | 37 ++ 1 file changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-rockchip.txt diff --git a/Documentation/devicetree/bindings/spi/spi-rockchip.txt b/Documentation/devicetree/bindings/spi/spi-rockchip.txt new file mode 100644 index 000..7bab355 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-rockchip.txt @@ -0,0 +1,37 @@ +* Rockchip SPI Controller + +The Rockchip SPI controller is used to interface with various devices such as flash +and display controllers using the SPI communication interface. + +Required Properties: + +- compatible: should be one of the following. +rockchip,rk3066-spi for rk3066. +rockchip,rk3188-spi, rockchip,rk3066-spi for rk3188. +rockchip,rk3288-spi, rockchip,rk3066-spi for rk3288. +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: The interrupt number to the cpu. The interrupt specifier format + depends on the interrupt controller. +- clocks: Must contain an entry for each entry in clock-names. +- clock-names: Shall be spiclk for the transfer-clock, and apb_pclk for + the peripheral clock. +- dmas: DMA specifiers for tx and rx dma. See the DMA client binding, + Documentation/devicetree/bindings/dma/dma.txt +- dma-names: DMA request names should include tx and rx if present. +- #address-cells: should be 1. +- #size-cells: should be 0. + +Example: + + spi0: spi@ff11 { + compatible = rockchip,rk3066-spi; + reg = 0xff11 0x1000; + dmas = pdma1 11, pdma1 12; + dma-names = tx, rx; + #address-cells = 1; + #size-cells = 0; + interrupts = GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH; + clocks = cru SCLK_SPI0, cru PCLK_SPI0; + clock-names = spiclk, apb_pclk; + }; -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 2/2] spi: add driver for Rockchip RK3xxx SoCs integrated SPI
In order to facilitate understanding, rockchip SPI controller IP design looks similar in its registers to designware. But IC implementation is different from designware, So we need a dedicated driver for Rockchip RK3XXX SoCs integrated SPI. The main differences: - dma request line: rockchip SPI controller have two DMA request line for tx and rx. - Register offset: RK3288dw SPI_CTRLR0 0x0x SPI_CTRLR1 0x00040x0004 SPI_SSIENR 0x00080x0008 SPI_MWCRNONE 0x000c SPI_SER 0x000c0x0010 SPI_BAUDR 0x00100x0014 SPI_TXFTLR 0x00140x0018 SPI_RXFTLR 0x00180x001c SPI_TXFLR 0x001c0x0020 SPI_RXFLR 0x00200x0024 SPI_SR 0x00240x0028 SPI_IPR 0x0028NONE SPI_IMR 0x002c0x002c SPI_ISR 0x00300x0030 SPI_RISR0x00340x0034 SPI_TXOICR NONE 0x0038 SPI_RXOICR NONE 0x003c SPI_RXUICR NONE 0x0040 SPI_MSTICR NONE 0x0044 SPI_ICR 0x00380x0048 SPI_DMACR 0x003c0x004c SPI_DMATDLR 0x00400x0050 SPI_DMARDLR 0x00440x0054 SPI_TXDR0x0400NONE SPI_RXDR0x0800NONE SPI_IDR NONE 0x0058 SPI_VERSION NONE 0x005c SPI_DR NONE 0x0060 - register configuration: such as SPI_CTRLRO in rockchip SPI controller: cr0 = (CR0_BHT_8BIT CR0_BHT_OFFSET) | (CR0_SSD_ONE CR0_SSD_OFFSET); cr0 |= (rs-n_bytes CR0_DFS_OFFSET); cr0 |= ((rs-mode 0x3) CR0_SCPH_OFFSET); cr0 |= (rs-tmode CR0_XFM_OFFSET); cr0 |= (rs-type CR0_FRF_OFFSET); For more information, see RK3288 chip manual. - Wait for idle: Must ensure that the FIFO data has been sent out before the next transfer. Signed-off-by: addy ke addy...@rock-chips.com --- changes since v1: - more specific about the differences according to comments from Mark Brown - not to cast away const according to comments from Mark Brown - add set_cs() operation provided to the core - change rockchip_spi_hw_init() to get_fifo_len() - remove spi_setup() and spi_clean() operations - remove wait_for_dma(), replaced by calling spi_finalize_current_transfer() when done - remove wait_for_not_busy(), replaced by setting xfer-delay_usecs - remove interrupt handler - call clk_disable_unprepare() if spi_master_resume() failed drivers/spi/Kconfig| 11 + drivers/spi/Makefile | 1 + drivers/spi/spi-rockchip.c | 822 + 3 files changed, 834 insertions(+) create mode 100644 drivers/spi/spi-rockchip.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 60f2b41..5b51ab2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -375,6 +375,17 @@ config SPI_PXA2XX config SPI_PXA2XX_PCI def_tristate SPI_PXA2XX PCI +config SPI_ROCKCHIP + tristate Rockchip SPI controller driver + help + This selects a driver for Rockchip SPI controller. + + If you say yes to this option, support will be included for + RK3066, RK3188 and RK3288 families of SPI controller. + Rockchip SPI controller support DMA transport and PIO mode. + The main usecase of this controller is to use spi flash as boot + device. + config SPI_RSPI tristate Renesas RSPI/QSPI controller depends on (SUPERH SH_DMAE_BASE) || ARCH_SHMOBILE diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index bd79266..361fbf3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -60,6 +60,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o obj-$(CONFIG_SPI_QUP) += spi-qup.o +obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o spi-s3c24xx-hw-y := spi-s3c24xx.o diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c new file mode 100644 index 000..72fb287 --- /dev/null +++ b/drivers/spi/spi-rockchip.c @@ -0,0 +1,822 @@ +/* + * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd + * Author: addy ke addy...@rock-chips.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include linux
[PATCH 0/2] add rockchip spi drive
Patch 1 add rockchip spi documentation Patch 2 add driver for Rockchip RK3XXX SoCs integrated SPI. Tested on rk3288 sdk board with polling mode and DMA mode. addy ke (2): documentation: add rockchip spi documentation spi: add driver for Rockchip RK3xxx SoCs integrated SPI .../devicetree/bindings/spi/spi-rockchip.txt | 51 ++ drivers/spi/Kconfig| 11 + drivers/spi/Makefile | 1 + drivers/spi/spi-rockchip.c | 894 + 4 files changed, 957 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-rockchip.txt create mode 100644 drivers/spi/spi-rockchip.c -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 1/2] documentation: add rockchip spi documentation
Signed-off-by: addy ke addy...@rock-chips.com --- .../devicetree/bindings/spi/spi-rockchip.txt | 51 ++ 1 file changed, 51 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-rockchip.txt diff --git a/Documentation/devicetree/bindings/spi/spi-rockchip.txt b/Documentation/devicetree/bindings/spi/spi-rockchip.txt new file mode 100644 index 000..ce9c881 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-rockchip.txt @@ -0,0 +1,51 @@ +* Rockchip SPI Controller + +The Rockchip SPI controller is used to interface with various devices such as flash +and display controllers using the SPI communication interface. + +Required SoC Specific Properties: + +- compatible: should be one of the following. +- rockchip,rk3066-spi: for rk3066, rk3188 and rk3288 platforms. +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: The interrupt number to the cpu. The interrupt specifier format + depends on the interrupt controller. +- clocks: Must contain an entry for each entry in clock-names. +- clock-names: Shall be spiclk for the transfer-clock, and apb_pclk for + the peripheral clock. + +Optional properties: +- dmas: DMA specifiers for tx and rx dma. See the DMA client binding, + Documentation/devicetree/bindings/dma/dma.txt +- dma-names: DMA request names should include tx and rx if present. + +Example: + +- SoC Specific Portion: + + spi0: spi@ff11 { + compatible = rockchip,rockchip-spi; + reg = 0xff11 0x1000; + dmas = pdma1 11, pdma1 12; + dma-names = tx, rx; + #address-cells = 1; + #size-cells = 0; + interrupts = GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH; + pinctrl-names = default; + pinctrl-0 = spi0_clk spi0_tx spi0_rx spi0_cs0 spi0_cs1; + clocks = cru SCLK_SPI0, cru PCLK_SPI0; + clock-names = spiclk, apb_pclk; + status = disabled; + }; + +- Board Specific Portion: + + spi0 { + status = okay; + spi_test@00 { + compatible = rockchip,spi_test; + reg = 0; + spi-max-frequency = 2400; + }; + }; -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe devicetree in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 2/2] spi: add driver for Rockchip RK3xxx SoCs integrated SPI
In order to facilitate understanding,rockchip SPI controller IP design looks similar in its registers to designware. But IC implementation is different from designware, such as dma request line, register offset, register configuration, and so on.So we need a dedicated driver for Rockchip RK3XXX SoCs integrated SPI. Signed-off-by: addy ke addy...@rock-chips.com --- drivers/spi/Kconfig| 11 + drivers/spi/Makefile | 1 + drivers/spi/spi-rockchip.c | 894 + 3 files changed, 906 insertions(+) create mode 100644 drivers/spi/spi-rockchip.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 60f2b41..5b51ab2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -375,6 +375,17 @@ config SPI_PXA2XX config SPI_PXA2XX_PCI def_tristate SPI_PXA2XX PCI +config SPI_ROCKCHIP + tristate Rockchip SPI controller driver + help + This selects a driver for Rockchip SPI controller. + + If you say yes to this option, support will be included for + RK3066, RK3188 and RK3288 families of SPI controller. + Rockchip SPI controller support DMA transport and PIO mode. + The main usecase of this controller is to use spi flash as boot + device. + config SPI_RSPI tristate Renesas RSPI/QSPI controller depends on (SUPERH SH_DMAE_BASE) || ARCH_SHMOBILE diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index bd79266..361fbf3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -60,6 +60,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o obj-$(CONFIG_SPI_QUP) += spi-qup.o +obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o spi-s3c24xx-hw-y := spi-s3c24xx.o diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c new file mode 100644 index 000..bd7c218 --- /dev/null +++ b/drivers/spi/spi-rockchip.c @@ -0,0 +1,894 @@ +/* + * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd + * Author: addy ke addy...@rock-chips.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include linux/init.h +#include linux/module.h +#include linux/clk.h +#include linux/err.h +#include linux/delay.h +#include linux/interrupt.h +#include linux/platform_device.h +#include linux/slab.h +#include linux/spi/spi.h +#include linux/scatterlist.h +#include linux/of.h +#include linux/pm_runtime.h +#include linux/io.h +#include linux/scatterlist.h +#include linux/dmaengine.h + +#define DRIVER_NAME rockchip-spi + +/* SPI register offsets */ +#define ROCKCHIP_SPI_CTRLR00x +#define ROCKCHIP_SPI_CTRLR10x0004 +#define ROCKCHIP_SPI_SSIENR0x0008 +#define ROCKCHIP_SPI_SER 0x000c +#define ROCKCHIP_SPI_BAUDR 0x0010 +#define ROCKCHIP_SPI_TXFTLR0x0014 +#define ROCKCHIP_SPI_RXFTLR0x0018 +#define ROCKCHIP_SPI_TXFLR 0x001c +#define ROCKCHIP_SPI_RXFLR 0x0020 +#define ROCKCHIP_SPI_SR0x0024 +#define ROCKCHIP_SPI_IPR 0x0028 +#define ROCKCHIP_SPI_IMR 0x002c +#define ROCKCHIP_SPI_ISR 0x0030 +#define ROCKCHIP_SPI_RISR 0x0034 +#define ROCKCHIP_SPI_ICR 0x0038 +#define ROCKCHIP_SPI_DMACR 0x003c +#define ROCKCHIP_SPI_DMATDLR 0x0040 +#define ROCKCHIP_SPI_DMARDLR 0x0044 +#define ROCKCHIP_SPI_TXDR 0x0400 +#define ROCKCHIP_SPI_RXDR 0x0800 + +/* Bit fields in CTRLR0 */ +#define CR0_DFS_OFFSET 0 + +#define CR0_CFS_OFFSET 2 + +#define CR0_SCPH_OFFSET6 + +#define CR0_SCPOL_OFFSET 7 + +#define CR0_CSM_OFFSET 8 +#define CR0_CSM_KEEP 0x0 +/* ss_n be high for half sclk_out cycles */ +#define CR0_CSM_HALF 0X1 +/* ss_n be high for one sclk_out cycle */ +#define CR0_CSM_ONE0x2 + +/* ss_n to sclk_out delay */ +#define CR0_SSD_OFFSET 10 +/* + * The period between