aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinaro CI <ci_notify@linaro.org>2022-09-14 23:04:18 +0000
committerLinaro CI <ci_notify@linaro.org>2022-09-14 23:04:18 +0000
commit597837a02e6699ff6c412d2014839bd1f9669735 (patch)
treede287ee93120a8a04c10adb28c4795a5d1df5e71
parent1f555c13c7771de4374a533a64852acebcda1363 (diff)
parentab32daaccd3a4cd8fa622b244d3cc151b2900cf1 (diff)
Merge remote-tracking branch 'sa8155p-adp-dts-drivers/tracking-qcomlt-sa8155p-dts-drivers' into integration-linux-qcomlt
-rw-r--r--arch/arm64/boot/dts/qcom/sm8150.dtsi24
-rw-r--r--drivers/firmware/qcom_scm.c15
-rw-r--r--drivers/firmware/qcom_scm.h4
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-core.c13
-rw-r--r--drivers/thermal/qcom/tsens-v2.c15
-rw-r--r--drivers/thermal/qcom/tsens.c200
-rw-r--r--drivers/thermal/qcom/tsens.h18
-rw-r--r--include/linux/qcom_scm.h2
8 files changed, 272 insertions, 19 deletions
diff --git a/arch/arm64/boot/dts/qcom/sm8150.dtsi b/arch/arm64/boot/dts/qcom/sm8150.dtsi
index 7d509ecd44da..916f12b799b7 100644
--- a/arch/arm64/boot/dts/qcom/sm8150.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8150.dtsi
@@ -3394,57 +3394,49 @@
compute-cb@1 {
compatible = "qcom,fastrpc-compute-cb";
reg = <1>;
- iommus = <&apps_smmu 0x1401 0x2040>,
- <&apps_smmu 0x1421 0x0>,
- <&apps_smmu 0x2001 0x420>,
- <&apps_smmu 0x2041 0x0>;
+ iommus = <&apps_smmu 0x1001 0x0460>;
};
compute-cb@2 {
compatible = "qcom,fastrpc-compute-cb";
reg = <2>;
- iommus = <&apps_smmu 0x2 0x3440>,
- <&apps_smmu 0x22 0x3400>;
+ iommus = <&apps_smmu 0x1002 0x0460>;
};
compute-cb@3 {
compatible = "qcom,fastrpc-compute-cb";
reg = <3>;
- iommus = <&apps_smmu 0x3 0x3440>,
- <&apps_smmu 0x1423 0x0>,
- <&apps_smmu 0x2023 0x0>;
+ iommus = <&apps_smmu 0x1003 0x0460>;
};
compute-cb@4 {
compatible = "qcom,fastrpc-compute-cb";
reg = <4>;
- iommus = <&apps_smmu 0x4 0x3440>,
- <&apps_smmu 0x24 0x3400>;
+ iommus = <&apps_smmu 0x1004 0x0460>;
};
compute-cb@5 {
compatible = "qcom,fastrpc-compute-cb";
reg = <5>;
- iommus = <&apps_smmu 0x5 0x3440>,
- <&apps_smmu 0x25 0x3400>;
+ iommus = <&apps_smmu 0x1005 0x0460>;
};
compute-cb@6 {
compatible = "qcom,fastrpc-compute-cb";
reg = <6>;
- iommus = <&apps_smmu 0x6 0x3460>;
+ iommus = <&apps_smmu 0x1006 0x0460>;
};
compute-cb@7 {
compatible = "qcom,fastrpc-compute-cb";
reg = <7>;
- iommus = <&apps_smmu 0x7 0x3460>;
+ iommus = <&apps_smmu 0x1007 0x0460>;
};
compute-cb@8 {
compatible = "qcom,fastrpc-compute-cb";
reg = <8>;
- iommus = <&apps_smmu 0x8 0x3460>;
+ iommus = <&apps_smmu 0x1008 0x0460>;
};
/* note: secure cb9 in downstream */
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
index cdbfe54c8146..93adcc046a62 100644
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -858,6 +858,21 @@ int qcom_scm_mem_protect_video_var(u32 cp_start, u32 cp_size,
}
EXPORT_SYMBOL(qcom_scm_mem_protect_video_var);
+int qcom_scm_tsens_reinit(void)
+{
+ int ret;
+ const struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_TSENS,
+ .cmd = QCOM_SCM_TSENS_INIT_ID,
+ };
+ struct qcom_scm_res res;
+
+ ret = qcom_scm_call(__scm->dev, &desc, &res);
+
+ return ret ? : res.result[0];
+}
+EXPORT_SYMBOL(qcom_scm_tsens_reinit);
+
static int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region,
size_t mem_sz, phys_addr_t src, size_t src_sz,
phys_addr_t dest, size_t dest_sz)
diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h
index 0d51eef2472f..495fa00230c7 100644
--- a/drivers/firmware/qcom_scm.h
+++ b/drivers/firmware/qcom_scm.h
@@ -94,6 +94,10 @@ extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a
+/* TSENS Services and Function IDs */
+#define QCOM_SCM_SVC_TSENS 0x1E
+#define QCOM_SCM_TSENS_INIT_ID 0x5
+
#define QCOM_SCM_SVC_IO 0x05
#define QCOM_SCM_IO_READ 0x01
#define QCOM_SCM_IO_WRITE 0x02
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index d39660a3e50c..14c1c7869795 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -978,6 +978,16 @@ static bool etm4_init_sysreg_access(struct etmv4_drvdata *drvdata,
return false;
/*
+ * Some Qualcomm implementations require skipping powering up the trace unit,
+ * as the ETMs are in the same power domain as their CPU cores.
+ *
+ * Since the 'skip_power_up' flag is used inside 'etm4_init_arch_data' function,
+ * initialize it before the function is called.
+ */
+ if (fwnode_property_present(dev_fwnode(dev), "qcom,skip-power-up"))
+ drvdata->skip_power_up = true;
+
+ /*
* ETMs implementing sysreg access must implement TRCDEVARCH.
*/
devarch = read_etm4x_sysreg_const_offset(TRCDEVARCH);
@@ -1951,8 +1961,7 @@ static int etm4_probe(struct device *dev, void __iomem *base, u32 etm_pid)
return -EINVAL;
/* TRCPDCR is not accessible with system instructions. */
- if (!desc.access.io_mem ||
- fwnode_property_present(dev_fwnode(dev), "qcom,skip-power-up"))
+ if (!desc.access.io_mem)
drvdata->skip_power_up = true;
major = ETM_ARCH_MAJOR_VERSION(drvdata->arch);
diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c
index b293ed32174b..431f17f99d34 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 = {
@@ -101,6 +104,18 @@ struct tsens_plat_data data_tsens_v2 = {
.fields = tsens_v2_regfields,
};
+/*
+ * For some tsens v2 controllers, its suggested to monitor the
+ * controller health periodically and in case an issue is detected
+ * to reinit tsens controller via trustzone.
+ */
+struct tsens_plat_data data_tsens_v2_reinit = {
+ .ops = &ops_generic_v2,
+ .feat = &tsens_v2_feat,
+ .needs_reinit_wa = true,
+ .fields = tsens_v2_regfields,
+};
+
/* Kept around for backward compatibility with old msm8996.dtsi */
struct tsens_plat_data data_8996 = {
.num_sensors = 13,
diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
index e49f58e83513..928b6a44fda8 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++) {
@@ -981,6 +1146,9 @@ static const struct of_device_id tsens_table[] = {
.compatible = "qcom,msm8996-tsens",
.data = &data_8996,
}, {
+ .compatible = "qcom,sm8150-tsens",
+ .data = &data_tsens_v2_reinit,
+ }, {
.compatible = "qcom,tsens-v1",
.data = &data_tsens_v1,
}, {
@@ -1082,6 +1250,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 +1299,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 +1319,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 ba05c8233356..03cc3a790972 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 */
@@ -513,6 +517,7 @@ struct tsens_features {
* @num_sensors: Number of sensors supported by platform
* @ops: operations the tsens instance supports
* @hw_ids: Subset of sensors ids supported by platform, if not the first n
+ * @needs_reinit_wa: tsens controller might need reinit via trustzone
* @feat: features of the IP
* @fields: bitfield locations
*/
@@ -520,6 +525,7 @@ struct tsens_plat_data {
const u32 num_sensors;
const struct tsens_ops *ops;
unsigned int *hw_ids;
+ bool needs_reinit_wa;
struct tsens_features *feat;
const struct reg_field *fields;
};
@@ -542,6 +548,7 @@ struct tsens_context {
* @srot_map: pointer to SROT register address space
* @tm_offset: deal with old device trees that don't address TM and SROT
* address space separately
+ * @needs_reinit_wa: tsens controller might need reinit via trustzone
* @ul_lock: lock while processing upper/lower threshold interrupts
* @crit_lock: lock while processing critical threshold interrupts
* @rf: array of regmap_fields used to store value of the field
@@ -559,6 +566,15 @@ struct tsens_priv {
struct regmap *tm_map;
struct regmap *srot_map;
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;
@@ -591,6 +607,6 @@ extern struct tsens_plat_data data_8916, data_8939, data_8974, data_9607;
extern struct tsens_plat_data data_tsens_v1, data_8976;
/* TSENS v2 targets */
-extern struct tsens_plat_data data_8996, data_tsens_v2;
+extern struct tsens_plat_data data_8996, data_tsens_v2_reinit, data_tsens_v2;
#endif /* __QCOM_TSENS_H__ */
diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h
index f8335644a01a..5c37e1658cef 100644
--- a/include/linux/qcom_scm.h
+++ b/include/linux/qcom_scm.h
@@ -124,4 +124,6 @@ extern int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val,
extern int qcom_scm_lmh_profile_change(u32 profile_id);
extern bool qcom_scm_lmh_dcvsh_available(void);
+extern int qcom_scm_tsens_reinit(void);
+
#endif