diff mbox

[3/5] mmc: sdhi: Add write16_hook

Message ID 1308610812-3479-4-git-send-email-horms@verge.net.au (mailing list archive)
State New, archived
Headers show

Commit Message

Simon Horman June 20, 2011, 11 p.m. UTC
Some controllers require waiting for the bus to become idle
before writing to some registers. I have implemented this
by adding a hook to sd_ctrl_write16() and implementing
a hook for SDHI which waits for the bus to become idle.

Cc: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Cc: Magnus Damm <magnus.damm@gmail.com>
Signed-off-by: Simon Horman <horms@verge.net.au>

---

Dependencies: "mmc: tmio: Share register access functions"

v2:
* Include linux/delay.h instead of asm/delay.h
* Skip write if sh_mobile_sdhi_wait_idle() times out
  - The bus will probably be in an inconsistent state and writing
    may lock up the bus
* Only set hook if TMIO_MMC_HAS_IDLE_WAIT is set in platform data
  rather than checking for TMIO_MMC_HAS_IDLE_WAIT each time the
  hook is called.
---
 drivers/mmc/host/sh_mobile_sdhi.c |   36 ++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/tmio_mmc.h       |    5 +++++
 include/linux/mfd/tmio.h          |    8 ++++++++
 include/linux/mmc/tmio.h          |    1 +
 4 files changed, 50 insertions(+), 0 deletions(-)

Comments

Magnus Damm June 21, 2011, 12:36 a.m. UTC | #1
On Tue, Jun 21, 2011 at 8:00 AM, Simon Horman <horms@verge.net.au> wrote:
> Some controllers require waiting for the bus to become idle
> before writing to some registers. I have implemented this
> by adding a hook to sd_ctrl_write16() and implementing
> a hook for SDHI which waits for the bus to become idle.
>
> Cc: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> Cc: Magnus Damm <magnus.damm@gmail.com>
> Signed-off-by: Simon Horman <horms@verge.net.au>
>
> ---
>
> Dependencies: "mmc: tmio: Share register access functions"
>
> v2:
> * Include linux/delay.h instead of asm/delay.h
> * Skip write if sh_mobile_sdhi_wait_idle() times out
>  - The bus will probably be in an inconsistent state and writing
>    may lock up the bus
> * Only set hook if TMIO_MMC_HAS_IDLE_WAIT is set in platform data
>  rather than checking for TMIO_MMC_HAS_IDLE_WAIT each time the
>  hook is called.
> ---

Thanks Simon, this version looks much better!

> index 5a90266..0dc9804 100644
> --- a/include/linux/mfd/tmio.h
> +++ b/include/linux/mfd/tmio.h
> @@ -94,6 +101,7 @@ struct tmio_mmc_data {
>        void (*set_pwr)(struct platform_device *host, int state);
>        void (*set_clk_div)(struct platform_device *host, int state);
>        int (*get_cd)(struct platform_device *host);
> +       int (*write16_hook)(struct tmio_mmc_host *host, int addr);
>  };
>
>  static inline void tmio_mmc_cd_wakeup(struct tmio_mmc_data *pdata)

What's the reason behind passing "struct tmio_mmc_host *"  as an
argument to the new hook? Performance? All other callbacks seem to
take a "struct platform_device *", so being consistent here may be
good unless it comes with too much overhead.

Thanks,

/ magnus
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Simon Horman June 21, 2011, 12:50 a.m. UTC | #2
On Tue, Jun 21, 2011 at 09:36:03AM +0900, Magnus Damm wrote:
> On Tue, Jun 21, 2011 at 8:00 AM, Simon Horman <horms@verge.net.au> wrote:
> > Some controllers require waiting for the bus to become idle
> > before writing to some registers. I have implemented this
> > by adding a hook to sd_ctrl_write16() and implementing
> > a hook for SDHI which waits for the bus to become idle.
> >
> > Cc: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> > Cc: Magnus Damm <magnus.damm@gmail.com>
> > Signed-off-by: Simon Horman <horms@verge.net.au>
> >
> > ---
> >
> > Dependencies: "mmc: tmio: Share register access functions"
> >
> > v2:
> > * Include linux/delay.h instead of asm/delay.h
> > * Skip write if sh_mobile_sdhi_wait_idle() times out
> >  - The bus will probably be in an inconsistent state and writing
> >    may lock up the bus
> > * Only set hook if TMIO_MMC_HAS_IDLE_WAIT is set in platform data
> >  rather than checking for TMIO_MMC_HAS_IDLE_WAIT each time the
> >  hook is called.
> > ---
> 
> Thanks Simon, this version looks much better!
> 
> > index 5a90266..0dc9804 100644
> > --- a/include/linux/mfd/tmio.h
> > +++ b/include/linux/mfd/tmio.h
> > @@ -94,6 +101,7 @@ struct tmio_mmc_data {
> >        void (*set_pwr)(struct platform_device *host, int state);
> >        void (*set_clk_div)(struct platform_device *host, int state);
> >        int (*get_cd)(struct platform_device *host);
> > +       int (*write16_hook)(struct tmio_mmc_host *host, int addr);
> >  };
> >
> >  static inline void tmio_mmc_cd_wakeup(struct tmio_mmc_data *pdata)
> 
> What's the reason behind passing "struct tmio_mmc_host *"  as an
> argument to the new hook? Performance? All other callbacks seem to
> take a "struct platform_device *", so being consistent here may be
> good unless it comes with too much overhead.

The reason is that
1) The hook is called from sd_ctrl_write16 which takes 
   struct tmio_mmc_host * as its first argument and;
2) The hook that has been implemented calls sd_ctrl_read16() which takes a
   struct tmio_mmc_host * as its first argument.
So it seemed logical to pass that down.

In the caes of 1) we can get the struct platform_device * using host->pdev.
However, in the case of 2) is it less clear to me how we can get the
struct tmio_mmc_host * from a struct platform_device *.

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Magnus Damm June 21, 2011, 12:59 a.m. UTC | #3
On Tue, Jun 21, 2011 at 9:50 AM, Simon Horman <horms@verge.net.au> wrote:
> On Tue, Jun 21, 2011 at 09:36:03AM +0900, Magnus Damm wrote:
>> On Tue, Jun 21, 2011 at 8:00 AM, Simon Horman <horms@verge.net.au> wrote:
>> > Some controllers require waiting for the bus to become idle
>> > before writing to some registers. I have implemented this
>> > by adding a hook to sd_ctrl_write16() and implementing
>> > a hook for SDHI which waits for the bus to become idle.
>> >
>> > Cc: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
>> > Cc: Magnus Damm <magnus.damm@gmail.com>
>> > Signed-off-by: Simon Horman <horms@verge.net.au>
>> >
>> > ---
>> >
>> > Dependencies: "mmc: tmio: Share register access functions"
>> >
>> > v2:
>> > * Include linux/delay.h instead of asm/delay.h
>> > * Skip write if sh_mobile_sdhi_wait_idle() times out
>> >  - The bus will probably be in an inconsistent state and writing
>> >    may lock up the bus
>> > * Only set hook if TMIO_MMC_HAS_IDLE_WAIT is set in platform data
>> >  rather than checking for TMIO_MMC_HAS_IDLE_WAIT each time the
>> >  hook is called.
>> > ---
>>
>> Thanks Simon, this version looks much better!
>>
>> > index 5a90266..0dc9804 100644
>> > --- a/include/linux/mfd/tmio.h
>> > +++ b/include/linux/mfd/tmio.h
>> > @@ -94,6 +101,7 @@ struct tmio_mmc_data {
>> >        void (*set_pwr)(struct platform_device *host, int state);
>> >        void (*set_clk_div)(struct platform_device *host, int state);
>> >        int (*get_cd)(struct platform_device *host);
>> > +       int (*write16_hook)(struct tmio_mmc_host *host, int addr);
>> >  };
>> >
>> >  static inline void tmio_mmc_cd_wakeup(struct tmio_mmc_data *pdata)
>>
>> What's the reason behind passing "struct tmio_mmc_host *"  as an
>> argument to the new hook? Performance? All other callbacks seem to
>> take a "struct platform_device *", so being consistent here may be
>> good unless it comes with too much overhead.
>
> The reason is that
> 1) The hook is called from sd_ctrl_write16 which takes
>   struct tmio_mmc_host * as its first argument and;
> 2) The hook that has been implemented calls sd_ctrl_read16() which takes a
>   struct tmio_mmc_host * as its first argument.
> So it seemed logical to pass that down.
>
> In the caes of 1) we can get the struct platform_device * using host->pdev.
> However, in the case of 2) is it less clear to me how we can get the
> struct tmio_mmc_host * from a struct platform_device *.

Have a look at the code in tmio_mmc_host_suspend() for some code that
does struct device * -> struct tmio_mmc_host *:
int tmio_mmc_host_suspend(struct device *dev)
{
	struct mmc_host *mmc = dev_get_drvdata(dev);
	struct tmio_mmc_host *host = mmc_priv(mmc);

You can easily change the dev_get_drvdata() to platform_get_drvdata(),
see include/linux/platform_device.h

I guess a similar conversion can be done in tmio_mmc_enable_dma() to
move from writew() to sd_ctrl_write16()?

Cheers,

/ magnus
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Simon Horman June 21, 2011, 1:13 a.m. UTC | #4
On Tue, Jun 21, 2011 at 09:59:37AM +0900, Magnus Damm wrote:
> On Tue, Jun 21, 2011 at 9:50 AM, Simon Horman <horms@verge.net.au> wrote:
> > On Tue, Jun 21, 2011 at 09:36:03AM +0900, Magnus Damm wrote:
> >> On Tue, Jun 21, 2011 at 8:00 AM, Simon Horman <horms@verge.net.au> wrote:

[snip]

> >> > index 5a90266..0dc9804 100644
> >> > --- a/include/linux/mfd/tmio.h
> >> > +++ b/include/linux/mfd/tmio.h
> >> > @@ -94,6 +101,7 @@ struct tmio_mmc_data {
> >> >        void (*set_pwr)(struct platform_device *host, int state);
> >> >        void (*set_clk_div)(struct platform_device *host, int state);
> >> >        int (*get_cd)(struct platform_device *host);
> >> > +       int (*write16_hook)(struct tmio_mmc_host *host, int addr);
> >> >  };
> >> >
> >> >  static inline void tmio_mmc_cd_wakeup(struct tmio_mmc_data *pdata)
> >>
> >> What's the reason behind passing "struct tmio_mmc_host *"  as an
> >> argument to the new hook? Performance? All other callbacks seem to
> >> take a "struct platform_device *", so being consistent here may be
> >> good unless it comes with too much overhead.
> >
> > The reason is that
> > 1) The hook is called from sd_ctrl_write16 which takes
> >   struct tmio_mmc_host * as its first argument and;
> > 2) The hook that has been implemented calls sd_ctrl_read16() which takes a
> >   struct tmio_mmc_host * as its first argument.
> > So it seemed logical to pass that down.
> >
> > In the caes of 1) we can get the struct platform_device * using host->pdev.
> > However, in the case of 2) is it less clear to me how we can get the
> > struct tmio_mmc_host * from a struct platform_device *.
> 
> Have a look at the code in tmio_mmc_host_suspend() for some code that
> does struct device * -> struct tmio_mmc_host *:
> int tmio_mmc_host_suspend(struct device *dev)
> {
> 	struct mmc_host *mmc = dev_get_drvdata(dev);
> 	struct tmio_mmc_host *host = mmc_priv(mmc);
> 
> You can easily change the dev_get_drvdata() to platform_get_drvdata(),
> see include/linux/platform_device.h

Thanks, I'm happy to make that change if you think it is worth it.
(I will need to re-test on AG5, which I could do this afternoon
 if it is free)

> I guess a similar conversion can be done in tmio_mmc_enable_dma() to
> move from writew() to sd_ctrl_write16()?

Are you proposing changing tmio_mmc_enable_dma() to take
a struct platform_device * as its first argument?

tmio_mmc_enable_dma() is already altered in one of the
patches in this series to use sd_ctrl_write16() without
altering the arguments taht tmio_mmc_enable_dma() takes.

static void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable)
{
#if defined(CONFIG_SUPERH) || defined(CONFIG_ARCH_SHMOBILE)
	/* Switch DMA mode on or off - SuperH specific? */
	sd_ctrl_write16(host, enable ? 2 : 0, CTL_DMA_ENABLE);
#endif
}
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Magnus Damm June 21, 2011, 1:36 a.m. UTC | #5
Hi Simon,

On Tue, Jun 21, 2011 at 10:13 AM, Simon Horman <horms@verge.net.au> wrote:
> On Tue, Jun 21, 2011 at 09:59:37AM +0900, Magnus Damm wrote:
>> On Tue, Jun 21, 2011 at 9:50 AM, Simon Horman <horms@verge.net.au> wrote:
>> > On Tue, Jun 21, 2011 at 09:36:03AM +0900, Magnus Damm wrote:
>> >> On Tue, Jun 21, 2011 at 8:00 AM, Simon Horman <horms@verge.net.au> wrote:
>
> [snip]
>
>> >> > index 5a90266..0dc9804 100644
>> >> > --- a/include/linux/mfd/tmio.h
>> >> > +++ b/include/linux/mfd/tmio.h
>> >> > @@ -94,6 +101,7 @@ struct tmio_mmc_data {
>> >> >        void (*set_pwr)(struct platform_device *host, int state);
>> >> >        void (*set_clk_div)(struct platform_device *host, int state);
>> >> >        int (*get_cd)(struct platform_device *host);
>> >> > +       int (*write16_hook)(struct tmio_mmc_host *host, int addr);
>> >> >  };
>> >> >
>> >> >  static inline void tmio_mmc_cd_wakeup(struct tmio_mmc_data *pdata)
>> >>
>> >> What's the reason behind passing "struct tmio_mmc_host *"  as an
>> >> argument to the new hook? Performance? All other callbacks seem to
>> >> take a "struct platform_device *", so being consistent here may be
>> >> good unless it comes with too much overhead.
>> >
>> > The reason is that
>> > 1) The hook is called from sd_ctrl_write16 which takes
>> >   struct tmio_mmc_host * as its first argument and;
>> > 2) The hook that has been implemented calls sd_ctrl_read16() which takes a
>> >   struct tmio_mmc_host * as its first argument.
>> > So it seemed logical to pass that down.
>> >
>> > In the caes of 1) we can get the struct platform_device * using host->pdev.
>> > However, in the case of 2) is it less clear to me how we can get the
>> > struct tmio_mmc_host * from a struct platform_device *.
>>
>> Have a look at the code in tmio_mmc_host_suspend() for some code that
>> does struct device * -> struct tmio_mmc_host *:
>> int tmio_mmc_host_suspend(struct device *dev)
>> {
>>       struct mmc_host *mmc = dev_get_drvdata(dev);
>>       struct tmio_mmc_host *host = mmc_priv(mmc);
>>
>> You can easily change the dev_get_drvdata() to platform_get_drvdata(),
>> see include/linux/platform_device.h
>
> Thanks, I'm happy to make that change if you think it is worth it.
> (I will need to re-test on AG5, which I could do this afternoon
>  if it is free)

Hm, perhaps it can be done with incremental patches in the future?

I think it's good to be consistent and use the same argument passing
style as other callbacks, but at the same time I'm not 100% sure if
passing a platform data pointer is the best approach. It probably made
sense with the old tmio_mmc driver that only hooked up to MFD, but I'm
not sure if that's the case anymore. I'm sure there is room for plenty
of cleanups - but exactly what to do I don't know. =)

At least passing a struct tmio_mmc_host * requires little conversion
which should add minimal overhead.

>> I guess a similar conversion can be done in tmio_mmc_enable_dma() to
>> move from writew() to sd_ctrl_write16()?
>
> Are you proposing changing tmio_mmc_enable_dma() to take
> a struct platform_device * as its first argument?

No, not at all. I just recall someone pointing out that
tmio_mmc_enable_dma() skipped the tmio_mmc specific I/O routines and
used writew() directly. I suspected the reason behind this was the
difficulty of converting between different pointer types, but that may
not be true.

> tmio_mmc_enable_dma() is already altered in one of the
> patches in this series to use sd_ctrl_write16() without
> altering the arguments taht tmio_mmc_enable_dma() takes.

Ok, that's good.

> static void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable)
> {
> #if defined(CONFIG_SUPERH) || defined(CONFIG_ARCH_SHMOBILE)
>        /* Switch DMA mode on or off - SuperH specific? */
>        sd_ctrl_write16(host, enable ? 2 : 0, CTL_DMA_ENABLE);
> #endif
> }

Hm, perhaps it's my mail setup that's the issue, but the version of
"[PATCH 1/5] mmc: tmio: name 0xd8 as CTL_DMA_ENABLE" that I'm looking
at is still using writew().

Cheers,

/ magnus
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index b365429..c8b1f6b 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -26,6 +26,7 @@ 
 #include <linux/mmc/sh_mobile_sdhi.h>
 #include <linux/mfd/tmio.h>
 #include <linux/sh_dma.h>
+#include <linux/delay.h>
 
 #include "tmio_mmc.h"
 
@@ -55,6 +56,39 @@  static int sh_mobile_sdhi_get_cd(struct platform_device *pdev)
 		return -ENOSYS;
 }
 
+static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
+{
+	int timeout = 1000;
+
+	while (--timeout && !(sd_ctrl_read16(host, CTL_STATUS2) & (1 << 13)))
+		udelay(1);
+
+	if (!timeout) {
+		dev_warn(host->pdata->dev, "timeout waiting for SD bus idle\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
+{
+	switch (addr)
+	{
+	case CTL_SD_CMD:
+	case CTL_STOP_INTERNAL_ACTION:
+	case CTL_XFER_BLK_COUNT:
+	case CTL_SD_CARD_CLK_CTL:
+	case CTL_SD_XFER_LEN:
+	case CTL_SD_MEM_CARD_OPT:
+	case CTL_TRANSACTION_CTL:
+	case CTL_DMA_ENABLE:
+		return sh_mobile_sdhi_wait_idle(host);
+	}
+
+	return 0;
+}
+
 static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
 {
 	struct sh_mobile_sdhi *priv;
@@ -86,6 +120,8 @@  static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
 	mmc_data->hclk = clk_get_rate(priv->clk);
 	mmc_data->set_pwr = sh_mobile_sdhi_set_pwr;
 	mmc_data->get_cd = sh_mobile_sdhi_get_cd;
+	if (mmc_data->flags & TMIO_MMC_HAS_IDLE_WAIT)
+		mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
 	mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
 	if (p) {
 		mmc_data->flags = p->tmio_flags;
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 0c22df0..211ef6e 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -153,6 +153,11 @@  static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
 
 static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val)
 {
+	/* If there is a hook and it returns non-zero then there
+	 * is an error and the write should be skipped
+	 */
+	if (host->pdata->write16_hook && host->pdata->write16_hook(host, addr))
+		return;
 	writew(val, host->ctl + (addr << host->bus_shift));
 }
 
diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h
index 5a90266..0dc9804 100644
--- a/include/linux/mfd/tmio.h
+++ b/include/linux/mfd/tmio.h
@@ -68,6 +68,11 @@ 
  * controller and report the event to the driver.
  */
 #define TMIO_MMC_HAS_COLD_CD		(1 << 3)
+/*
+ * Some controllers require waiting for the SD bus to become
+ * idle before writing to some registers.
+ */
+#define TMIO_MMC_HAS_IDLE_WAIT		(1 << 4)
 
 int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base);
 int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
@@ -80,6 +85,8 @@  struct tmio_mmc_dma {
 	int alignment_shift;
 };
 
+struct tmio_mmc_host;
+
 /*
  * data for the MMC controller
  */
@@ -94,6 +101,7 @@  struct tmio_mmc_data {
 	void (*set_pwr)(struct platform_device *host, int state);
 	void (*set_clk_div)(struct platform_device *host, int state);
 	int (*get_cd)(struct platform_device *host);
+	int (*write16_hook)(struct tmio_mmc_host *host, int addr);
 };
 
 static inline void tmio_mmc_cd_wakeup(struct tmio_mmc_data *pdata)
diff --git a/include/linux/mmc/tmio.h b/include/linux/mmc/tmio.h
index 0a6e40d..f235757 100644
--- a/include/linux/mmc/tmio.h
+++ b/include/linux/mmc/tmio.h
@@ -21,6 +21,7 @@ 
 #define CTL_XFER_BLK_COUNT 0xa
 #define CTL_RESPONSE 0x0c
 #define CTL_STATUS 0x1c
+#define CTL_STATUS2 0x1e
 #define CTL_IRQ_MASK 0x20
 #define CTL_SD_CARD_CLK_CTL 0x24
 #define CTL_SD_XFER_LEN 0x26