All of lore.kernel.org
 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: 41+ 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 05/15] arm: dts: msm8974: thermal: Add thermal zones for each sensor Amit Kucheria
2019-08-28  0:32   ` Stephen Boyd
2019-08-27 12:14 ` [PATCH v2 06/15] arm64: dts: msm8916: thermal: Fixup HW ids for cpu sensors Amit Kucheria
2019-08-28  0:32   ` 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 08/15] arm64: dts: sdm845: thermal: Add interrupt support Amit Kucheria
2019-08-28  0:34   ` Stephen Boyd
2019-08-27 12:14 ` [PATCH v2 09/15] arm64: dts: msm8996: " Amit Kucheria
2019-08-28  0:35   ` Stephen Boyd
2019-08-30 20:10     ` Amit Kucheria
2019-08-27 12:14 ` [PATCH v2 10/15] arm64: dts: msm8998: " Amit Kucheria
2019-08-27 12:14 ` [PATCH v2 11/15] arm64: dts: qcs404: " Amit Kucheria
2019-08-27 12:14 ` [PATCH v2 12/15] arm: dts: msm8974: " Amit Kucheria
2019-08-27 12:14 ` [PATCH v2 13/15] arm64: dts: msm8916: " 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.