diff mbox series

[v2,3/5] thermal: qcom: tsens: Add driver support for re-initialization quirk

Message ID 20220724122424.2509021-4-bhupesh.sharma@linaro.org
State Superseded
Headers show
Series Add support for tsens controller reinit via trustzone | expand

Commit Message

Bhupesh Sharma July 24, 2022, 12:24 p.m. UTC
Since for some Qualcomm tsens controllers, its suggested to
monitor the controller health periodically and in case an
issue is detected, to re-initialize the tsens controller
via trustzone, add the support for the same in the
qcom tsens driver.

Note that once the tsens controller is reset using scm call,
all SROT and TM region registers will enter the reset mode.

While all the SROT registers will be re-programmed and
re-enabled in trustzone prior to the scm call exit, the TM
region registers will not re-initialized in trustzone and thus
need to be handled by the tsens driver.

Cc: Bjorn Andersson <bjorn.andersson@linaro.org>
Cc: Amit Kucheria <amitk@kernel.org>
Cc: Thara Gopinath <thara.gopinath@gmail.com>
Cc: linux-pm@vger.kernel.org
Cc: linux-arm-msm@vger.kernel.org
Signed-off-by: Bhupesh Sharma <bhupesh.sharma@linaro.org>
---
 drivers/thermal/qcom/tsens-v2.c |   3 +
 drivers/thermal/qcom/tsens.c    | 197 ++++++++++++++++++++++++++++++++
 drivers/thermal/qcom/tsens.h    |  12 ++
 3 files changed, 212 insertions(+)

Comments

kernel test robot July 27, 2022, 7:31 a.m. UTC | #1
On 7/26/2022 7:38 PM, Bhupesh Sharma wrote:
> Hi,
> 
> On 7/26/22 4:40 AM, kernel test robot wrote:
>> Hi Bhupesh,
>>
>> Thank you for the patch! Yet something to improve:
>>
>> [auto build test ERROR on rafael-pm/thermal]
>> [also build test ERROR on linus/master v5.19-rc8 next-20220725]
>> [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#_base_tree_information]
>>
>> url:    
>> https://github.com/intel-lab-lkp/linux/commits/Bhupesh-Sharma/Add-support-for-tsens-controller-reinit-via-trustzone/20220724-202546 
>>
>> base:   
>> https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git 
>> thermal
>> config: ia64-randconfig-r005-20220724 
>> (https://download.01.org/0day-ci/archive/20220726/202207260755.tUajnfB4-lkp@intel.com/config) 
>>
>> compiler: ia64-linux-gcc (GCC) 12.1.0
>> 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
>>          # 
>> https://github.com/intel-lab-lkp/linux/commit/2356630fadc0a622264bf292b6930f8c728b0709 
>>
>>          git remote add linux-review 
>> https://github.com/intel-lab-lkp/linux
>>          git fetch --no-tags linux-review 
>> Bhupesh-Sharma/Add-support-for-tsens-controller-reinit-via-trustzone/20220724-202546 
>>
>>          git checkout 2356630fadc0a622264bf292b6930f8c728b0709
>>          # save the config file
>>          mkdir build_dir && cp config build_dir/.config
>>          COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 
>> make.cross W=1 O=build_dir ARCH=ia64 SHELL=/bin/bash
>>
>> If you fix the issue, kindly add following tag where applicable
>> Reported-by: kernel test robot <lkp@intel.com>
>>
>> All errors (new ones prefixed by >>):
>>
>>     ia64-linux-ld: drivers/thermal/qcom/tsens.o: in function 
>> `tsens_probe':
>>>> tsens.c:(.text+0x6d2): undefined reference to `qcom_scm_is_available'
>>     ia64-linux-ld: drivers/thermal/qcom/tsens.o: in function 
>> `tsens_health_check_and_reinit.constprop.0':
>>>> tsens.c:(.text+0x10c2): undefined reference to `qcom_scm_tsens_reinit'
> 
> It seems you have missed picking up [PATCH 1/5] firmware: qcom: scm: Add 
> support for tsens reinit workaround before running the checks on this 
> patch which is a part of this series itself (see [1]).

Hi Bhupesh,

Thanks for the feedback, the bot applied the full patch set to test,
please see

 
https://github.com/intel-lab-lkp/linux/commits/Bhupesh-Sharma/Add-support-for-tsens-controller-reinit-via-trustzone/20220724-202546 


Maybe the bot chose a wrong base branch to apply which caused the
unexpected issue.

Best Regards,
Rong Chen

> 
> If I pick the PATCHes in the right order [PATCH 1/5], [PATCH 2/5] 
> followed by [PATCH 3/5], I don't see the compilation error being 
> reported (even with W=1 build options).
> 
> [1]. 
> https://lore.kernel.org/linux-arm-msm/20220724122424.2509021-2-bhupesh.sharma@linaro.org/ 
> 
> 
> Thanks,
> Bhupesh
> _______________________________________________
> kbuild-all mailing list -- kbuild-all@lists.01.org
> To unsubscribe send an email to kbuild-all-leave@lists.01.org
Bhupesh Sharma Aug. 3, 2022, 5:30 a.m. UTC | #2
Hi,

On Wed, 27 Jul 2022 at 13:01, Chen, Rong A <rong.a.chen@intel.com> wrote:
> On 7/26/2022 7:38 PM, Bhupesh Sharma wrote:
> > Hi,
> >
> > On 7/26/22 4:40 AM, kernel test robot wrote:
> >> Hi Bhupesh,
> >>
> >> Thank you for the patch! Yet something to improve:
> >>
> >> [auto build test ERROR on rafael-pm/thermal]
> >> [also build test ERROR on linus/master v5.19-rc8 next-20220725]
> >> [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#_base_tree_information]
> >>
> >> url:
> >> https://github.com/intel-lab-lkp/linux/commits/Bhupesh-Sharma/Add-support-for-tsens-controller-reinit-via-trustzone/20220724-202546
> >>
> >> base:
> >> https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git
> >> thermal
> >> config: ia64-randconfig-r005-20220724
> >> (https://download.01.org/0day-ci/archive/20220726/202207260755.tUajnfB4-lkp@intel.com/config)
> >>
> >> compiler: ia64-linux-gcc (GCC) 12.1.0
> >> 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
> >>          #
> >> https://github.com/intel-lab-lkp/linux/commit/2356630fadc0a622264bf292b6930f8c728b0709
> >>
> >>          git remote add linux-review
> >> https://github.com/intel-lab-lkp/linux
> >>          git fetch --no-tags linux-review
> >> Bhupesh-Sharma/Add-support-for-tsens-controller-reinit-via-trustzone/20220724-202546
> >>
> >>          git checkout 2356630fadc0a622264bf292b6930f8c728b0709
> >>          # save the config file
> >>          mkdir build_dir && cp config build_dir/.config
> >>          COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0
> >> make.cross W=1 O=build_dir ARCH=ia64 SHELL=/bin/bash
> >>
> >> If you fix the issue, kindly add following tag where applicable
> >> Reported-by: kernel test robot <lkp@intel.com>
> >>
> >> All errors (new ones prefixed by >>):
> >>
> >>     ia64-linux-ld: drivers/thermal/qcom/tsens.o: in function
> >> `tsens_probe':
> >>>> tsens.c:(.text+0x6d2): undefined reference to `qcom_scm_is_available'
> >>     ia64-linux-ld: drivers/thermal/qcom/tsens.o: in function
> >> `tsens_health_check_and_reinit.constprop.0':
> >>>> tsens.c:(.text+0x10c2): undefined reference to `qcom_scm_tsens_reinit'
> >
> > It seems you have missed picking up [PATCH 1/5] firmware: qcom: scm: Add
> > support for tsens reinit workaround before running the checks on this
> > patch which is a part of this series itself (see [1]).
>
> Hi Bhupesh,
>
> Thanks for the feedback, the bot applied the full patch set to test,
> please see
>
>
> https://github.com/intel-lab-lkp/linux/commits/Bhupesh-Sharma/Add-support-for-tsens-controller-reinit-via-trustzone/20220724-202546
>
>
> Maybe the bot chose a wrong base branch to apply which caused the
> unexpected issue.

Sure, no problem. Thanks for your email.

Regards,
Bhupesh

> > If I pick the PATCHes in the right order [PATCH 1/5], [PATCH 2/5]
> > followed by [PATCH 3/5], I don't see the compilation error being
> > reported (even with W=1 build options).
> >
> > [1].
> > https://lore.kernel.org/linux-arm-msm/20220724122424.2509021-2-bhupesh.sharma@linaro.org/
> >
> >
> > Thanks,
> > Bhupesh
> > _______________________________________________
> > kbuild-all mailing list -- kbuild-all@lists.01.org
> > To unsubscribe send an email to kbuild-all-leave@lists.01.org
diff mbox series

Patch

diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c
index b293ed32174b..f521e4479cc5 100644
--- a/drivers/thermal/qcom/tsens-v2.c
+++ b/drivers/thermal/qcom/tsens-v2.c
@@ -88,6 +88,9 @@  static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
 
 	/* TRDY: 1=ready, 0=in progress */
 	[TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
+
+	/* FIRST_ROUND_COMPLETE: 1=complete, 0=not complete */
+	[FIRST_ROUND_COMPLETE] = REG_FIELD(TM_TRDY_OFF, 3, 3),
 };
 
 static const struct tsens_ops ops_generic_v2 = {
diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
index e49f58e83513..c2d085fb5447 100644
--- a/drivers/thermal/qcom/tsens.c
+++ b/drivers/thermal/qcom/tsens.c
@@ -7,6 +7,7 @@ 
 #include <linux/debugfs.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/qcom_scm.h>
 #include <linux/module.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/of.h>
@@ -594,6 +595,113 @@  static void tsens_disable_irq(struct tsens_priv *priv)
 	regmap_field_write(priv->rf[INT_EN], 0);
 }
 
+static int tsens_reenable_hw_after_scm(struct tsens_priv *priv)
+{
+	/*
+	 * Re-enable watchdog, unmask the bark and
+	 * disable cycle completion monitoring.
+	 */
+	regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 1);
+	regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 0);
+	regmap_field_write(priv->rf[WDOG_BARK_MASK], 0);
+	regmap_field_write(priv->rf[CC_MON_MASK], 1);
+
+	/* Re-enable interrupts */
+	tsens_enable_irq(priv);
+
+	return 0;
+}
+
+static int tsens_health_check_and_reinit(struct tsens_priv *priv,
+					 int hw_id)
+{
+	int ret, trdy, first_round, sw_reg;
+	unsigned long timeout;
+
+	/* First check if TRDY is SET */
+	ret = regmap_field_read(priv->rf[TRDY], &trdy);
+	if (ret)
+		goto err;
+
+	if (!trdy) {
+		ret = regmap_field_read(priv->rf[FIRST_ROUND_COMPLETE], &first_round);
+		if (ret)
+			goto err;
+
+		if (!first_round) {
+			WARN_ON(!mutex_is_locked(&priv->reinit_mutex));
+
+			/* Wait for 2 ms for tsens controller to recover */
+			timeout = jiffies + msecs_to_jiffies(RESET_TIMEOUT_MS);
+			do {
+				ret = regmap_field_read(priv->rf[FIRST_ROUND_COMPLETE],
+						&first_round);
+				if (ret)
+					goto err;
+
+				if (first_round) {
+					dev_dbg(priv->dev, "tsens controller recovered\n");
+					return 0; /* success */
+				}
+			} while (time_before(jiffies, timeout));
+
+			spin_lock(&priv->reinit_lock);
+
+			/*
+			 * Invoke SCM call only if SW register write is
+			 * reflecting in controller. Try it for 2 ms.
+			 * In case that fails mark the tsens controller
+			 * as unrecoverable.
+			 */
+			timeout = jiffies + msecs_to_jiffies(RESET_TIMEOUT_MS);
+			do {
+				ret = regmap_field_write(priv->rf[INT_EN], CRITICAL_INT_EN);
+				if (ret)
+					goto err;
+
+				ret = regmap_field_read(priv->rf[INT_EN], &sw_reg);
+				if (ret)
+					goto err;
+			} while ((sw_reg & CRITICAL_INT_EN) && (time_before(jiffies, timeout)));
+
+			if (!(sw_reg & CRITICAL_INT_EN)) {
+				ret = -ENOTRECOVERABLE;
+				goto err;
+			}
+
+			/*
+			 * tsens controller did not recover,
+			 * proceed with SCM call to re-init it.
+			 */
+			ret = qcom_scm_tsens_reinit();
+			if (ret) {
+				dev_err(priv->dev, "tsens reinit scm call failed (%d)\n", ret);
+				goto err;
+			}
+
+			/*
+			 * After the SCM call, we need to re-enable
+			 * the interrupts and also set active threshold
+			 * for each sensor.
+			 */
+			ret = tsens_reenable_hw_after_scm(priv);
+			if (ret) {
+				dev_err(priv->dev,
+					"tsens re-enable after scm call failed (%d)\n", ret);
+				goto err;
+			}
+
+			/* Notify reinit wa worker */
+			queue_work(system_highpri_wq, &priv->reinit_wa_notify);
+
+			spin_unlock(&priv->reinit_lock);
+		}
+	}
+
+err:
+	return ret;
+}
+
 int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
 {
 	struct tsens_priv *priv = s->priv;
@@ -607,6 +715,21 @@  int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
 	if (tsens_version(priv) == VER_0)
 		goto get_temp;
 
+	/*
+	 * For some tsens controllers, its suggested to
+	 * monitor the controller health periodically
+	 * and in case an issue is detected to reinit
+	 * tsens controller via trustzone.
+	 */
+	if (priv->needs_reinit_wa) {
+		mutex_lock(&priv->reinit_mutex);
+		ret = tsens_health_check_and_reinit(priv, hw_id);
+		mutex_unlock(&priv->reinit_mutex);
+
+		if (ret)
+			return ret;
+	}
+
 	/* Valid bit is 0 for 6 AHB clock cycles.
 	 * At 19.2MHz, 1 AHB clock is ~60ns.
 	 * We should enter this loop very, very rarely.
@@ -739,6 +862,40 @@  static const struct regmap_config tsens_srot_config = {
 	.reg_stride	= 4,
 };
 
+static void __tsens_reinit_worker(struct tsens_priv *priv)
+{
+	int ret, temp;
+	unsigned int i;
+	struct tsens_irq_data d;
+
+	for (i = 0; i < priv->num_sensors; i++) {
+		const struct tsens_sensor *s = &priv->sensor[i];
+		u32 hw_id = s->hw_id;
+
+		if (!s->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] error reading sensor during reinit\n", hw_id);
+			continue;
+		}
+
+		tsens_read_irq_state(priv, hw_id, s, &d);
+
+		if ((d.up_thresh < temp) || (d.low_thresh > temp)) {
+			dev_dbg(priv->dev, "[%u] TZ update trigger during reinit (%d mC)\n",
+				hw_id, temp);
+			thermal_zone_device_update(s->tzd, THERMAL_EVENT_UNSPECIFIED);
+		} else {
+			dev_dbg(priv->dev, "[%u] no violation during reinit (%d)\n",
+				hw_id, temp);
+		}
+	}
+}
+
 int __init init_common(struct tsens_priv *priv)
 {
 	void __iomem *tm_base, *srot_base;
@@ -860,6 +1017,14 @@  int __init init_common(struct tsens_priv *priv)
 		goto err_put_device;
 	}
 
+	priv->rf[FIRST_ROUND_COMPLETE] = devm_regmap_field_alloc(dev,
+								priv->tm_map,
+								priv->fields[FIRST_ROUND_COMPLETE]);
+	if (IS_ERR(priv->rf[FIRST_ROUND_COMPLETE])) {
+		ret = PTR_ERR(priv->rf[FIRST_ROUND_COMPLETE]);
+		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++) {
@@ -1082,6 +1247,14 @@  static int tsens_register(struct tsens_priv *priv)
 	return ret;
 }
 
+static void tsens_reinit_worker_notify(struct work_struct *work)
+{
+	struct tsens_priv *priv = container_of(work, struct tsens_priv,
+					       reinit_wa_notify);
+
+	__tsens_reinit_worker(priv);
+}
+
 static int tsens_probe(struct platform_device *pdev)
 {
 	int ret, i;
@@ -1123,6 +1296,11 @@  static int tsens_probe(struct platform_device *pdev)
 
 	priv->dev = dev;
 	priv->num_sensors = num_sensors;
+	priv->needs_reinit_wa = data->needs_reinit_wa;
+
+	if (priv->needs_reinit_wa && !qcom_scm_is_available())
+		return -EPROBE_DEFER;
+
 	priv->ops = data->ops;
 	for (i = 0;  i < priv->num_sensors; i++) {
 		if (data->hw_ids)
@@ -1138,6 +1316,25 @@  static int tsens_probe(struct platform_device *pdev)
 	if (!priv->ops || !priv->ops->init || !priv->ops->get_temp)
 		return -EINVAL;
 
+	/*
+	 * Reinitialization workaround is currently supported only for
+	 * tsens controller versions v2.
+	 *
+	 * If incorrect platform data is passed to this effect, ignore
+	 * the requested setting and move forward.
+	 */
+	if (priv->needs_reinit_wa && (tsens_version(priv) < VER_2_X)) {
+		dev_warn(dev,
+			 "%s: Reinit quirk available only for tsens v2\n", __func__);
+		priv->needs_reinit_wa = false;
+	}
+
+	mutex_init(&priv->reinit_mutex);
+	spin_lock_init(&priv->reinit_lock);
+
+	if (priv->needs_reinit_wa)
+		INIT_WORK(&priv->reinit_wa_notify, tsens_reinit_worker_notify);
+
 	ret = priv->ops->init(priv);
 	if (ret < 0) {
 		dev_err(dev, "%s: init failed\n", __func__);
diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
index 92787017c6ab..900d2a74d25e 100644
--- a/drivers/thermal/qcom/tsens.h
+++ b/drivers/thermal/qcom/tsens.h
@@ -14,9 +14,12 @@ 
 #define SLOPE_FACTOR		1000
 #define SLOPE_DEFAULT		3200
 #define TIMEOUT_US		100
+#define RESET_TIMEOUT_MS	2
 #define THRESHOLD_MAX_ADC_CODE	0x3ff
 #define THRESHOLD_MIN_ADC_CODE	0x0
 
+#define CRITICAL_INT_EN		(BIT(2))
+
 #include <linux/interrupt.h>
 #include <linux/thermal.h>
 #include <linux/regmap.h>
@@ -165,6 +168,7 @@  enum regfield_ids {
 	/* ----- TM ------ */
 	/* TRDY */
 	TRDY,
+	FIRST_ROUND_COMPLETE,
 	/* INTERRUPT ENABLE */
 	INT_EN,	/* v2+ has separate enables for crit, upper and lower irq */
 	/* STATUS */
@@ -564,6 +568,14 @@  struct tsens_priv {
 	u32				tm_offset;
 	bool				needs_reinit_wa;
 
+	struct work_struct		reinit_wa_notify;
+
+	/* protects reinit related serialization */
+	struct mutex			reinit_mutex;
+
+	/* lock for reinit workaround */
+	spinlock_t			reinit_lock;
+
 	/* lock for upper/lower threshold interrupts */
 	spinlock_t			ul_lock;