diff mbox series

[RFC] w1: omap: disable iclk autoidle

Message ID 20180929223421.32556-1-andreas@kemnade.info (mailing list archive)
State New, archived
Headers show
Series [RFC] w1: omap: disable iclk autoidle | expand

Commit Message

Andreas Kemnade Sept. 29, 2018, 10:34 p.m. UTC
On the gta04 in DM3730, omap_hdq gets stuck whenever
autosuspend is enabled for UART1/2. The system will go into
a lower power state then. According to the TRM, the module has
no way to prevent the ick from being but during a transfer if
autoidle is enabled.
So disable autoidle.
Having omap_hdq working on the gta04 is important for measuring
currents through a bq27000.

The question is what is the best place to do this.
Perhaps better in arch/arm/mach-omap2 somehow, so
no additional exported symbols are needed.
But there seems to be no simple flag to set there.
Maybe we need something like
arch/arm/mach-omap2/mcbsp.c?

And also the affected platforms need to be checked.

Probably omap_hdq_get/put should also be cleaned up and
stuff from there should be put into a runtime_suspend/resume
handler.

Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
---
 drivers/clk/ti/autoidle.c     |  2 ++
 drivers/w1/masters/omap_hdq.c | 27 ++++++++++++++++++++++++++-
 2 files changed, 28 insertions(+), 1 deletion(-)

Comments

Tony Lindgren Oct. 1, 2018, 2:47 p.m. UTC | #1
* Andreas Kemnade <andreas@kemnade.info> [180929 22:39]:
> +	 * needed to disable autoidle, if system power state is too low
> +	 * hdq transactions will not work correctly, although registers
> +	 * are accessible.
> +	 * According to AM/DM3730 TRM p.2879 the hwmod has to way to
> +	 * keep iclk running during a transfer if autoidle is enabled

Sounds like hdq1w is not wake-up capable and the uart is blocking
deeper SoC idle states. To me it seems that you should rather just
use pm_qos in the hdq1w driver to block SoC idle for the duration
of transfers.

We had a similar problem with audio playback glitches a while
back, see commit 9834ffd1ecc3 ("ASoC: omap-mcbsp: Add PM QoS
support for McBSP to prevent glitches"). See how it does
pm_qos_add_request(), pm_qos_update_request() and
pm_qos_remove_request().

Regards,

Tony
Andreas Kemnade Oct. 2, 2018, 9:37 p.m. UTC | #2
Hi Tony,


On Mon, 1 Oct 2018 07:47:45 -0700
Tony Lindgren <tony@atomide.com> wrote:

> * Andreas Kemnade <andreas@kemnade.info> [180929 22:39]:
> > +	 * needed to disable autoidle, if system power state is too low
> > +	 * hdq transactions will not work correctly, although registers
> > +	 * are accessible.
> > +	 * According to AM/DM3730 TRM p.2879 the hwmod has to way to
> > +	 * keep iclk running during a transfer if autoidle is enabled  
> 
> Sounds like hdq1w is not wake-up capable and the uart is blocking
> deeper SoC idle states. To me it seems that you should rather just
> use pm_qos in the hdq1w driver to block SoC idle for the duration
> of transfers.
> 
> We had a similar problem with audio playback glitches a while
> back, see commit 9834ffd1ecc3 ("ASoC: omap-mcbsp: Add PM QoS
> support for McBSP to prevent glitches"). See how it does
> pm_qos_add_request(), pm_qos_update_request() and
> pm_qos_remove_request().
> 
Hmm, that formula for the latency should really be commented.

I experimented with that and the results were strange.
latency = 100 seems not to be safe. 
latency = 10 seems to be safe.
If there is a qos request with latency 10, power consumption
increases even in the case when uarts are active by around 35mA.
I do not see any such increase if I keep that autoidle bit clear and
disable runtime suspend for hdq.
So the qos stuff does keep active more things when needed (of course
such a qos request should be removed if not needed anymore, that was
just for testing). And also the required latency values are quite
strange.
We have at least 190µs per bit transferred if I understand things right,
and we are getting an interrupt for every byte we transfer.
To me it feels like doing random things to make things work.
A flag in omap_hwmod_3xxx_data.c to disable iclk autoidle would feel a
lot better.
I will repeat my tests.

BTW: It is enough to idle uart 0 and 1 to have the problem, the others
can be active (well, they belong to other domains).

Regards,
Andreas
Andreas Kemnade Oct. 3, 2018, 6:08 p.m. UTC | #3
Hi Tony,

On Mon, 1 Oct 2018 07:47:45 -0700
Tony Lindgren <tony@atomide.com> wrote:

> * Andreas Kemnade <andreas@kemnade.info> [180929 22:39]:
> > +	 * needed to disable autoidle, if system power state is too low
> > +	 * hdq transactions will not work correctly, although registers
> > +	 * are accessible.
> > +	 * According to AM/DM3730 TRM p.2879 the hwmod has to way to
> > +	 * keep iclk running during a transfer if autoidle is enabled  
> 
> Sounds like hdq1w is not wake-up capable and the uart is blocking
> deeper SoC idle states. To me it seems that you should rather just
> use pm_qos in the hdq1w driver to block SoC idle for the duration
> of transfers.
> 
> We had a similar problem with audio playback glitches a while
> back, see commit 9834ffd1ecc3 ("ASoC: omap-mcbsp: Add PM QoS
> support for McBSP to prevent glitches"). See how it does
> pm_qos_add_request(), pm_qos_update_request() and
> pm_qos_remove_request().

I found this interesting function:
/**
 * _setup_iclk_autoidle - configure an IP block's interface clocks
 * @oh: struct omap_hwmod *
 *
 * Set up the module's interface clocks.  XXX This function is still mostly
 * a stub; implementing this properly requires iclk autoidle usecounting in
 * the clock code.   No return value.
 */
static void __init _setup_iclk_autoidle(struct omap_hwmod *oh)
{
        struct omap_hwmod_ocp_if *os;

        if (oh->_state != _HWMOD_STATE_INITIALIZED)
                return;

        list_for_each_entry(os, &oh->slave_ports, node) {
                if (!os->_clk)
                        continue;

                if (os->flags & OCPIF_SWSUP_IDLE) {
                        /* XXX omap_iclk_deny_idle(c); */
                } else {
                        /* XXX omap_iclk_allow_idle(c); */
                        clk_enable(os->_clk);
                }
        }

        return;
}

So at first glance it looks like we just need to uncomment the first XXX
line here. But why the heck we need usecounting for the autoidle
stuff? How do I test it (if I would implement some counter)?
IMHO the clean solution for the hdq problem would be to fix this
function.

Regards,
Andreas
Tony Lindgren Oct. 4, 2018, 2:35 p.m. UTC | #4
* Andreas Kemnade <andreas@kemnade.info> [181002 21:42]:
> On Mon, 1 Oct 2018 07:47:45 -0700
> Tony Lindgren <tony@atomide.com> wrote:
> > Sounds like hdq1w is not wake-up capable and the uart is blocking
> > deeper SoC idle states. To me it seems that you should rather just
> > use pm_qos in the hdq1w driver to block SoC idle for the duration
> > of transfers.
> > 
> > We had a similar problem with audio playback glitches a while
> > back, see commit 9834ffd1ecc3 ("ASoC: omap-mcbsp: Add PM QoS
> > support for McBSP to prevent glitches"). See how it does
> > pm_qos_add_request(), pm_qos_update_request() and
> > pm_qos_remove_request().
> > 
> Hmm, that formula for the latency should really be commented.
> 
> I experimented with that and the results were strange.
> latency = 100 seems not to be safe. 
> latency = 10 seems to be safe.

FYI, the value to pick should in theory be something out of the
latencies in omap3_idle_driver. But that data is for omap34xx,
and 36xx is faster so we really should have separate data for
36xx. Basically you'd want to block anything that cuts off the
clocks, so probably anything retention related.

> If there is a qos request with latency 10, power consumption
> increases even in the case when uarts are active by around 35mA.

OK yeah this will block deeper idle states.

> I do not see any such increase if I keep that autoidle bit clear and
> disable runtime suspend for hdq.

OK

Regards,

Tony
diff mbox series

Patch

diff --git a/drivers/clk/ti/autoidle.c b/drivers/clk/ti/autoidle.c
index 7bb9afbe4058..b8970006efd9 100644
--- a/drivers/clk/ti/autoidle.c
+++ b/drivers/clk/ti/autoidle.c
@@ -52,6 +52,7 @@  int omap2_clk_deny_idle(struct clk *clk)
 		c->ops->deny_idle(c);
 	return 0;
 }
+EXPORT_SYMBOL(omap2_clk_deny_idle);
 
 /**
  * omap2_clk_allow_idle - enable autoidle on an OMAP clock
@@ -68,6 +69,7 @@  int omap2_clk_allow_idle(struct clk *clk)
 		c->ops->allow_idle(c);
 	return 0;
 }
+EXPORT_SYMBOL(omap2_clk_allow_idle);
 
 static void _allow_autoidle(struct clk_ti_autoidle *clk)
 {
diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c
index 3099052e1243..e3aeba8a1155 100644
--- a/drivers/w1/masters/omap_hdq.c
+++ b/drivers/w1/masters/omap_hdq.c
@@ -18,6 +18,8 @@ 
 #include <linux/sched.h>
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/clk/ti.h>
 
 #include <linux/w1.h>
 
@@ -59,6 +61,14 @@  MODULE_PARM_DESC(w1_id, "1-wire id for the slave detection in HDQ mode");
 
 struct hdq_data {
 	struct device		*dev;
+	/*
+	 * needed to disable autoidle, if system power state is too low
+	 * hdq transactions will not work correctly, although registers
+	 * are accessible.
+	 * According to AM/DM3730 TRM p.2879 the hwmod has to way to
+	 * keep iclk running during a transfer if autoidle is enabled
+	 */
+	struct clk		*ick;
 	void __iomem		*hdq_base;
 	/* lock status update */
 	struct  mutex		hdq_mutex;
@@ -414,6 +424,9 @@  static int omap_hdq_get(struct hdq_data *hdq_data)
 		try_module_get(THIS_MODULE);
 		if (1 == hdq_data->hdq_usecount) {
 
+			if (!IS_ERR_OR_NULL(hdq_data->ick))
+				omap2_clk_deny_idle(hdq_data->ick);
+
 			pm_runtime_get_sync(hdq_data->dev);
 
 			/* make sure HDQ/1W is out of reset */
@@ -460,8 +473,11 @@  static int omap_hdq_put(struct hdq_data *hdq_data)
 	} else {
 		hdq_data->hdq_usecount--;
 		module_put(THIS_MODULE);
-		if (0 == hdq_data->hdq_usecount)
+		if (hdq_data->hdq_usecount == 0) {
 			pm_runtime_put_sync(hdq_data->dev);
+			if (!IS_ERR_OR_NULL(hdq_data->ick))
+				omap2_clk_allow_idle(hdq_data->ick);
+		}
 	}
 	mutex_unlock(&hdq_data->hdq_mutex);
 
@@ -681,8 +697,15 @@  static int omap_hdq_probe(struct platform_device *pdev)
 
 	hdq_data->hdq_usecount = 0;
 	hdq_data->rrw = 0;
+	hdq_data->ick = devm_clk_get(dev, "hdq_ick");
+	if (IS_ERR_OR_NULL(hdq_data->ick))
+		dev_info(dev, "no hdq_ick, lets hope autoidle behaves!");
+
 	mutex_init(&hdq_data->hdq_mutex);
 
+	if (!IS_ERR_OR_NULL(hdq_data->ick))
+		omap2_clk_deny_idle(hdq_data->ick);
+
 	pm_runtime_enable(&pdev->dev);
 	ret = pm_runtime_get_sync(&pdev->dev);
 	if (ret < 0) {
@@ -718,6 +741,8 @@  static int omap_hdq_probe(struct platform_device *pdev)
 	omap_hdq_break(hdq_data);
 
 	pm_runtime_put_sync(&pdev->dev);
+	if (!IS_ERR_OR_NULL(hdq_data->ick))
+		omap2_clk_allow_idle(hdq_data->ick);
 
 	ret = of_property_read_string(pdev->dev.of_node, "ti,mode", &mode);
 	if (ret < 0 || !strcmp(mode, "hdq")) {