diff mbox

[2/2] hwmon: (aspeed-pwm-tacho) Poll for fan_tach instead of sleep

Message ID 20170621011228.25128-2-venture@google.com (mailing list archive)
State Changes Requested
Headers show

Commit Message

Patrick Leis June 21, 2017, 1:12 a.m. UTC
The reference driver polled but mentioned it was possible to sleep
for a computed period to know when it's ready to read.  However, polling
is quick and works.  This also improves responsiveness from the driver.

Testing: tested on ast2400 on quanta-q71l

Signed-off-by: Patrick Venture <venture@google.com>
---
 drivers/hwmon/aspeed-pwm-tacho.c | 23 +++++++++++++++++------
 1 file changed, 17 insertions(+), 6 deletions(-)

Comments

Guenter Roeck June 21, 2017, 1:32 a.m. UTC | #1
On 06/20/2017 06:12 PM, Patrick Venture wrote:
> The reference driver polled but mentioned it was possible to sleep
> for a computed period to know when it's ready to read.  However, polling
> is quick and works.  This also improves responsiveness from the driver.
> 
> Testing: tested on ast2400 on quanta-q71l
> 
> Signed-off-by: Patrick Venture <venture@google.com>
> ---
>   drivers/hwmon/aspeed-pwm-tacho.c | 23 +++++++++++++++++------
>   1 file changed, 17 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c
> index b2ab5612d8a4..a31d2648fb3a 100644
> --- a/drivers/hwmon/aspeed-pwm-tacho.c
> +++ b/drivers/hwmon/aspeed-pwm-tacho.c
> @@ -163,6 +163,9 @@
>   #define M_TACH_UNIT 0x00c0
>   #define INIT_FAN_CTRL 0xFF
>   
> +/* How many times we poll the fan_tach controller for rpm data. */
> +#define FAN_TACH_RPM_TIMEOUT 25
> +

Not such a good idea. It means that the poll timeout depends on the local CPU speed.
At some point, with some CPUs, this is going to fail.

>   struct aspeed_pwm_tacho_data {
>   	struct regmap *regmap;
>   	unsigned long clk_freq;
> @@ -508,7 +511,7 @@ static u32 aspeed_get_fan_tach_ch_measure_period(struct aspeed_pwm_tacho_data
>   static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data *priv,
>   				      u8 fan_tach_ch)
>   {
> -	u32 raw_data, tach_div, clk_source, sec, val;
> +	u32 raw_data, tach_div, clk_source, sec, val, timeout;
>   	u8 fan_tach_ch_source, type, mode, both;
>   
>   	regmap_write(priv->regmap, ASPEED_PTCR_TRIGGER, 0);
> @@ -517,12 +520,20 @@ static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data *priv,
>   	fan_tach_ch_source = priv->fan_tach_ch_source[fan_tach_ch];
>   	type = priv->pwm_port_type[fan_tach_ch_source];
>   
> -	sec = (1000 / aspeed_get_fan_tach_ch_measure_period(priv, type));
> -	msleep(sec);
> -
>   	regmap_read(priv->regmap, ASPEED_PTCR_RESULT, &val);
> -	if (!(val & RESULT_STATUS_MASK))
> -		return -ETIMEDOUT;
> +	/*
> +	 * Instead of sleeping first, poll register for result.
> +	 * This is based on the reference driver's approach which expects to
> +	 * receive a value quickly.
> +	 */
> +	timeout = 0;
> +	while (!(val & RESULT_STATUS_MASK)) {
> +		timeout++;
> +		if (timeout > FAN_TACH_RPM_TIMEOUT)
> +			return -ETIMEDOUT;
> +
> +		regmap_read(priv->regmap, ASPEED_PTCR_RESULT, &val);
> +	}

No hot loop, please. Please use usleep_range() or similar within the loop,
and a well defined timeout.

Guenter

>   
>   	raw_data = val & RESULT_VALUE_MASK;
>   	tach_div = priv->type_fan_tach_clock_division[type];
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Guenter Roeck June 21, 2017, 1:39 a.m. UTC | #2
On 06/20/2017 06:32 PM, Guenter Roeck wrote:
> On 06/20/2017 06:12 PM, Patrick Venture wrote:
>> The reference driver polled but mentioned it was possible to sleep
>> for a computed period to know when it's ready to read.  However, polling
>> is quick and works.  This also improves responsiveness from the driver.
>>
>> Testing: tested on ast2400 on quanta-q71l
>>
>> Signed-off-by: Patrick Venture <venture@google.com>
>> ---
>>   drivers/hwmon/aspeed-pwm-tacho.c | 23 +++++++++++++++++------
>>   1 file changed, 17 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c
>> index b2ab5612d8a4..a31d2648fb3a 100644
>> --- a/drivers/hwmon/aspeed-pwm-tacho.c
>> +++ b/drivers/hwmon/aspeed-pwm-tacho.c
>> @@ -163,6 +163,9 @@
>>   #define M_TACH_UNIT 0x00c0
>>   #define INIT_FAN_CTRL 0xFF
>> +/* How many times we poll the fan_tach controller for rpm data. */
>> +#define FAN_TACH_RPM_TIMEOUT 25
>> +
> 
> Not such a good idea. It means that the poll timeout depends on the local CPU speed.
> At some point, with some CPUs, this is going to fail.
> 
>>   struct aspeed_pwm_tacho_data {
>>       struct regmap *regmap;
>>       unsigned long clk_freq;
>> @@ -508,7 +511,7 @@ static u32 aspeed_get_fan_tach_ch_measure_period(struct aspeed_pwm_tacho_data
>>   static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data *priv,
>>                         u8 fan_tach_ch)
>>   {
>> -    u32 raw_data, tach_div, clk_source, sec, val;
>> +    u32 raw_data, tach_div, clk_source, sec, val, timeout;
>>       u8 fan_tach_ch_source, type, mode, both;
>>       regmap_write(priv->regmap, ASPEED_PTCR_TRIGGER, 0);
>> @@ -517,12 +520,20 @@ static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data *priv,
>>       fan_tach_ch_source = priv->fan_tach_ch_source[fan_tach_ch];
>>       type = priv->pwm_port_type[fan_tach_ch_source];
>> -    sec = (1000 / aspeed_get_fan_tach_ch_measure_period(priv, type));
>> -    msleep(sec);
>> -
>>       regmap_read(priv->regmap, ASPEED_PTCR_RESULT, &val);
>> -    if (!(val & RESULT_STATUS_MASK))
>> -        return -ETIMEDOUT;
>> +    /*
>> +     * Instead of sleeping first, poll register for result.
>> +     * This is based on the reference driver's approach which expects to
>> +     * receive a value quickly.
>> +     */
>> +    timeout = 0;
>> +    while (!(val & RESULT_STATUS_MASK)) {
>> +        timeout++;
>> +        if (timeout > FAN_TACH_RPM_TIMEOUT)
>> +            return -ETIMEDOUT;
>> +
>> +        regmap_read(priv->regmap, ASPEED_PTCR_RESULT, &val);
>> +    }
> 
> No hot loop, please. Please use usleep_range() or similar within the loop,
> and a well defined timeout.
> 
You might possibly consider using regmap_read_poll_timeout().

Guenter

> Guenter
> 
>>       raw_data = val & RESULT_VALUE_MASK;
>>       tach_div = priv->type_fan_tach_clock_division[type];
>>
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Patrick Leis June 21, 2017, 1:42 a.m. UTC | #3
On Tue, Jun 20, 2017 at 6:32 PM, Guenter Roeck <linux@roeck-us.net> wrote:
> On 06/20/2017 06:12 PM, Patrick Venture wrote:
>>
>> The reference driver polled but mentioned it was possible to sleep
>> for a computed period to know when it's ready to read.  However, polling
>> is quick and works.  This also improves responsiveness from the driver.
>>
>> Testing: tested on ast2400 on quanta-q71l
>>
>> Signed-off-by: Patrick Venture <venture@google.com>
>> ---
>>   drivers/hwmon/aspeed-pwm-tacho.c | 23 +++++++++++++++++------
>>   1 file changed, 17 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/hwmon/aspeed-pwm-tacho.c
>> b/drivers/hwmon/aspeed-pwm-tacho.c
>> index b2ab5612d8a4..a31d2648fb3a 100644
>> --- a/drivers/hwmon/aspeed-pwm-tacho.c
>> +++ b/drivers/hwmon/aspeed-pwm-tacho.c
>> @@ -163,6 +163,9 @@
>>   #define M_TACH_UNIT 0x00c0
>>   #define INIT_FAN_CTRL 0xFF
>>   +/* How many times we poll the fan_tach controller for rpm data. */
>> +#define FAN_TACH_RPM_TIMEOUT 25
>> +
>
>
> Not such a good idea. It means that the poll timeout depends on the local
> CPU speed.
> At some point, with some CPUs, this is going to fail.
>
>
>>   struct aspeed_pwm_tacho_data {
>>         struct regmap *regmap;
>>         unsigned long clk_freq;
>> @@ -508,7 +511,7 @@ static u32
>> aspeed_get_fan_tach_ch_measure_period(struct aspeed_pwm_tacho_data
>>   static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data
>> *priv,
>>                                       u8 fan_tach_ch)
>>   {
>> -       u32 raw_data, tach_div, clk_source, sec, val;
>> +       u32 raw_data, tach_div, clk_source, sec, val, timeout;
>>         u8 fan_tach_ch_source, type, mode, both;
>>         regmap_write(priv->regmap, ASPEED_PTCR_TRIGGER, 0);
>> @@ -517,12 +520,20 @@ static int aspeed_get_fan_tach_ch_rpm(struct
>> aspeed_pwm_tacho_data *priv,
>>         fan_tach_ch_source = priv->fan_tach_ch_source[fan_tach_ch];
>>         type = priv->pwm_port_type[fan_tach_ch_source];
>>   -     sec = (1000 / aspeed_get_fan_tach_ch_measure_period(priv, type));
>> -       msleep(sec);
>> -
>>         regmap_read(priv->regmap, ASPEED_PTCR_RESULT, &val);
>> -       if (!(val & RESULT_STATUS_MASK))
>> -               return -ETIMEDOUT;
>> +       /*
>> +        * Instead of sleeping first, poll register for result.
>> +        * This is based on the reference driver's approach which expects
>> to
>> +        * receive a value quickly.
>> +        */
>> +       timeout = 0;
>> +       while (!(val & RESULT_STATUS_MASK)) {
>> +               timeout++;
>> +               if (timeout > FAN_TACH_RPM_TIMEOUT)
>> +                       return -ETIMEDOUT;
>> +
>> +               regmap_read(priv->regmap, ASPEED_PTCR_RESULT, &val);
>> +       }
>
>
> No hot loop, please. Please use usleep_range() or similar within the loop,
> and a well defined timeout.
>

Thanks, that's a good call.

I'll implement a variation in the morning and test it to see if there
are any subtle changes required and try to pick good ranges or
something similar.  We need responsiveness down to under 0.0125s so
eight can be read sequentially ten times per second.  The controller
itself actually seems to internally cache a value and only gets
updated once ever 0.08s.  Just as an aside.

I'll check out "regmap_read_poll_timeout" as well per follow-on email.

> Guenter
>
>
>>         raw_data = val & RESULT_VALUE_MASK;
>>         tach_div = priv->type_fan_tach_clock_division[type];
>>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Guenter Roeck June 21, 2017, 1:52 a.m. UTC | #4
On 06/20/2017 06:42 PM, Patrick Venture wrote:
> On Tue, Jun 20, 2017 at 6:32 PM, Guenter Roeck <linux@roeck-us.net> wrote:
>> On 06/20/2017 06:12 PM, Patrick Venture wrote:
>>>
>>> The reference driver polled but mentioned it was possible to sleep
>>> for a computed period to know when it's ready to read.  However, polling
>>> is quick and works.  This also improves responsiveness from the driver.
>>>
>>> Testing: tested on ast2400 on quanta-q71l
>>>
>>> Signed-off-by: Patrick Venture <venture@google.com>
>>> ---
>>>    drivers/hwmon/aspeed-pwm-tacho.c | 23 +++++++++++++++++------
>>>    1 file changed, 17 insertions(+), 6 deletions(-)
>>>
>>> diff --git a/drivers/hwmon/aspeed-pwm-tacho.c
>>> b/drivers/hwmon/aspeed-pwm-tacho.c
>>> index b2ab5612d8a4..a31d2648fb3a 100644
>>> --- a/drivers/hwmon/aspeed-pwm-tacho.c
>>> +++ b/drivers/hwmon/aspeed-pwm-tacho.c
>>> @@ -163,6 +163,9 @@
>>>    #define M_TACH_UNIT 0x00c0
>>>    #define INIT_FAN_CTRL 0xFF
>>>    +/* How many times we poll the fan_tach controller for rpm data. */
>>> +#define FAN_TACH_RPM_TIMEOUT 25
>>> +
>>
>>
>> Not such a good idea. It means that the poll timeout depends on the local
>> CPU speed.
>> At some point, with some CPUs, this is going to fail.
>>
>>
>>>    struct aspeed_pwm_tacho_data {
>>>          struct regmap *regmap;
>>>          unsigned long clk_freq;
>>> @@ -508,7 +511,7 @@ static u32
>>> aspeed_get_fan_tach_ch_measure_period(struct aspeed_pwm_tacho_data
>>>    static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data
>>> *priv,
>>>                                        u8 fan_tach_ch)
>>>    {
>>> -       u32 raw_data, tach_div, clk_source, sec, val;
>>> +       u32 raw_data, tach_div, clk_source, sec, val, timeout;
>>>          u8 fan_tach_ch_source, type, mode, both;
>>>          regmap_write(priv->regmap, ASPEED_PTCR_TRIGGER, 0);
>>> @@ -517,12 +520,20 @@ static int aspeed_get_fan_tach_ch_rpm(struct
>>> aspeed_pwm_tacho_data *priv,
>>>          fan_tach_ch_source = priv->fan_tach_ch_source[fan_tach_ch];
>>>          type = priv->pwm_port_type[fan_tach_ch_source];
>>>    -     sec = (1000 / aspeed_get_fan_tach_ch_measure_period(priv, type));
>>> -       msleep(sec);
>>> -
>>>          regmap_read(priv->regmap, ASPEED_PTCR_RESULT, &val);
>>> -       if (!(val & RESULT_STATUS_MASK))
>>> -               return -ETIMEDOUT;
>>> +       /*
>>> +        * Instead of sleeping first, poll register for result.
>>> +        * This is based on the reference driver's approach which expects
>>> to
>>> +        * receive a value quickly.
>>> +        */
>>> +       timeout = 0;
>>> +       while (!(val & RESULT_STATUS_MASK)) {
>>> +               timeout++;
>>> +               if (timeout > FAN_TACH_RPM_TIMEOUT)
>>> +                       return -ETIMEDOUT;
>>> +
>>> +               regmap_read(priv->regmap, ASPEED_PTCR_RESULT, &val);
>>> +       }
>>
>>
>> No hot loop, please. Please use usleep_range() or similar within the loop,
>> and a well defined timeout.
>>
> 
> Thanks, that's a good call.
> 
> I'll implement a variation in the morning and test it to see if there
> are any subtle changes required and try to pick good ranges or
> something similar.  We need responsiveness down to under 0.0125s so

That is forever ;-). 12.5 ms -> picking something like a 500 uS sleep period
for regmap_read_poll_timeout() and the old maximum sleep time for its
timeout_us parameter should do it.

Guenter

> eight can be read sequentially ten times per second.  The controller
> itself actually seems to internally cache a value and only gets
> updated once ever 0.08s.  Just as an aside.
> 
> I'll check out "regmap_read_poll_timeout" as well per follow-on email.
> 
>> Guenter
>>
>>
>>>          raw_data = val & RESULT_VALUE_MASK;
>>>          tach_div = priv->type_fan_tach_clock_division[type];
>>>
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" 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/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c
index b2ab5612d8a4..a31d2648fb3a 100644
--- a/drivers/hwmon/aspeed-pwm-tacho.c
+++ b/drivers/hwmon/aspeed-pwm-tacho.c
@@ -163,6 +163,9 @@ 
 #define M_TACH_UNIT 0x00c0
 #define INIT_FAN_CTRL 0xFF
 
+/* How many times we poll the fan_tach controller for rpm data. */
+#define FAN_TACH_RPM_TIMEOUT 25
+
 struct aspeed_pwm_tacho_data {
 	struct regmap *regmap;
 	unsigned long clk_freq;
@@ -508,7 +511,7 @@  static u32 aspeed_get_fan_tach_ch_measure_period(struct aspeed_pwm_tacho_data
 static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data *priv,
 				      u8 fan_tach_ch)
 {
-	u32 raw_data, tach_div, clk_source, sec, val;
+	u32 raw_data, tach_div, clk_source, sec, val, timeout;
 	u8 fan_tach_ch_source, type, mode, both;
 
 	regmap_write(priv->regmap, ASPEED_PTCR_TRIGGER, 0);
@@ -517,12 +520,20 @@  static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data *priv,
 	fan_tach_ch_source = priv->fan_tach_ch_source[fan_tach_ch];
 	type = priv->pwm_port_type[fan_tach_ch_source];
 
-	sec = (1000 / aspeed_get_fan_tach_ch_measure_period(priv, type));
-	msleep(sec);
-
 	regmap_read(priv->regmap, ASPEED_PTCR_RESULT, &val);
-	if (!(val & RESULT_STATUS_MASK))
-		return -ETIMEDOUT;
+	/*
+	 * Instead of sleeping first, poll register for result.
+	 * This is based on the reference driver's approach which expects to
+	 * receive a value quickly.
+	 */
+	timeout = 0;
+	while (!(val & RESULT_STATUS_MASK)) {
+		timeout++;
+		if (timeout > FAN_TACH_RPM_TIMEOUT)
+			return -ETIMEDOUT;
+
+		regmap_read(priv->regmap, ASPEED_PTCR_RESULT, &val);
+	}
 
 	raw_data = val & RESULT_VALUE_MASK;
 	tach_div = priv->type_fan_tach_clock_division[type];