linux-pm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Stephen Boyd <swboyd@chromium.org>
To: Amit Kucheria <amit.kucheria@linaro.org>,
	Daniel Lezcano <daniel.lezcano@linaro.org>,
	Mark Rutland <mark.rutland@arm.com>,
	Rob Herring <robh+dt@kernel.org>, Zhang Rui <rui.zhang@intel.com>,
	agross@kernel.org, bjorn.andersson@linaro.org,
	edubezval@gmail.com, linux-arm-msm@vger.kernel.org,
	linux-kernel@vger.kernel.org, marc.w.gonzalez@free.fr,
	masneyb@onstation.org
Cc: linux-pm@vger.kernel.org
Subject: Re: [PATCH v2 15/15] drivers: thermal: tsens: Add interrupt support
Date: Wed, 28 Aug 2019 14:42:28 -0700	[thread overview]
Message-ID: <5d66f545.1c69fb81.3663f.129d@mx.google.com> (raw)
In-Reply-To: <a4666f8afa39471658602e06758b04a991f80828.1566907161.git.amit.kucheria@linaro.org>

Quoting Amit Kucheria (2019-08-27 05:14:11)
> diff --git a/drivers/thermal/qcom/tsens-common.c b/drivers/thermal/qcom/tsens-common.c
> index 06b44cfd5eab9..c549f8e1488ba 100644
> --- a/drivers/thermal/qcom/tsens-common.c
> +++ b/drivers/thermal/qcom/tsens-common.c
> @@ -114,6 +146,314 @@ static int tsens_hw_to_mC(struct tsens_sensor *s, int field)
>         return sign_extend32(temp, priv->tempres) * 100;
>  }
>  
> +/**
> + * tsens_mC_to_hw - Return correct value to be written to threshold
> + * registers, whether in ADC code or deciCelsius depending on IP version

Document arguments and return value? Maybe summary can be 'convert
tsens temperature to hardware register value'?

> + */
> +static int tsens_mC_to_hw(struct tsens_sensor *s, int temp)
> +{
> +       struct tsens_priv *priv = s->priv;
> +
> +       if (priv->feat->adc) {
> +               /* milliC to C to adc code */
> +               return degc_to_code(temp / 1000, s);
> +       }
> +
> +       /* milliC to deciC */
> +       return temp / 100;
> +}
> +
> +static inline unsigned int tsens_ver(struct tsens_priv *priv)

Can this return the enum instead of unsigned int?

> +{
> +       return priv->feat->ver_major;
> +}
> +
> +/**
> + * tsens_set_interrupt_v1 - Disable an interrupt (enable = false)
> + *                          Re-enable an interrupt (enable = true)
> + */
> +static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id,
> +                                  enum tsens_irq_type irq_type, bool enable)
> +{
> +       u32 index;
> +
> +       if (enable) {
> +               switch (irq_type) {
> +               case UPPER:
> +                       index = UP_INT_CLEAR_0 + hw_id;
> +                       break;
> +               case LOWER:
> +                       index = LOW_INT_CLEAR_0 + hw_id;
> +                       break;
> +               }
> +               regmap_field_write(priv->rf[index], 0);
> +       } else {
> +               switch (irq_type) {
> +               case UPPER:
> +                       index = UP_INT_CLEAR_0 + hw_id;
> +                       break;
> +               case LOWER:
> +                       index = LOW_INT_CLEAR_0 + hw_id;
> +                       break;
> +               }
> +               regmap_field_write(priv->rf[index], 1);
> +       }

De-dup the switch statement and have

	regmap_field_write(priv->rf[index], enable ? 1 : 0);

> +}
> +
> +/**
> + * tsens_set_interrupt_v2 - Disable an interrupt (enable = false)
> + *                          Re-enable an interrupt (enable = true)
> + */
> +static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id,
> +                                  enum tsens_irq_type irq_type, bool enable)
> +{
> +       u32 index_mask, index_clear;
> +
> +       if (enable) {
> +               switch (irq_type) {
> +               case UPPER:
> +                       index_mask = UP_INT_MASK_0 + hw_id;
> +                       break;
> +               case LOWER:
> +                       index_mask = LOW_INT_MASK_0 + hw_id;
> +                       break;
> +               }
> +               regmap_field_write(priv->rf[index_mask], 0);
> +       } else {
> +               /* To disable the interrupt flag for a sensor:

Nitpick: Wrong comment style.

> +                *  1. Mask further interrupts for this sensor
> +                *  2. Write 1 followed by 0 to clear the interrupt
> +                */
> +               switch (irq_type) {
> +               case UPPER:
> +                       index_mask  = UP_INT_MASK_0 + hw_id;
> +                       index_clear = UP_INT_CLEAR_0 + hw_id;
> +                       break;
> +               case LOWER:
> +                       index_mask  = LOW_INT_MASK_0 + hw_id;
> +                       index_clear = LOW_INT_CLEAR_0 + hw_id;
> +                       break;
> +               }

Please extract index_mask and index_clear assignments to one switch
statement and then change the sequence to an if/else

	if (enable) {
               regmap_field_write(priv->rf[index_mask], 1);
               regmap_field_write(priv->rf[index_clear], 1);
               regmap_field_write(priv->rf[index_clear], 0);
       } else {
               regmap_field_write(priv->rf[index_mask], 0);
       }

> +}
> +
> +/**
> + * tsens_set_interrupt - Disable an interrupt (enable = false)
> + *                       Re-enable an interrupt (enable = true)
> + */
> +static void tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id,
> +                               enum tsens_irq_type irq_type, bool enable)
> +{
> +       dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__,
> +               irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW",
> +               enable ? "en" : "dis");
> +       if (tsens_ver(priv) > VER_1_X)
> +               tsens_set_interrupt_v2(priv, hw_id, irq_type, enable);
> +       else
> +               tsens_set_interrupt_v1(priv, hw_id, irq_type, enable);
> +}
> +
> +/**
> + * tsens_threshold_violated - Check if a sensor temperature violated a preset threshold
> + *

Document arguments?

> + * Return: 0 if threshold was not violated, 1 if it was violated and negative
> + * errno in case of errors
> + */
> +static int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id,
> +                                   struct tsens_irq_data *d)
> +{
> +       int ret;
> +
> +       ret = regmap_field_read(priv->rf[UPPER_STATUS_0 + hw_id], &d->up_viol);
> +       if (ret)
> +               return ret;
> +       ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol);
> +       if (ret)
> +               return ret;
> +       if (d->up_viol || d->low_viol)
> +               return 1;
> +
> +       return 0;
> +}
> +
> +static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
> +                               struct tsens_sensor *s, struct tsens_irq_data *d)
> +{
> +       int ret;
> +
> +       ret = regmap_field_read(priv->rf[UP_INT_CLEAR_0 + hw_id], &d->up_irq_clear);
> +       if (ret)
> +               return ret;
> +       ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear);
> +       if (ret)
> +               return ret;
> +       if (tsens_ver(priv) > VER_1_X) {
> +               ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask);
> +               if (ret)
> +                       return ret;
> +               ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask);
> +               if (ret)
> +                       return ret;
> +       } else {
> +               /* No mask register on older TSENS */
> +               d->up_irq_mask = 0;
> +               d->low_irq_mask = 0;
> +       }
> +
> +       d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id);
> +       d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id);
> +
> +       dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u) | clr(%u|%u) | mask(%u|%u)\n",
> +               hw_id, __func__, (d->up_viol || d->low_viol) ? "(V)" : "",
> +               d->low_viol, d->up_viol, d->low_irq_clear, d->up_irq_clear,
> +               d->low_irq_mask, d->up_irq_mask);
> +       dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d)\n", hw_id, __func__,
> +               (d->up_viol || d->low_viol) ? "(violation)" : "",
> +               d->low_thresh, d->up_thresh);
> +
> +       return 0;
> +}
> +
> +static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver)
> +{
> +       if (ver > VER_1_X)
> +               return mask & (1 << hw_id);
> +
> +       /* v1, v0.1 don't have a irq mask register */
> +       return 0;
> +}
> +
> +irqreturn_t tsens_irq_thread(int irq, void *data)
> +{
> +       struct tsens_priv *priv = data;
> +       struct tsens_irq_data d;
> +       bool enable = true, disable = false;
> +       unsigned long flags;
> +       int temp, ret, i;
> +
> +       /*
> +        * Check if any sensor raised an IRQ - for each sensor connected to the
> +        * TSENS block if it set the threshold violation bit.
> +        */
> +       for (i = 0; i < priv->num_sensors; i++) {
> +               bool trigger = 0;

How about trigger = false? It's a bool.

> +               struct tsens_sensor *s = &priv->sensor[i];
> +               u32 hw_id = s->hw_id;
> +
> +               if (IS_ERR(priv->sensor[i].tzd))
> +                       continue;
> +               if (!tsens_threshold_violated(priv, hw_id, &d))
> +                       continue;
> +               ret = get_temp_tsens_valid(s, &temp);
> +               if (ret) {
> +                       dev_err(priv->dev, "[%u] %s: error reading sensor\n", hw_id, __func__);

I hope there isn't an interrupt storm where we're trying to print out
messages from the irq handler.

> +                       continue;
> +               }
> +
> +               spin_lock_irqsave(&priv->ul_lock, flags);
> +
> +               tsens_read_irq_state(priv, hw_id, s, &d);
> +
> +               if (d.up_viol &&
> +                   !masked_irq(hw_id, d.up_irq_mask, tsens_ver(priv))) {
> +                       tsens_set_interrupt(priv, hw_id, UPPER, disable);
> +                       if (d.up_thresh > temp) {
> +                               dev_dbg(priv->dev, "[%u] %s: re-arm upper\n",
> +                                       priv->sensor[i].hw_id, __func__);
> +                               /* unmask the interrupt for this sensor */
> +                               tsens_set_interrupt(priv, hw_id, UPPER, enable);
> +                       } else {
> +                               trigger = 1;
> +                               /* Keep irq masked */
> +                       }
> +               } else if (d.low_viol &&
> +                          !masked_irq(hw_id, d.low_irq_mask, tsens_ver(priv))) {
> +                       tsens_set_interrupt(priv, hw_id, LOWER, disable);
> +                       if (d.low_thresh < temp) {
> +                               dev_dbg(priv->dev, "[%u] %s: re-arm low\n",
> +                                       priv->sensor[i].hw_id, __func__);
> +                               /* unmask the interrupt for this sensor */
> +                               tsens_set_interrupt(priv, hw_id, LOWER, enable);
> +                       } else {
> +                               trigger = 1;
> +                               /* Keep irq masked */
> +                       }
> +               }
> +
> +               spin_unlock_irqrestore(&priv->ul_lock, flags);
> +
> +               if (trigger) {
> +                       dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n",
> +                               hw_id, __func__, temp);
> +                       thermal_zone_device_update(priv->sensor[i].tzd,
> +                                                  THERMAL_EVENT_UNSPECIFIED);
> +               } else {
> +                       dev_dbg(priv->dev, "[%u] %s: no violation:  %d\n",
> +                               hw_id, __func__, temp);
> +               }
> +       }
> +
> +       return IRQ_HANDLED;

Should we return IRQ_NONE in the case that the above for loop didn't
find anything in those if/else-ifs?

> +}
> +
> +int tsens_set_trips(void *_sensor, int low, int high)
> +{
> +       struct tsens_sensor *s = _sensor;
> +       struct tsens_priv *priv = s->priv;
> +       struct device *dev = priv->dev;
> +       struct tsens_irq_data d;
> +       unsigned long flags;
> +       int high_val, low_val, cl_high, cl_low;
> +       bool enable = true;
> +       u32 hw_id = s->hw_id;
> +
> +       dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n",
> +               hw_id, __func__, low, high);
> +
> +       cl_high = clamp_val(high, -40000, 120000);
> +       cl_low  = clamp_val(low, -40000, 120000);
> +
> +       high_val = tsens_mC_to_hw(s, cl_high);
> +       low_val  = tsens_mC_to_hw(s, cl_low);
> +
> +       spin_lock_irqsave(&priv->ul_lock, flags);
> +
> +       tsens_read_irq_state(priv, hw_id, s, &d);
> +
> +       /* Write the new thresholds and clear the status */
> +       regmap_field_write(priv->rf[LOW_THRESH_0 + hw_id], low_val);
> +       regmap_field_write(priv->rf[UP_THRESH_0 + hw_id], high_val);
> +       tsens_set_interrupt(priv, hw_id, LOWER, enable);
> +       tsens_set_interrupt(priv, hw_id, UPPER, enable);

Just pass true? Why is there an enable local variable?

> +
> +       spin_unlock_irqrestore(&priv->ul_lock, flags);
> +
> +       dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n",
> +               s->hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high);
> +
> +       return 0;
> +}
> +
[...]
> @@ -319,28 +659,31 @@ int __init init_common(struct tsens_priv *priv)
>                 ret = PTR_ERR(priv->rf[SENSOR_EN]);
>                 goto err_put_device;
>         }
> -       /* now alloc regmap_fields in tm_map */
> -       for (i = 0, j = LAST_TEMP_0; i < priv->feat->max_sensors; i++, j++) {
> -               priv->rf[j] = devm_regmap_field_alloc(dev, priv->tm_map,
> -                                                     priv->fields[j]);
> -               if (IS_ERR(priv->rf[j])) {
> -                       ret = PTR_ERR(priv->rf[j]);
> -                       goto err_put_device;
> -               }
> +       priv->rf[INT_EN] = devm_regmap_field_alloc(dev, priv->tm_map,
> +                                                  priv->fields[INT_EN]);
> +       if (IS_ERR(priv->rf[INT_EN])) {
> +               ret = PTR_ERR(priv->rf[INT_EN]);
> +               goto err_put_device;
>         }
>  
> -       /* Save away resolution of signed temperature value for this IP */
> -       priv->tempres = priv->fields[LAST_TEMP_0].msb - priv->fields[LAST_TEMP_0].lsb;
> -
> -       for (i = 0, j = VALID_0; i < priv->feat->max_sensors; i++, j++) {
> -               priv->rf[j] = devm_regmap_field_alloc(dev, priv->tm_map,
> -                                                     priv->fields[j]);
> -               if (IS_ERR(priv->rf[j])) {
> -                       ret = PTR_ERR(priv->rf[j]);
> -                       goto err_put_device;
> +       /* This loop might need changes if enum regfield_ids is reordered */
> +       for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) {
> +               for (i = 0; i < priv->feat->max_sensors; i++) {
> +                       int idx = j + i;
> +
> +                       priv->rf[idx] = devm_regmap_field_alloc(dev, priv->tm_map,
> +                                                               priv->fields[idx]);
> +                       if (IS_ERR(priv->rf[idx])) {
> +                               ret = PTR_ERR(priv->rf[idx]);
> +                               goto err_put_device;
> +                       }
>                 }
>         }
> +       /* Save away resolution of signed temperature value for this IP */
> +       priv->tempres = priv->fields[LAST_TEMP_0].msb - priv->fields[LAST_TEMP_0].lsb;

Leave this where it was, i.e. before the for loop? Or is that a bug and
it doesn't actually work unless it's after the for loop? In which case,
this should go to the previous patch.

>  
> +       spin_lock_init(&priv->ul_lock);
> +       tsens_enable_irq(priv);
>         tsens_debug_init(op);
>  
>         return 0;
> diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
> index 772aa76b50e12..a4335717aeede 100644
> --- a/drivers/thermal/qcom/tsens.c
> +++ b/drivers/thermal/qcom/tsens.c
> @@ -96,7 +99,33 @@ static int tsens_register(struct tsens_priv *priv)
>                 if (priv->ops->enable)
>                         priv->ops->enable(priv, i);
>         }
> +
> +       pdev = of_find_device_by_node(priv->dev->of_node);
> +       if (!pdev) {
> +               dev_err(&pdev->dev, "%s: device node not found in DT\n", __func__);

Do we really need this? Maybe just bail out in silence because this
should never happen?

> +               return -ENODEV;
> +       }
> +
> +       irq = platform_get_irq_byname(pdev, "uplow");
> +       if (irq < 0) {
> +               dev_err(&pdev->dev, "%s: missing irq in dt: uplow\n", __func__);

You can drop the error print. I upstreamed a change to print the error
generically in the core.

> +               return irq;

Did we need to put_device() here?

> +       }
> +
> +       ret = devm_request_threaded_irq(&pdev->dev, irq,
> +                                       NULL, tsens_irq_thread,
> +                                       IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
> +                                       dev_name(&pdev->dev), priv);
> +       if (ret) {
> +               dev_err(&pdev->dev, "%s: failed to get irq\n", __func__);
> +               goto err_put_device;
> +       }
> +       enable_irq_wake(irq);
>         return 0;
> +
> +err_put_device:
> +       put_device(&pdev->dev);
> +       return ret;

  reply	other threads:[~2019-08-28 21:42 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-08-27 12:13 [PATCH v2 00/15] thermal: qcom: tsens: Add interrupt support Amit Kucheria
2019-08-27 12:13 ` [PATCH v2 01/15] drivers: thermal: tsens: Get rid of id field in tsens_sensor Amit Kucheria
2019-08-27 12:13 ` [PATCH v2 02/15] drivers: thermal: tsens: Simplify code flow in tsens_probe Amit Kucheria
2019-08-27 12:13 ` [PATCH v2 03/15] drivers: thermal: tsens: Add __func__ identifier to debug statements Amit Kucheria
2019-08-29 14:04   ` Daniel Thompson
2019-08-29 14:28     ` Amit Kucheria
2019-08-29 15:19       ` Daniel Thompson
2019-08-29 16:14         ` Amit Kucheria
2019-08-29 16:34           ` Daniel Thompson
2019-08-27 12:14 ` [PATCH v2 04/15] drivers: thermal: tsens: Add debugfs support Amit Kucheria
2019-08-28  0:31   ` Stephen Boyd
2019-08-27 12:14 ` [PATCH v2 07/15] dt: thermal: tsens: Document interrupt support in tsens driver Amit Kucheria
2019-08-28  0:33   ` Stephen Boyd
2019-08-29  8:48     ` Amit Kucheria
2019-08-29 14:53       ` Stephen Boyd
2019-08-29 16:34         ` Amit Kucheria
2019-08-30 11:32           ` Amit Kucheria
2019-08-30 15:55             ` Stephen Boyd
2019-08-30 16:40               ` Amit Kucheria
2019-08-27 12:14 ` [PATCH v2 14/15] drivers: thermal: tsens: Create function to return sign-extended temperature Amit Kucheria
2019-08-28  0:38   ` Stephen Boyd
     [not found]     ` <CAP245DVGY6+vue_REqy=Tbvka2fcBx6XhSBePW4L3=pNagX=Dw@mail.gmail.com>
2019-08-28 15:44       ` Stephen Boyd
2019-08-27 12:14 ` [PATCH v2 15/15] drivers: thermal: tsens: Add interrupt support Amit Kucheria
2019-08-28 21:42   ` Stephen Boyd [this message]
2019-08-29 12:30     ` Amit Kucheria
2019-08-29 14:54       ` Stephen Boyd
2019-08-29  3:53   ` Thara Gopinath
2019-09-10 16:01     ` Amit Kucheria

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=5d66f545.1c69fb81.3663f.129d@mx.google.com \
    --to=swboyd@chromium.org \
    --cc=agross@kernel.org \
    --cc=amit.kucheria@linaro.org \
    --cc=bjorn.andersson@linaro.org \
    --cc=daniel.lezcano@linaro.org \
    --cc=edubezval@gmail.com \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=marc.w.gonzalez@free.fr \
    --cc=mark.rutland@arm.com \
    --cc=masneyb@onstation.org \
    --cc=robh+dt@kernel.org \
    --cc=rui.zhang@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).