diff mbox series

[v4,5/7] drivers: thermal: tsens: add interrupt support for 9860 driver

Message ID 20200716022817.30439-6-ansuelsmth@gmail.com (mailing list archive)
State New, archived
Delegated to: Daniel Lezcano
Headers show
Series Add support for ipq8064 tsens | expand

Commit Message

Christian Marangi July 16, 2020, 2:28 a.m. UTC
Add interrupt support for 9860 tsens driver used to set thermal trip
point for the system.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/thermal/qcom/tsens-8960.c | 197 +++++++++++++++++++++++++++---
 drivers/thermal/qcom/tsens.h      |   3 +
 2 files changed, 186 insertions(+), 14 deletions(-)

Comments

kernel test robot July 17, 2020, 2:24 a.m. UTC | #1
Hi Ansuel,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on robh/for-next]
[also build test WARNING on clk/clk-next linus/master v5.8-rc5 next-20200716]
[cannot apply to thermal/next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Ansuel-Smith/Add-support-for-ipq8064-tsens/20200716-103106
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
config: arm64-randconfig-r004-20200716 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project ed6b578040a85977026c93bf4188f996148f3218)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install arm64 cross compiling tool for clang build
        # apt-get install binutils-aarch64-linux-gnu
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=arm64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/thermal/qcom/tsens-8960.c:216:14: warning: signed shift result (0x9B0000000) requires 37 bits to represent, but 'int' only has 32 bits [-Wshift-overflow]
                             (CONFIG << CONFIG_SHIFT_8660);
                              ~~~~~~ ^  ~~~~~~~~~~~~~~~~~
   1 warning generated.

vim +/int +216 drivers/thermal/qcom/tsens-8960.c

   191	
   192	static void hw_init(struct tsens_priv *priv)
   193	{
   194		int ret;
   195		unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0;
   196		unsigned int reg_status_cntl = 0;
   197	
   198		regmap_read(priv->tm_map, CNTL_ADDR, &reg_cntl);
   199		regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl | TSENS_SW_RST);
   200	
   201		reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18) |
   202			    (((1 << priv->num_sensors) - 1) << SENSOR0_SHIFT);
   203		regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
   204		regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064, &reg_status_cntl);
   205		reg_status_cntl |= LOWER_STATUS_CLR | UPPER_STATUS_CLR |
   206				   MIN_STATUS_MASK | MAX_STATUS_MASK;
   207		regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064, reg_status_cntl);
   208		reg_cntl |= TSENS_EN;
   209		regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
   210	
   211		regmap_read(priv->tm_map, CONFIG_ADDR, &reg_cfg);
   212		if (priv->num_sensors > 1)
   213			reg_cfg = (reg_cfg & ~CONFIG_MASK) | CONFIG;
   214		else
   215			reg_cfg = (reg_cfg & ~CONFIG_MASK) |
 > 216				  (CONFIG << CONFIG_SHIFT_8660);
   217		regmap_write(priv->tm_map, CONFIG_ADDR, reg_cfg);
   218	
   219		reg_thr |= (LOWER_LIMIT_TH_8064 << THRESHOLD_LOWER_LIMIT_SHIFT) |
   220			   (UPPER_LIMIT_TH_8064 << THRESHOLD_UPPER_LIMIT_SHIFT) |
   221			   (MIN_LIMIT_TH << THRESHOLD_MIN_LIMIT_SHIFT) |
   222			   (MAX_LIMIT_TH << THRESHOLD_MAX_LIMIT_SHIFT);
   223	
   224		regmap_write(priv->tm_map, THRESHOLD_ADDR, reg_thr);
   225	
   226		ret = devm_request_irq(priv->dev, priv->tsens_irq, tsens_isr,
   227				       IRQF_TRIGGER_RISING, "tsens_interrupt", priv);
   228		if (ret < 0) {
   229			dev_err(priv->dev, "request_irq FAIL: %d", ret);
   230			return;
   231		}
   232	
   233		INIT_WORK(&priv->tsens_work, tsens_scheduler_fn);
   234	}
   235	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Amit Kucheria July 20, 2020, 9:41 a.m. UTC | #2
Hi Ansuel,

Thanks for this patch.

On Thu, Jul 16, 2020 at 7:58 AM Ansuel Smith <ansuelsmth@gmail.com> wrote:
>
> Add interrupt support for 9860 tsens driver used to set thermal trip
> point for the system.

typo: 8960

You've used the names 8960 and ipq8064 interchangeably throughout the
series. AFAICT, msm8960, ipq8064 and apq8064 use the same IP version
of tsens. Please use 8960 in all patches, descriptions and dt-binding.
to reflect the filename for the driver.
Then add ipq8064 and apq8064 in a comment in the driver like here to
show that the driver also supports these other SoCs:
https://elixir.bootlin.com/linux/v5.8-rc4/source/drivers/thermal/qcom/tsens-v0_1.c#L328

You can also add a new compatible string for ipq8064 as a separate
patch at the end of the series.

> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---
>  drivers/thermal/qcom/tsens-8960.c | 197 +++++++++++++++++++++++++++---
>  drivers/thermal/qcom/tsens.h      |   3 +
>  2 files changed, 186 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c
> index 45788eb3c666..20d0bfb10f1f 100644
> --- a/drivers/thermal/qcom/tsens-8960.c
> +++ b/drivers/thermal/qcom/tsens-8960.c
> @@ -8,6 +8,7 @@
>  #include <linux/bitops.h>
>  #include <linux/regmap.h>
>  #include <linux/mfd/syscon.h>
> +#include <linux/interrupt.h>
>  #include <linux/thermal.h>
>  #include "tsens.h"
>
> @@ -27,7 +28,6 @@
>  /* CNTL_ADDR bitmasks */
>  #define EN                     BIT(0)
>  #define SW_RST                 BIT(1)
> -#define SENSOR0_EN             BIT(3)
>  #define SLP_CLK_ENA            BIT(26)
>  #define SLP_CLK_ENA_8660       BIT(24)
>  #define MEASURE_PERIOD         1
> @@ -41,14 +41,26 @@
>
>  #define THRESHOLD_ADDR         0x3624
>  /* THRESHOLD_ADDR bitmasks */
> +#define THRESHOLD_MAX_CODE             0x20000
> +#define THRESHOLD_MIN_CODE             0
>  #define THRESHOLD_MAX_LIMIT_SHIFT      24
>  #define THRESHOLD_MIN_LIMIT_SHIFT      16
>  #define THRESHOLD_UPPER_LIMIT_SHIFT    8
>  #define THRESHOLD_LOWER_LIMIT_SHIFT    0
> +#define THRESHOLD_MAX_LIMIT_MASK       (THRESHOLD_MAX_CODE << \
> +                                               THRESHOLD_MAX_LIMIT_SHIFT)
> +#define THRESHOLD_MIN_LIMIT_MASK       (THRESHOLD_MAX_CODE << \
> +                                               THRESHOLD_MIN_LIMIT_SHIFT)
> +#define THRESHOLD_UPPER_LIMIT_MASK     (THRESHOLD_MAX_CODE << \
> +                                               THRESHOLD_UPPER_LIMIT_SHIFT)
> +#define THRESHOLD_LOWER_LIMIT_MASK     (THRESHOLD_MAX_CODE << \
> +                                               THRESHOLD_LOWER_LIMIT_SHIFT)
>
>  /* Initial temperature threshold values */
> -#define LOWER_LIMIT_TH         0x50
> -#define UPPER_LIMIT_TH         0xdf
> +#define LOWER_LIMIT_TH_8960    0x50
> +#define UPPER_LIMIT_TH_8960    0xdf
> +#define LOWER_LIMIT_TH_8064    0x9d /* 95C */
> +#define UPPER_LIMIT_TH_8064    0xa6 /* 105C */
>  #define MIN_LIMIT_TH           0x0
>  #define MAX_LIMIT_TH           0xff
>
> @@ -57,6 +69,170 @@
>  #define TRDY_MASK              BIT(7)
>  #define TIMEOUT_US             100
>
> +#define TSENS_EN               BIT(0)
> +#define TSENS_SW_RST           BIT(1)
> +#define TSENS_ADC_CLK_SEL      BIT(2)
> +#define SENSOR0_EN             BIT(3)
> +#define SENSOR1_EN             BIT(4)
> +#define SENSOR2_EN             BIT(5)
> +#define SENSOR3_EN             BIT(6)
> +#define SENSOR4_EN             BIT(7)
> +#define SENSORS_EN             (SENSOR0_EN | SENSOR1_EN | \
> +                               SENSOR2_EN | SENSOR3_EN | SENSOR4_EN)
> +#define TSENS_8064_SENSOR5_EN                          BIT(8)
> +#define TSENS_8064_SENSOR6_EN                          BIT(9)
> +#define TSENS_8064_SENSOR7_EN                          BIT(10)
> +#define TSENS_8064_SENSOR8_EN                          BIT(11)
> +#define TSENS_8064_SENSOR9_EN                          BIT(12)
> +#define TSENS_8064_SENSOR10_EN                         BIT(13)
> +#define TSENS_8064_SENSORS_EN                          (SENSORS_EN | \
> +                                               TSENS_8064_SENSOR5_EN | \
> +                                               TSENS_8064_SENSOR6_EN | \
> +                                               TSENS_8064_SENSOR7_EN | \
> +                                               TSENS_8064_SENSOR8_EN | \
> +                                               TSENS_8064_SENSOR9_EN | \
> +                                               TSENS_8064_SENSOR10_EN)
> +
> +u32 tsens_8960_slope[] = {
> +                       1176, 1176, 1154, 1176,
> +                       1111, 1132, 1132, 1199,
> +                       1132, 1199, 1132
> +                       };
> +
> +/* Temperature on y axis and ADC-code on x-axis */
> +static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s)
> +{
> +       int slope, offset;
> +
> +       slope = thermal_zone_get_slope(s->tzd);
> +       offset = CAL_MDEGC - slope * s->offset;
> +
> +       return adc_code * slope + offset;
> +}
> +
> +static void notify_uspace_tsens_fn(struct work_struct *work)
> +{
> +       struct tsens_sensor *s = container_of(work, struct tsens_sensor,
> +                                                               notify_work);
> +
> +       sysfs_notify(&s->tzd->device.kobj, NULL, "type");
> +}
> +
> +static void tsens_scheduler_fn(struct work_struct *work)
> +{
> +       struct tsens_priv *priv =
> +               container_of(work, struct tsens_priv, tsens_work);
> +       unsigned int threshold, threshold_low, code, reg, sensor;
> +       unsigned long mask;
> +       bool upper_th_x, lower_th_x;
> +       int ret;
> +
> +       ret = regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064, &reg);
> +       if (ret)
> +               return;
> +       reg = reg | LOWER_STATUS_CLR | UPPER_STATUS_CLR;
> +       ret = regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064, reg);
> +       if (ret)
> +               return;
> +
> +       mask = ~(LOWER_STATUS_CLR | UPPER_STATUS_CLR);
> +       ret = regmap_read(priv->tm_map, THRESHOLD_ADDR, &threshold);
> +       if (ret)
> +               return;
> +       threshold_low = (threshold & THRESHOLD_LOWER_LIMIT_MASK) >>
> +                       THRESHOLD_LOWER_LIMIT_SHIFT;
> +       threshold = (threshold & THRESHOLD_UPPER_LIMIT_MASK) >>
> +                   THRESHOLD_UPPER_LIMIT_SHIFT;
> +
> +       ret = regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064, &reg);
> +       if (ret)
> +               return;
> +
> +       ret = regmap_read(priv->tm_map, CNTL_ADDR, &sensor);
> +       if (ret)
> +               return;
> +       sensor &= (uint32_t)TSENS_8064_SENSORS_EN;
> +       sensor >>= SENSOR0_SHIFT;
> +
> +       /* Constraint: There is only 1 interrupt control register for all
> +        * 11 temperature sensor. So monitoring more than 1 sensor based
> +        * on interrupts will yield inconsistent result. To overcome this
> +        * issue we will monitor only sensor 0 which is the master sensor.
> +        */
> +
> +       /* Skip if the sensor is disabled */
> +       if (sensor & 1) {
> +               ret = regmap_read(priv->tm_map, priv->sensor[0].status, &code);
> +               if (ret)
> +                       return;
> +               upper_th_x = code >= threshold;
> +               lower_th_x = code <= threshold_low;
> +               if (upper_th_x)
> +                       mask |= UPPER_STATUS_CLR;
> +               if (lower_th_x)
> +                       mask |= LOWER_STATUS_CLR;
> +               if (upper_th_x || lower_th_x) {
> +                       /* Notify user space */
> +                       schedule_work(&priv->sensor[0].notify_work);
> +                       pr_debug("Trigger (%d degrees) for sensor %d\n",
> +                                code_to_mdegC(code, &priv->sensor[0]), 0);
> +               }
> +       }
> +       regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064, reg & mask);
> +}
> +
> +static irqreturn_t tsens_isr(int irq, void *data)
> +{
> +       struct tsens_priv *priv = data;
> +
> +       schedule_work(&priv->tsens_work);
> +       return IRQ_HANDLED;


Have you considered trying to reuse the regmap and interrupt handling
infrastructure in tsens.c that I used to convert over everything after
IP version 0.1?

I started converting over 8960 but never managed to finish testing
this[1]. I'd be happy for you to take this over and get it working so
the 8960 doesn't end up being a completely separate driver from the
other platforms.

[1] https://git.linaro.org/people/amit.kucheria/kernel.git/log/?h=wrk3/tsens-8960-breakage

> +}
> +
> +static void hw_init(struct tsens_priv *priv)
> +{
> +       int ret;
> +       unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0;
> +       unsigned int reg_status_cntl = 0;
> +
> +       regmap_read(priv->tm_map, CNTL_ADDR, &reg_cntl);
> +       regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl | TSENS_SW_RST);
> +
> +       reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18) |
> +                   (((1 << priv->num_sensors) - 1) << SENSOR0_SHIFT);
> +       regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
> +       regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064, &reg_status_cntl);
> +       reg_status_cntl |= LOWER_STATUS_CLR | UPPER_STATUS_CLR |
> +                          MIN_STATUS_MASK | MAX_STATUS_MASK;
> +       regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064, reg_status_cntl);
> +       reg_cntl |= TSENS_EN;
> +       regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
> +
> +       regmap_read(priv->tm_map, CONFIG_ADDR, &reg_cfg);
> +       if (priv->num_sensors > 1)
> +               reg_cfg = (reg_cfg & ~CONFIG_MASK) | CONFIG;
> +       else
> +               reg_cfg = (reg_cfg & ~CONFIG_MASK) |
> +                         (CONFIG << CONFIG_SHIFT_8660);
> +       regmap_write(priv->tm_map, CONFIG_ADDR, reg_cfg);
> +
> +       reg_thr |= (LOWER_LIMIT_TH_8064 << THRESHOLD_LOWER_LIMIT_SHIFT) |
> +                  (UPPER_LIMIT_TH_8064 << THRESHOLD_UPPER_LIMIT_SHIFT) |
> +                  (MIN_LIMIT_TH << THRESHOLD_MIN_LIMIT_SHIFT) |
> +                  (MAX_LIMIT_TH << THRESHOLD_MAX_LIMIT_SHIFT);
> +
> +       regmap_write(priv->tm_map, THRESHOLD_ADDR, reg_thr);
> +
> +       ret = devm_request_irq(priv->dev, priv->tsens_irq, tsens_isr,
> +                              IRQF_TRIGGER_RISING, "tsens_interrupt", priv);
> +       if (ret < 0) {
> +               dev_err(priv->dev, "request_irq FAIL: %d", ret);
> +               return;
> +       }
> +
> +       INIT_WORK(&priv->tsens_work, tsens_scheduler_fn);
> +}
> +
>  static int suspend_8960(struct tsens_priv *priv)
>  {
>         int ret;
> @@ -191,6 +367,8 @@ static int init_8960(struct tsens_priv *priv)
>                 if (i >= 5)
>                         priv->sensor[i].status = S0_STATUS_ADDR + 40;
>                 priv->sensor[i].status += i * 4;
> +               priv->sensor[i].slope = tsens_8960_slope[i];
> +               INIT_WORK(&priv->sensor[i].notify_work, notify_uspace_tsens_fn);
>         }
>
>         reg_cntl = SW_RST;
> @@ -241,18 +419,9 @@ static int calibrate_8960(struct tsens_priv *priv)
>
>         kfree(data);
>
> -       return 0;
> -}
> -
> -/* Temperature on y axis and ADC-code on x-axis */
> -static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s)
> -{
> -       int slope, offset;
> +       hw_init(priv);
>
> -       slope = thermal_zone_get_slope(s->tzd);
> -       offset = CAL_MDEGC - slope * s->offset;
> -
> -       return adc_code * slope + offset;

This code move hunk belongs in a separate patch.

> +       return 0;
>  }
>
>  static int get_temp_8960(const struct tsens_sensor *s, int *temp)
> diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
> index 59d01162c66a..e66048fabcc7 100644
> --- a/drivers/thermal/qcom/tsens.h
> +++ b/drivers/thermal/qcom/tsens.h
> @@ -48,6 +48,7 @@ enum tsens_irq_type {
>  struct tsens_sensor {
>         struct tsens_priv               *priv;
>         struct thermal_zone_device      *tzd;
> +       struct work_struct              notify_work;
>         int                             offset;
>         unsigned int                    hw_id;
>         int                             slope;
> @@ -559,6 +560,7 @@ struct tsens_priv {
>         struct regmap                   *tm_map;
>         struct regmap                   *srot_map;
>         u32                             tm_offset;
> +       u32                             tsens_irq;
>
>         /* lock for upper/lower threshold interrupts */
>         spinlock_t                      ul_lock;
> @@ -568,6 +570,7 @@ struct tsens_priv {
>         struct tsens_features           *feat;
>         const struct reg_field          *fields;
>         const struct tsens_ops          *ops;
> +       struct work_struct              tsens_work;
>
>         struct dentry                   *debug_root;
>         struct dentry                   *debug;
> --
> 2.27.0
>
Christian Marangi July 21, 2020, 7:22 p.m. UTC | #3
> -----Messaggio originale-----
> Da: Amit Kucheria <amit.kucheria@linaro.org>
> Inviato: lunedì 20 luglio 2020 11:41
> A: Ansuel Smith <ansuelsmth@gmail.com>
> Cc: Rob Herring <robh+dt@kernel.org>; Andy Gross <agross@kernel.org>;
> Bjorn Andersson <bjorn.andersson@linaro.org>; Zhang Rui
> <rui.zhang@intel.com>; Daniel Lezcano <daniel.lezcano@linaro.org>;
> Michael Turquette <mturquette@baylibre.com>; Stephen Boyd
> <sboyd@kernel.org>; Linux PM list <linux-pm@vger.kernel.org>; linux-arm-
> msm <linux-arm-msm@vger.kernel.org>; DTML
> <devicetree@vger.kernel.org>; Linux Kernel Mailing List <linux-
> kernel@vger.kernel.org>; linux-clk <linux-clk@vger.kernel.org>
> Oggetto: Re: [PATCH v4 5/7] drivers: thermal: tsens: add interrupt support
> for 9860 driver
> 
> Hi Ansuel,
> 
> Thanks for this patch.
> 
> On Thu, Jul 16, 2020 at 7:58 AM Ansuel Smith <ansuelsmth@gmail.com>
> wrote:
> >
> > Add interrupt support for 9860 tsens driver used to set thermal trip
> > point for the system.
> 
> typo: 8960
> 
> You've used the names 8960 and ipq8064 interchangeably throughout the
> series. AFAICT, msm8960, ipq8064 and apq8064 use the same IP version
> of tsens. Please use 8960 in all patches, descriptions and dt-binding.
> to reflect the filename for the driver.
> Then add ipq8064 and apq8064 in a comment in the driver like here to
> show that the driver also supports these other SoCs:
> https://elixir.bootlin.com/linux/v5.8-
> rc4/source/drivers/thermal/qcom/tsens-v0_1.c#L328
> 
> You can also add a new compatible string for ipq8064 as a separate
> patch at the end of the series.
> 
> > Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> > ---
> >  drivers/thermal/qcom/tsens-8960.c | 197
> +++++++++++++++++++++++++++---
> >  drivers/thermal/qcom/tsens.h      |   3 +
> >  2 files changed, 186 insertions(+), 14 deletions(-)
> >
> > diff --git a/drivers/thermal/qcom/tsens-8960.c
> b/drivers/thermal/qcom/tsens-8960.c
> > index 45788eb3c666..20d0bfb10f1f 100644
> > --- a/drivers/thermal/qcom/tsens-8960.c
> > +++ b/drivers/thermal/qcom/tsens-8960.c
> > @@ -8,6 +8,7 @@
> >  #include <linux/bitops.h>
> >  #include <linux/regmap.h>
> >  #include <linux/mfd/syscon.h>
> > +#include <linux/interrupt.h>
> >  #include <linux/thermal.h>
> >  #include "tsens.h"
> >
> > @@ -27,7 +28,6 @@
> >  /* CNTL_ADDR bitmasks */
> >  #define EN                     BIT(0)
> >  #define SW_RST                 BIT(1)
> > -#define SENSOR0_EN             BIT(3)
> >  #define SLP_CLK_ENA            BIT(26)
> >  #define SLP_CLK_ENA_8660       BIT(24)
> >  #define MEASURE_PERIOD         1
> > @@ -41,14 +41,26 @@
> >
> >  #define THRESHOLD_ADDR         0x3624
> >  /* THRESHOLD_ADDR bitmasks */
> > +#define THRESHOLD_MAX_CODE             0x20000
> > +#define THRESHOLD_MIN_CODE             0
> >  #define THRESHOLD_MAX_LIMIT_SHIFT      24
> >  #define THRESHOLD_MIN_LIMIT_SHIFT      16
> >  #define THRESHOLD_UPPER_LIMIT_SHIFT    8
> >  #define THRESHOLD_LOWER_LIMIT_SHIFT    0
> > +#define THRESHOLD_MAX_LIMIT_MASK       (THRESHOLD_MAX_CODE
> << \
> > +                                               THRESHOLD_MAX_LIMIT_SHIFT)
> > +#define THRESHOLD_MIN_LIMIT_MASK       (THRESHOLD_MAX_CODE <<
> \
> > +                                               THRESHOLD_MIN_LIMIT_SHIFT)
> > +#define THRESHOLD_UPPER_LIMIT_MASK     (THRESHOLD_MAX_CODE
> << \
> > +                                               THRESHOLD_UPPER_LIMIT_SHIFT)
> > +#define THRESHOLD_LOWER_LIMIT_MASK     (THRESHOLD_MAX_CODE
> << \
> > +                                               THRESHOLD_LOWER_LIMIT_SHIFT)
> >
> >  /* Initial temperature threshold values */
> > -#define LOWER_LIMIT_TH         0x50
> > -#define UPPER_LIMIT_TH         0xdf
> > +#define LOWER_LIMIT_TH_8960    0x50
> > +#define UPPER_LIMIT_TH_8960    0xdf
> > +#define LOWER_LIMIT_TH_8064    0x9d /* 95C */
> > +#define UPPER_LIMIT_TH_8064    0xa6 /* 105C */
> >  #define MIN_LIMIT_TH           0x0
> >  #define MAX_LIMIT_TH           0xff
> >
> > @@ -57,6 +69,170 @@
> >  #define TRDY_MASK              BIT(7)
> >  #define TIMEOUT_US             100
> >
> > +#define TSENS_EN               BIT(0)
> > +#define TSENS_SW_RST           BIT(1)
> > +#define TSENS_ADC_CLK_SEL      BIT(2)
> > +#define SENSOR0_EN             BIT(3)
> > +#define SENSOR1_EN             BIT(4)
> > +#define SENSOR2_EN             BIT(5)
> > +#define SENSOR3_EN             BIT(6)
> > +#define SENSOR4_EN             BIT(7)
> > +#define SENSORS_EN             (SENSOR0_EN | SENSOR1_EN | \
> > +                               SENSOR2_EN | SENSOR3_EN | SENSOR4_EN)
> > +#define TSENS_8064_SENSOR5_EN                          BIT(8)
> > +#define TSENS_8064_SENSOR6_EN                          BIT(9)
> > +#define TSENS_8064_SENSOR7_EN                          BIT(10)
> > +#define TSENS_8064_SENSOR8_EN                          BIT(11)
> > +#define TSENS_8064_SENSOR9_EN                          BIT(12)
> > +#define TSENS_8064_SENSOR10_EN                         BIT(13)
> > +#define TSENS_8064_SENSORS_EN                          (SENSORS_EN | \
> > +                                               TSENS_8064_SENSOR5_EN | \
> > +                                               TSENS_8064_SENSOR6_EN | \
> > +                                               TSENS_8064_SENSOR7_EN | \
> > +                                               TSENS_8064_SENSOR8_EN | \
> > +                                               TSENS_8064_SENSOR9_EN | \
> > +                                               TSENS_8064_SENSOR10_EN)
> > +
> > +u32 tsens_8960_slope[] = {
> > +                       1176, 1176, 1154, 1176,
> > +                       1111, 1132, 1132, 1199,
> > +                       1132, 1199, 1132
> > +                       };
> > +
> > +/* Temperature on y axis and ADC-code on x-axis */
> > +static inline int code_to_mdegC(u32 adc_code, const struct
> tsens_sensor *s)
> > +{
> > +       int slope, offset;
> > +
> > +       slope = thermal_zone_get_slope(s->tzd);
> > +       offset = CAL_MDEGC - slope * s->offset;
> > +
> > +       return adc_code * slope + offset;
> > +}
> > +
> > +static void notify_uspace_tsens_fn(struct work_struct *work)
> > +{
> > +       struct tsens_sensor *s = container_of(work, struct tsens_sensor,
> > +                                                               notify_work);
> > +
> > +       sysfs_notify(&s->tzd->device.kobj, NULL, "type");
> > +}
> > +
> > +static void tsens_scheduler_fn(struct work_struct *work)
> > +{
> > +       struct tsens_priv *priv =
> > +               container_of(work, struct tsens_priv, tsens_work);
> > +       unsigned int threshold, threshold_low, code, reg, sensor;
> > +       unsigned long mask;
> > +       bool upper_th_x, lower_th_x;
> > +       int ret;
> > +
> > +       ret = regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064,
> &reg);
> > +       if (ret)
> > +               return;
> > +       reg = reg | LOWER_STATUS_CLR | UPPER_STATUS_CLR;
> > +       ret = regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064, reg);
> > +       if (ret)
> > +               return;
> > +
> > +       mask = ~(LOWER_STATUS_CLR | UPPER_STATUS_CLR);
> > +       ret = regmap_read(priv->tm_map, THRESHOLD_ADDR, &threshold);
> > +       if (ret)
> > +               return;
> > +       threshold_low = (threshold & THRESHOLD_LOWER_LIMIT_MASK) >>
> > +                       THRESHOLD_LOWER_LIMIT_SHIFT;
> > +       threshold = (threshold & THRESHOLD_UPPER_LIMIT_MASK) >>
> > +                   THRESHOLD_UPPER_LIMIT_SHIFT;
> > +
> > +       ret = regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064,
> &reg);
> > +       if (ret)
> > +               return;
> > +
> > +       ret = regmap_read(priv->tm_map, CNTL_ADDR, &sensor);
> > +       if (ret)
> > +               return;
> > +       sensor &= (uint32_t)TSENS_8064_SENSORS_EN;
> > +       sensor >>= SENSOR0_SHIFT;
> > +
> > +       /* Constraint: There is only 1 interrupt control register for all
> > +        * 11 temperature sensor. So monitoring more than 1 sensor based
> > +        * on interrupts will yield inconsistent result. To overcome this
> > +        * issue we will monitor only sensor 0 which is the master sensor.
> > +        */
> > +
> > +       /* Skip if the sensor is disabled */
> > +       if (sensor & 1) {
> > +               ret = regmap_read(priv->tm_map, priv->sensor[0].status,
> &code);
> > +               if (ret)
> > +                       return;
> > +               upper_th_x = code >= threshold;
> > +               lower_th_x = code <= threshold_low;
> > +               if (upper_th_x)
> > +                       mask |= UPPER_STATUS_CLR;
> > +               if (lower_th_x)
> > +                       mask |= LOWER_STATUS_CLR;
> > +               if (upper_th_x || lower_th_x) {
> > +                       /* Notify user space */
> > +                       schedule_work(&priv->sensor[0].notify_work);
> > +                       pr_debug("Trigger (%d degrees) for sensor %d\n",
> > +                                code_to_mdegC(code, &priv->sensor[0]), 0);
> > +               }
> > +       }
> > +       regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064, reg &
> mask);
> > +}
> > +
> > +static irqreturn_t tsens_isr(int irq, void *data)
> > +{
> > +       struct tsens_priv *priv = data;
> > +
> > +       schedule_work(&priv->tsens_work);
> > +       return IRQ_HANDLED;
> 
> 
> Have you considered trying to reuse the regmap and interrupt handling
> infrastructure in tsens.c that I used to convert over everything after
> IP version 0.1?
> 
> I started converting over 8960 but never managed to finish testing
> this[1]. I'd be happy for you to take this over and get it working so
> the 8960 doesn't end up being a completely separate driver from the
> other platforms.
> 
> [1]
> https://git.linaro.org/people/amit.kucheria/kernel.git/log/?h=wrk3/tsens-
> 8960-breakage
> 

Thanks a lot for the link. I started doing some test and I think the only general
code we will be able to use will be the init_common. The get temp and 
the function to convert code to decg are very different from the one used in 
8960.  Do you think keep a custom get temp function is good or not?


> > +}
> > +
> > +static void hw_init(struct tsens_priv *priv)
> > +{
> > +       int ret;
> > +       unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0;
> > +       unsigned int reg_status_cntl = 0;
> > +
> > +       regmap_read(priv->tm_map, CNTL_ADDR, &reg_cntl);
> > +       regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl |
> TSENS_SW_RST);
> > +
> > +       reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18) |
> > +                   (((1 << priv->num_sensors) - 1) << SENSOR0_SHIFT);
> > +       regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
> > +       regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064,
> &reg_status_cntl);
> > +       reg_status_cntl |= LOWER_STATUS_CLR | UPPER_STATUS_CLR |
> > +                          MIN_STATUS_MASK | MAX_STATUS_MASK;
> > +       regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064,
> reg_status_cntl);
> > +       reg_cntl |= TSENS_EN;
> > +       regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
> > +
> > +       regmap_read(priv->tm_map, CONFIG_ADDR, &reg_cfg);
> > +       if (priv->num_sensors > 1)
> > +               reg_cfg = (reg_cfg & ~CONFIG_MASK) | CONFIG;
> > +       else
> > +               reg_cfg = (reg_cfg & ~CONFIG_MASK) |
> > +                         (CONFIG << CONFIG_SHIFT_8660);
> > +       regmap_write(priv->tm_map, CONFIG_ADDR, reg_cfg);
> > +
> > +       reg_thr |= (LOWER_LIMIT_TH_8064 <<
> THRESHOLD_LOWER_LIMIT_SHIFT) |
> > +                  (UPPER_LIMIT_TH_8064 << THRESHOLD_UPPER_LIMIT_SHIFT)
> |
> > +                  (MIN_LIMIT_TH << THRESHOLD_MIN_LIMIT_SHIFT) |
> > +                  (MAX_LIMIT_TH << THRESHOLD_MAX_LIMIT_SHIFT);
> > +
> > +       regmap_write(priv->tm_map, THRESHOLD_ADDR, reg_thr);
> > +
> > +       ret = devm_request_irq(priv->dev, priv->tsens_irq, tsens_isr,
> > +                              IRQF_TRIGGER_RISING, "tsens_interrupt", priv);
> > +       if (ret < 0) {
> > +               dev_err(priv->dev, "request_irq FAIL: %d", ret);
> > +               return;
> > +       }
> > +
> > +       INIT_WORK(&priv->tsens_work, tsens_scheduler_fn);
> > +}
> > +
> >  static int suspend_8960(struct tsens_priv *priv)
> >  {
> >         int ret;
> > @@ -191,6 +367,8 @@ static int init_8960(struct tsens_priv *priv)
> >                 if (i >= 5)
> >                         priv->sensor[i].status = S0_STATUS_ADDR + 40;
> >                 priv->sensor[i].status += i * 4;
> > +               priv->sensor[i].slope = tsens_8960_slope[i];
> > +               INIT_WORK(&priv->sensor[i].notify_work,
> notify_uspace_tsens_fn);
> >         }
> >
> >         reg_cntl = SW_RST;
> > @@ -241,18 +419,9 @@ static int calibrate_8960(struct tsens_priv
> *priv)
> >
> >         kfree(data);
> >
> > -       return 0;
> > -}
> > -
> > -/* Temperature on y axis and ADC-code on x-axis */
> > -static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor
> *s)
> > -{
> > -       int slope, offset;
> > +       hw_init(priv);
> >
> > -       slope = thermal_zone_get_slope(s->tzd);
> > -       offset = CAL_MDEGC - slope * s->offset;
> > -
> > -       return adc_code * slope + offset;
> 
> This code move hunk belongs in a separate patch.
> 
> > +       return 0;
> >  }
> >
> >  static int get_temp_8960(const struct tsens_sensor *s, int *temp)
> > diff --git a/drivers/thermal/qcom/tsens.h
> b/drivers/thermal/qcom/tsens.h
> > index 59d01162c66a..e66048fabcc7 100644
> > --- a/drivers/thermal/qcom/tsens.h
> > +++ b/drivers/thermal/qcom/tsens.h
> > @@ -48,6 +48,7 @@ enum tsens_irq_type {
> >  struct tsens_sensor {
> >         struct tsens_priv               *priv;
> >         struct thermal_zone_device      *tzd;
> > +       struct work_struct              notify_work;
> >         int                             offset;
> >         unsigned int                    hw_id;
> >         int                             slope;
> > @@ -559,6 +560,7 @@ struct tsens_priv {
> >         struct regmap                   *tm_map;
> >         struct regmap                   *srot_map;
> >         u32                             tm_offset;
> > +       u32                             tsens_irq;
> >
> >         /* lock for upper/lower threshold interrupts */
> >         spinlock_t                      ul_lock;
> > @@ -568,6 +570,7 @@ struct tsens_priv {
> >         struct tsens_features           *feat;
> >         const struct reg_field          *fields;
> >         const struct tsens_ops          *ops;
> > +       struct work_struct              tsens_work;
> >
> >         struct dentry                   *debug_root;
> >         struct dentry                   *debug;
> > --
> > 2.27.0
> >
Amit Kucheria July 21, 2020, 7:42 p.m. UTC | #4
On Wed, Jul 22, 2020 at 12:52 AM <ansuelsmth@gmail.com> wrote:
>
>
>
> > -----Messaggio originale-----
> > Da: Amit Kucheria <amit.kucheria@linaro.org>
> > Inviato: lunedì 20 luglio 2020 11:41
> > A: Ansuel Smith <ansuelsmth@gmail.com>
> > Cc: Rob Herring <robh+dt@kernel.org>; Andy Gross <agross@kernel.org>;
> > Bjorn Andersson <bjorn.andersson@linaro.org>; Zhang Rui
> > <rui.zhang@intel.com>; Daniel Lezcano <daniel.lezcano@linaro.org>;
> > Michael Turquette <mturquette@baylibre.com>; Stephen Boyd
> > <sboyd@kernel.org>; Linux PM list <linux-pm@vger.kernel.org>; linux-arm-
> > msm <linux-arm-msm@vger.kernel.org>; DTML
> > <devicetree@vger.kernel.org>; Linux Kernel Mailing List <linux-
> > kernel@vger.kernel.org>; linux-clk <linux-clk@vger.kernel.org>
> > Oggetto: Re: [PATCH v4 5/7] drivers: thermal: tsens: add interrupt support
> > for 9860 driver
> >
> > Hi Ansuel,
> >
> > Thanks for this patch.
> >
> > On Thu, Jul 16, 2020 at 7:58 AM Ansuel Smith <ansuelsmth@gmail.com>
> > wrote:
> > >
> > > Add interrupt support for 9860 tsens driver used to set thermal trip
> > > point for the system.
> >
> > typo: 8960
> >
> > You've used the names 8960 and ipq8064 interchangeably throughout the
> > series. AFAICT, msm8960, ipq8064 and apq8064 use the same IP version
> > of tsens. Please use 8960 in all patches, descriptions and dt-binding.
> > to reflect the filename for the driver.
> > Then add ipq8064 and apq8064 in a comment in the driver like here to
> > show that the driver also supports these other SoCs:
> > https://elixir.bootlin.com/linux/v5.8-
> > rc4/source/drivers/thermal/qcom/tsens-v0_1.c#L328
> >
> > You can also add a new compatible string for ipq8064 as a separate
> > patch at the end of the series.
> >
> > > Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> > > ---
> > >  drivers/thermal/qcom/tsens-8960.c | 197
> > +++++++++++++++++++++++++++---
> > >  drivers/thermal/qcom/tsens.h      |   3 +
> > >  2 files changed, 186 insertions(+), 14 deletions(-)
> > >
> > > diff --git a/drivers/thermal/qcom/tsens-8960.c
> > b/drivers/thermal/qcom/tsens-8960.c
> > > index 45788eb3c666..20d0bfb10f1f 100644
> > > --- a/drivers/thermal/qcom/tsens-8960.c
> > > +++ b/drivers/thermal/qcom/tsens-8960.c
> > > @@ -8,6 +8,7 @@
> > >  #include <linux/bitops.h>
> > >  #include <linux/regmap.h>
> > >  #include <linux/mfd/syscon.h>
> > > +#include <linux/interrupt.h>
> > >  #include <linux/thermal.h>
> > >  #include "tsens.h"
> > >
> > > @@ -27,7 +28,6 @@
> > >  /* CNTL_ADDR bitmasks */
> > >  #define EN                     BIT(0)
> > >  #define SW_RST                 BIT(1)
> > > -#define SENSOR0_EN             BIT(3)
> > >  #define SLP_CLK_ENA            BIT(26)
> > >  #define SLP_CLK_ENA_8660       BIT(24)
> > >  #define MEASURE_PERIOD         1
> > > @@ -41,14 +41,26 @@
> > >
> > >  #define THRESHOLD_ADDR         0x3624
> > >  /* THRESHOLD_ADDR bitmasks */
> > > +#define THRESHOLD_MAX_CODE             0x20000
> > > +#define THRESHOLD_MIN_CODE             0
> > >  #define THRESHOLD_MAX_LIMIT_SHIFT      24
> > >  #define THRESHOLD_MIN_LIMIT_SHIFT      16
> > >  #define THRESHOLD_UPPER_LIMIT_SHIFT    8
> > >  #define THRESHOLD_LOWER_LIMIT_SHIFT    0
> > > +#define THRESHOLD_MAX_LIMIT_MASK       (THRESHOLD_MAX_CODE
> > << \
> > > +                                               THRESHOLD_MAX_LIMIT_SHIFT)
> > > +#define THRESHOLD_MIN_LIMIT_MASK       (THRESHOLD_MAX_CODE <<
> > \
> > > +                                               THRESHOLD_MIN_LIMIT_SHIFT)
> > > +#define THRESHOLD_UPPER_LIMIT_MASK     (THRESHOLD_MAX_CODE
> > << \
> > > +                                               THRESHOLD_UPPER_LIMIT_SHIFT)
> > > +#define THRESHOLD_LOWER_LIMIT_MASK     (THRESHOLD_MAX_CODE
> > << \
> > > +                                               THRESHOLD_LOWER_LIMIT_SHIFT)
> > >
> > >  /* Initial temperature threshold values */
> > > -#define LOWER_LIMIT_TH         0x50
> > > -#define UPPER_LIMIT_TH         0xdf
> > > +#define LOWER_LIMIT_TH_8960    0x50
> > > +#define UPPER_LIMIT_TH_8960    0xdf
> > > +#define LOWER_LIMIT_TH_8064    0x9d /* 95C */
> > > +#define UPPER_LIMIT_TH_8064    0xa6 /* 105C */
> > >  #define MIN_LIMIT_TH           0x0
> > >  #define MAX_LIMIT_TH           0xff
> > >
> > > @@ -57,6 +69,170 @@
> > >  #define TRDY_MASK              BIT(7)
> > >  #define TIMEOUT_US             100
> > >
> > > +#define TSENS_EN               BIT(0)
> > > +#define TSENS_SW_RST           BIT(1)
> > > +#define TSENS_ADC_CLK_SEL      BIT(2)
> > > +#define SENSOR0_EN             BIT(3)
> > > +#define SENSOR1_EN             BIT(4)
> > > +#define SENSOR2_EN             BIT(5)
> > > +#define SENSOR3_EN             BIT(6)
> > > +#define SENSOR4_EN             BIT(7)
> > > +#define SENSORS_EN             (SENSOR0_EN | SENSOR1_EN | \
> > > +                               SENSOR2_EN | SENSOR3_EN | SENSOR4_EN)
> > > +#define TSENS_8064_SENSOR5_EN                          BIT(8)
> > > +#define TSENS_8064_SENSOR6_EN                          BIT(9)
> > > +#define TSENS_8064_SENSOR7_EN                          BIT(10)
> > > +#define TSENS_8064_SENSOR8_EN                          BIT(11)
> > > +#define TSENS_8064_SENSOR9_EN                          BIT(12)
> > > +#define TSENS_8064_SENSOR10_EN                         BIT(13)
> > > +#define TSENS_8064_SENSORS_EN                          (SENSORS_EN | \
> > > +                                               TSENS_8064_SENSOR5_EN | \
> > > +                                               TSENS_8064_SENSOR6_EN | \
> > > +                                               TSENS_8064_SENSOR7_EN | \
> > > +                                               TSENS_8064_SENSOR8_EN | \
> > > +                                               TSENS_8064_SENSOR9_EN | \
> > > +                                               TSENS_8064_SENSOR10_EN)
> > > +
> > > +u32 tsens_8960_slope[] = {
> > > +                       1176, 1176, 1154, 1176,
> > > +                       1111, 1132, 1132, 1199,
> > > +                       1132, 1199, 1132
> > > +                       };
> > > +
> > > +/* Temperature on y axis and ADC-code on x-axis */
> > > +static inline int code_to_mdegC(u32 adc_code, const struct
> > tsens_sensor *s)
> > > +{
> > > +       int slope, offset;
> > > +
> > > +       slope = thermal_zone_get_slope(s->tzd);
> > > +       offset = CAL_MDEGC - slope * s->offset;
> > > +
> > > +       return adc_code * slope + offset;
> > > +}
> > > +
> > > +static void notify_uspace_tsens_fn(struct work_struct *work)
> > > +{
> > > +       struct tsens_sensor *s = container_of(work, struct tsens_sensor,
> > > +                                                               notify_work);
> > > +
> > > +       sysfs_notify(&s->tzd->device.kobj, NULL, "type");
> > > +}
> > > +
> > > +static void tsens_scheduler_fn(struct work_struct *work)
> > > +{
> > > +       struct tsens_priv *priv =
> > > +               container_of(work, struct tsens_priv, tsens_work);
> > > +       unsigned int threshold, threshold_low, code, reg, sensor;
> > > +       unsigned long mask;
> > > +       bool upper_th_x, lower_th_x;
> > > +       int ret;
> > > +
> > > +       ret = regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064,
> > &reg);
> > > +       if (ret)
> > > +               return;
> > > +       reg = reg | LOWER_STATUS_CLR | UPPER_STATUS_CLR;
> > > +       ret = regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064, reg);
> > > +       if (ret)
> > > +               return;
> > > +
> > > +       mask = ~(LOWER_STATUS_CLR | UPPER_STATUS_CLR);
> > > +       ret = regmap_read(priv->tm_map, THRESHOLD_ADDR, &threshold);
> > > +       if (ret)
> > > +               return;
> > > +       threshold_low = (threshold & THRESHOLD_LOWER_LIMIT_MASK) >>
> > > +                       THRESHOLD_LOWER_LIMIT_SHIFT;
> > > +       threshold = (threshold & THRESHOLD_UPPER_LIMIT_MASK) >>
> > > +                   THRESHOLD_UPPER_LIMIT_SHIFT;
> > > +
> > > +       ret = regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064,
> > &reg);
> > > +       if (ret)
> > > +               return;
> > > +
> > > +       ret = regmap_read(priv->tm_map, CNTL_ADDR, &sensor);
> > > +       if (ret)
> > > +               return;
> > > +       sensor &= (uint32_t)TSENS_8064_SENSORS_EN;
> > > +       sensor >>= SENSOR0_SHIFT;
> > > +
> > > +       /* Constraint: There is only 1 interrupt control register for all
> > > +        * 11 temperature sensor. So monitoring more than 1 sensor based
> > > +        * on interrupts will yield inconsistent result. To overcome this
> > > +        * issue we will monitor only sensor 0 which is the master sensor.
> > > +        */
> > > +
> > > +       /* Skip if the sensor is disabled */
> > > +       if (sensor & 1) {
> > > +               ret = regmap_read(priv->tm_map, priv->sensor[0].status,
> > &code);
> > > +               if (ret)
> > > +                       return;
> > > +               upper_th_x = code >= threshold;
> > > +               lower_th_x = code <= threshold_low;
> > > +               if (upper_th_x)
> > > +                       mask |= UPPER_STATUS_CLR;
> > > +               if (lower_th_x)
> > > +                       mask |= LOWER_STATUS_CLR;
> > > +               if (upper_th_x || lower_th_x) {
> > > +                       /* Notify user space */
> > > +                       schedule_work(&priv->sensor[0].notify_work);
> > > +                       pr_debug("Trigger (%d degrees) for sensor %d\n",
> > > +                                code_to_mdegC(code, &priv->sensor[0]), 0);
> > > +               }
> > > +       }
> > > +       regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064, reg &
> > mask);
> > > +}
> > > +
> > > +static irqreturn_t tsens_isr(int irq, void *data)
> > > +{
> > > +       struct tsens_priv *priv = data;
> > > +
> > > +       schedule_work(&priv->tsens_work);
> > > +       return IRQ_HANDLED;
> >
> >
> > Have you considered trying to reuse the regmap and interrupt handling
> > infrastructure in tsens.c that I used to convert over everything after
> > IP version 0.1?
> >
> > I started converting over 8960 but never managed to finish testing
> > this[1]. I'd be happy for you to take this over and get it working so
> > the 8960 doesn't end up being a completely separate driver from the
> > other platforms.
> >
> > [1]
> > https://git.linaro.org/people/amit.kucheria/kernel.git/log/?h=wrk3/tsens-
> > 8960-breakage
> >
>
> Thanks a lot for the link. I started doing some test and I think the only general
> code we will be able to use will be the init_common. The get temp and
> the function to convert code to decg are very different from the one used in
> 8960.  Do you think keep a custom get temp function is good or not?

OK. Let's start common init code and custom get_temp and adc-to-degc functions.

Regards,
Amit
diff mbox series

Patch

diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c
index 45788eb3c666..20d0bfb10f1f 100644
--- a/drivers/thermal/qcom/tsens-8960.c
+++ b/drivers/thermal/qcom/tsens-8960.c
@@ -8,6 +8,7 @@ 
 #include <linux/bitops.h>
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
+#include <linux/interrupt.h>
 #include <linux/thermal.h>
 #include "tsens.h"
 
@@ -27,7 +28,6 @@ 
 /* CNTL_ADDR bitmasks */
 #define EN			BIT(0)
 #define SW_RST			BIT(1)
-#define SENSOR0_EN		BIT(3)
 #define SLP_CLK_ENA		BIT(26)
 #define SLP_CLK_ENA_8660	BIT(24)
 #define MEASURE_PERIOD		1
@@ -41,14 +41,26 @@ 
 
 #define THRESHOLD_ADDR		0x3624
 /* THRESHOLD_ADDR bitmasks */
+#define THRESHOLD_MAX_CODE		0x20000
+#define THRESHOLD_MIN_CODE		0
 #define THRESHOLD_MAX_LIMIT_SHIFT	24
 #define THRESHOLD_MIN_LIMIT_SHIFT	16
 #define THRESHOLD_UPPER_LIMIT_SHIFT	8
 #define THRESHOLD_LOWER_LIMIT_SHIFT	0
+#define THRESHOLD_MAX_LIMIT_MASK	(THRESHOLD_MAX_CODE << \
+						THRESHOLD_MAX_LIMIT_SHIFT)
+#define THRESHOLD_MIN_LIMIT_MASK	(THRESHOLD_MAX_CODE << \
+						THRESHOLD_MIN_LIMIT_SHIFT)
+#define THRESHOLD_UPPER_LIMIT_MASK	(THRESHOLD_MAX_CODE << \
+						THRESHOLD_UPPER_LIMIT_SHIFT)
+#define THRESHOLD_LOWER_LIMIT_MASK	(THRESHOLD_MAX_CODE << \
+						THRESHOLD_LOWER_LIMIT_SHIFT)
 
 /* Initial temperature threshold values */
-#define LOWER_LIMIT_TH		0x50
-#define UPPER_LIMIT_TH		0xdf
+#define LOWER_LIMIT_TH_8960	0x50
+#define UPPER_LIMIT_TH_8960	0xdf
+#define LOWER_LIMIT_TH_8064	0x9d /* 95C */
+#define UPPER_LIMIT_TH_8064	0xa6 /* 105C */
 #define MIN_LIMIT_TH		0x0
 #define MAX_LIMIT_TH		0xff
 
@@ -57,6 +69,170 @@ 
 #define TRDY_MASK		BIT(7)
 #define TIMEOUT_US		100
 
+#define TSENS_EN		BIT(0)
+#define TSENS_SW_RST		BIT(1)
+#define TSENS_ADC_CLK_SEL	BIT(2)
+#define SENSOR0_EN		BIT(3)
+#define SENSOR1_EN		BIT(4)
+#define SENSOR2_EN		BIT(5)
+#define SENSOR3_EN		BIT(6)
+#define SENSOR4_EN		BIT(7)
+#define SENSORS_EN		(SENSOR0_EN | SENSOR1_EN | \
+				SENSOR2_EN | SENSOR3_EN | SENSOR4_EN)
+#define TSENS_8064_SENSOR5_EN				BIT(8)
+#define TSENS_8064_SENSOR6_EN				BIT(9)
+#define TSENS_8064_SENSOR7_EN				BIT(10)
+#define TSENS_8064_SENSOR8_EN				BIT(11)
+#define TSENS_8064_SENSOR9_EN				BIT(12)
+#define TSENS_8064_SENSOR10_EN				BIT(13)
+#define TSENS_8064_SENSORS_EN				(SENSORS_EN | \
+						TSENS_8064_SENSOR5_EN | \
+						TSENS_8064_SENSOR6_EN | \
+						TSENS_8064_SENSOR7_EN | \
+						TSENS_8064_SENSOR8_EN | \
+						TSENS_8064_SENSOR9_EN | \
+						TSENS_8064_SENSOR10_EN)
+
+u32 tsens_8960_slope[] = {
+			1176, 1176, 1154, 1176,
+			1111, 1132, 1132, 1199,
+			1132, 1199, 1132
+			};
+
+/* Temperature on y axis and ADC-code on x-axis */
+static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s)
+{
+	int slope, offset;
+
+	slope = thermal_zone_get_slope(s->tzd);
+	offset = CAL_MDEGC - slope * s->offset;
+
+	return adc_code * slope + offset;
+}
+
+static void notify_uspace_tsens_fn(struct work_struct *work)
+{
+	struct tsens_sensor *s = container_of(work, struct tsens_sensor,
+								notify_work);
+
+	sysfs_notify(&s->tzd->device.kobj, NULL, "type");
+}
+
+static void tsens_scheduler_fn(struct work_struct *work)
+{
+	struct tsens_priv *priv =
+		container_of(work, struct tsens_priv, tsens_work);
+	unsigned int threshold, threshold_low, code, reg, sensor;
+	unsigned long mask;
+	bool upper_th_x, lower_th_x;
+	int ret;
+
+	ret = regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064, &reg);
+	if (ret)
+		return;
+	reg = reg | LOWER_STATUS_CLR | UPPER_STATUS_CLR;
+	ret = regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064, reg);
+	if (ret)
+		return;
+
+	mask = ~(LOWER_STATUS_CLR | UPPER_STATUS_CLR);
+	ret = regmap_read(priv->tm_map, THRESHOLD_ADDR, &threshold);
+	if (ret)
+		return;
+	threshold_low = (threshold & THRESHOLD_LOWER_LIMIT_MASK) >>
+			THRESHOLD_LOWER_LIMIT_SHIFT;
+	threshold = (threshold & THRESHOLD_UPPER_LIMIT_MASK) >>
+		    THRESHOLD_UPPER_LIMIT_SHIFT;
+
+	ret = regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064, &reg);
+	if (ret)
+		return;
+
+	ret = regmap_read(priv->tm_map, CNTL_ADDR, &sensor);
+	if (ret)
+		return;
+	sensor &= (uint32_t)TSENS_8064_SENSORS_EN;
+	sensor >>= SENSOR0_SHIFT;
+
+	/* Constraint: There is only 1 interrupt control register for all
+	 * 11 temperature sensor. So monitoring more than 1 sensor based
+	 * on interrupts will yield inconsistent result. To overcome this
+	 * issue we will monitor only sensor 0 which is the master sensor.
+	 */
+
+	/* Skip if the sensor is disabled */
+	if (sensor & 1) {
+		ret = regmap_read(priv->tm_map, priv->sensor[0].status, &code);
+		if (ret)
+			return;
+		upper_th_x = code >= threshold;
+		lower_th_x = code <= threshold_low;
+		if (upper_th_x)
+			mask |= UPPER_STATUS_CLR;
+		if (lower_th_x)
+			mask |= LOWER_STATUS_CLR;
+		if (upper_th_x || lower_th_x) {
+			/* Notify user space */
+			schedule_work(&priv->sensor[0].notify_work);
+			pr_debug("Trigger (%d degrees) for sensor %d\n",
+				 code_to_mdegC(code, &priv->sensor[0]), 0);
+		}
+	}
+	regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064, reg & mask);
+}
+
+static irqreturn_t tsens_isr(int irq, void *data)
+{
+	struct tsens_priv *priv = data;
+
+	schedule_work(&priv->tsens_work);
+	return IRQ_HANDLED;
+}
+
+static void hw_init(struct tsens_priv *priv)
+{
+	int ret;
+	unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0;
+	unsigned int reg_status_cntl = 0;
+
+	regmap_read(priv->tm_map, CNTL_ADDR, &reg_cntl);
+	regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl | TSENS_SW_RST);
+
+	reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18) |
+		    (((1 << priv->num_sensors) - 1) << SENSOR0_SHIFT);
+	regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
+	regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064, &reg_status_cntl);
+	reg_status_cntl |= LOWER_STATUS_CLR | UPPER_STATUS_CLR |
+			   MIN_STATUS_MASK | MAX_STATUS_MASK;
+	regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064, reg_status_cntl);
+	reg_cntl |= TSENS_EN;
+	regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
+
+	regmap_read(priv->tm_map, CONFIG_ADDR, &reg_cfg);
+	if (priv->num_sensors > 1)
+		reg_cfg = (reg_cfg & ~CONFIG_MASK) | CONFIG;
+	else
+		reg_cfg = (reg_cfg & ~CONFIG_MASK) |
+			  (CONFIG << CONFIG_SHIFT_8660);
+	regmap_write(priv->tm_map, CONFIG_ADDR, reg_cfg);
+
+	reg_thr |= (LOWER_LIMIT_TH_8064 << THRESHOLD_LOWER_LIMIT_SHIFT) |
+		   (UPPER_LIMIT_TH_8064 << THRESHOLD_UPPER_LIMIT_SHIFT) |
+		   (MIN_LIMIT_TH << THRESHOLD_MIN_LIMIT_SHIFT) |
+		   (MAX_LIMIT_TH << THRESHOLD_MAX_LIMIT_SHIFT);
+
+	regmap_write(priv->tm_map, THRESHOLD_ADDR, reg_thr);
+
+	ret = devm_request_irq(priv->dev, priv->tsens_irq, tsens_isr,
+			       IRQF_TRIGGER_RISING, "tsens_interrupt", priv);
+	if (ret < 0) {
+		dev_err(priv->dev, "request_irq FAIL: %d", ret);
+		return;
+	}
+
+	INIT_WORK(&priv->tsens_work, tsens_scheduler_fn);
+}
+
 static int suspend_8960(struct tsens_priv *priv)
 {
 	int ret;
@@ -191,6 +367,8 @@  static int init_8960(struct tsens_priv *priv)
 		if (i >= 5)
 			priv->sensor[i].status = S0_STATUS_ADDR + 40;
 		priv->sensor[i].status += i * 4;
+		priv->sensor[i].slope = tsens_8960_slope[i];
+		INIT_WORK(&priv->sensor[i].notify_work, notify_uspace_tsens_fn);
 	}
 
 	reg_cntl = SW_RST;
@@ -241,18 +419,9 @@  static int calibrate_8960(struct tsens_priv *priv)
 
 	kfree(data);
 
-	return 0;
-}
-
-/* Temperature on y axis and ADC-code on x-axis */
-static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s)
-{
-	int slope, offset;
+	hw_init(priv);
 
-	slope = thermal_zone_get_slope(s->tzd);
-	offset = CAL_MDEGC - slope * s->offset;
-
-	return adc_code * slope + offset;
+	return 0;
 }
 
 static int get_temp_8960(const struct tsens_sensor *s, int *temp)
diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
index 59d01162c66a..e66048fabcc7 100644
--- a/drivers/thermal/qcom/tsens.h
+++ b/drivers/thermal/qcom/tsens.h
@@ -48,6 +48,7 @@  enum tsens_irq_type {
 struct tsens_sensor {
 	struct tsens_priv		*priv;
 	struct thermal_zone_device	*tzd;
+	struct work_struct		notify_work;
 	int				offset;
 	unsigned int			hw_id;
 	int				slope;
@@ -559,6 +560,7 @@  struct tsens_priv {
 	struct regmap			*tm_map;
 	struct regmap			*srot_map;
 	u32				tm_offset;
+	u32				tsens_irq;
 
 	/* lock for upper/lower threshold interrupts */
 	spinlock_t			ul_lock;
@@ -568,6 +570,7 @@  struct tsens_priv {
 	struct tsens_features		*feat;
 	const struct reg_field		*fields;
 	const struct tsens_ops		*ops;
+	struct work_struct		tsens_work;
 
 	struct dentry			*debug_root;
 	struct dentry			*debug;