diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-04-08 05:00:16 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-04-08 05:00:16 +0200 |
commit | 34183ddd13dbfa859c4b68d16a30aad2cce72b11 (patch) | |
tree | bb32ff408c71c834352a9bdf1e7969c12cc91976 /drivers/thermal/qcom | |
parent | Merge tag 'mfd-next-5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/lee... (diff) | |
parent | Documentation: cpu-idle-cooling: Fix diagram for 33% duty cycle (diff) | |
download | linux-34183ddd13dbfa859c4b68d16a30aad2cce72b11.tar.xz linux-34183ddd13dbfa859c4b68d16a30aad2cce72b11.zip |
Merge tag 'thermal-v5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux
Pull thermal updates from Daniel Lezcano:
- Convert tsens configuration DT binding to yaml (Rajeshwari)
- Add interrupt support on the rcar sensor (Niklas Söderlund)
- Add a new Spreadtrum thermal driver (Baolin Wang)
- Add thermal binding for the fsl scu board, a new API to retrieve the
sensor id bound to the thermal zone and i.MX system controller sensor
(Anson Huang))
- Remove warning log when a deferred probe is requested on Exynos
(Marek Szyprowski)
- Add the thermal monitoring unit support for imx8mm with its DT
bindings (Anson Huang)
- Rephrase the Kconfig text for clarity (Linus Walleij)
- Use the gpio descriptor for the ti-soc-thermal (Linus Walleij)
- Align msg structure to 4 bytes for i.MX SC, fix the Kconfig
dependency, add the __may_be unused annotation for PM functions and
the COMPILE_TEST option for imx8mm (Anson Huang)
- Fix a dependency on regmap in Kconfig for qoriq (Yuantian Tang)
- Add DT binding and support for the rcar gen3 r8a77961 and improve the
error path on the rcar init function (Niklas Söderlund)
- Cleanup and improvements for the tsens Qcom sensor (Amit Kucheria)
- Improve code by removing lock and caching values in the rcar thermal
sensor (Niklas Söderlund)
- Cleanup in the qoriq drivers and add a call to
imx_thermal_unregister_legacy_cooling in the removal function (Anson
Huang)
- Remove redundant 'maxItems' in tsens and sprd DT bindings (Rob
Herring)
- Change the thermal DT bindings by making the cooling-maps optional
(Yuantian Tang)
- Add Tiger Lake support (Sumeet Pawnikar)
- Use scnprintf() for avoiding potential buffer overflow (Takashi Iwai)
- Make pkg_temp_lock a raw_spinlock_t(Clark Williams)
- Fix incorrect data types by changing them to signed on i.MX SC (Anson
Huang)
- Replace zero-length array with flexible-array member (Gustavo A. R.
Silva)
- Add support for i.MX8MP in the driver and in the DT bindings (Anson
Huang)
- Fix return value of the cpufreq_set_cur_state() function (Willy
Wolff)
- Remove abusing and scary WARN_ON in the cpufreq cooling device
(Daniel Lezcano)
- Fix build warning of incorrect argument type reported by sparse on
imx8mm (Anson Huang)
- Fix stub for the devfreq cooling device (Martin Blumenstingl)
- Fix cpu idle cooling documentation (Sergey Vidishev)
* tag 'thermal-v5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux: (52 commits)
Documentation: cpu-idle-cooling: Fix diagram for 33% duty cycle
thermal: devfreq_cooling: inline all stubs for CONFIG_DEVFREQ_THERMAL=n
thermal: imx8mm: Fix build warning of incorrect argument type
thermal/drivers/cpufreq_cooling: Remove abusing WARN_ON
thermal/drivers/cpufreq_cooling: Fix return of cpufreq_set_cur_state
thermal: imx8mm: Add i.MX8MP support
dt-bindings: thermal: imx8mm-thermal: Add support for i.MX8MP
thermal: qcom: tsens.h: Replace zero-length array with flexible-array member
thermal: imx_sc_thermal: Fix incorrect data type
thermal: int340x_thermal: Use scnprintf() for avoiding potential buffer overflow
thermal: int340x: processor_thermal: Add Tiger Lake support
thermal/x86_pkg_temp: Make pkg_temp_lock a raw_spinlock_t
dt-bindings: thermal: make cooling-maps property optional
dt-bindings: thermal: qcom-tsens: Remove redundant 'maxItems'
dt-bindings: thermal: sprd: Remove redundant 'maxItems'
thermal: imx: Calling imx_thermal_unregister_legacy_cooling() in .remove
thermal: qoriq: Sort includes alphabetically
thermal: qoriq: Use devm_add_action_or_reset() to handle all cleanups
thermal: rcar_thermal: Remove lock in rcar_thermal_get_current_temp()
thermal: rcar_thermal: Do not store ctemp in rcar_thermal_priv
...
Diffstat (limited to 'drivers/thermal/qcom')
-rw-r--r-- | drivers/thermal/qcom/tsens-8960.c | 4 | ||||
-rw-r--r-- | drivers/thermal/qcom/tsens-common.c | 194 | ||||
-rw-r--r-- | drivers/thermal/qcom/tsens-v0_1.c | 6 | ||||
-rw-r--r-- | drivers/thermal/qcom/tsens-v1.c | 6 | ||||
-rw-r--r-- | drivers/thermal/qcom/tsens-v2.c | 24 | ||||
-rw-r--r-- | drivers/thermal/qcom/tsens.c | 65 | ||||
-rw-r--r-- | drivers/thermal/qcom/tsens.h | 105 |
7 files changed, 332 insertions, 72 deletions
diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c index fb77acb8d13b..2a28a5af209e 100644 --- a/drivers/thermal/qcom/tsens-8960.c +++ b/drivers/thermal/qcom/tsens-8960.c @@ -245,7 +245,7 @@ static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) return adc_code * slope + offset; } -static int get_temp_8960(struct tsens_sensor *s, int *temp) +static int get_temp_8960(const struct tsens_sensor *s, int *temp) { int ret; u32 code, trdy; @@ -279,7 +279,7 @@ static const struct tsens_ops ops_8960 = { .resume = resume_8960, }; -const struct tsens_plat_data data_8960 = { +struct tsens_plat_data data_8960 = { .num_sensors = 11, .ops = &ops_8960, }; diff --git a/drivers/thermal/qcom/tsens-common.c b/drivers/thermal/qcom/tsens-common.c index c8d57ee0a5bb..172545366636 100644 --- a/drivers/thermal/qcom/tsens-common.c +++ b/drivers/thermal/qcom/tsens-common.c @@ -23,6 +23,10 @@ * @low_thresh: lower threshold temperature value * @low_irq_mask: mask register for lower threshold irqs * @low_irq_clear: clear register for lower threshold irqs + * @crit_viol: critical threshold violated + * @crit_thresh: critical threshold temperature value + * @crit_irq_mask: mask register for critical threshold irqs + * @crit_irq_clear: clear register for critical threshold irqs * * Structure containing data about temperature threshold settings and * irq status if they were violated. @@ -36,6 +40,10 @@ struct tsens_irq_data { int low_thresh; u32 low_irq_mask; u32 low_irq_clear; + u32 crit_viol; + u32 crit_thresh; + u32 crit_irq_mask; + u32 crit_irq_clear; }; char *qfprom_read(struct device *dev, const char *cname) @@ -128,7 +136,7 @@ static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) * Return: Temperature in milliCelsius on success, a negative errno will * be returned in error cases */ -static int tsens_hw_to_mC(struct tsens_sensor *s, int field) +static int tsens_hw_to_mC(const struct tsens_sensor *s, int field) { struct tsens_priv *priv = s->priv; u32 resolution; @@ -160,7 +168,7 @@ static int tsens_hw_to_mC(struct tsens_sensor *s, int field) * * Return: ADC code or temperature in deciCelsius. */ -static int tsens_mC_to_hw(struct tsens_sensor *s, int temp) +static int tsens_mC_to_hw(const struct tsens_sensor *s, int temp) { struct tsens_priv *priv = s->priv; @@ -189,6 +197,9 @@ static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id, case LOWER: index = LOW_INT_CLEAR_0 + hw_id; break; + case CRITICAL: + /* No critical interrupts before v2 */ + return; } regmap_field_write(priv->rf[index], enable ? 0 : 1); } @@ -214,6 +225,10 @@ static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id, index_mask = LOW_INT_MASK_0 + hw_id; index_clear = LOW_INT_CLEAR_0 + hw_id; break; + case CRITICAL: + index_mask = CRIT_INT_MASK_0 + hw_id; + index_clear = CRIT_INT_CLEAR_0 + hw_id; + break; } if (enable) { @@ -268,14 +283,23 @@ static int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id, 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) + + if (priv->feat->crit_int) { + ret = regmap_field_read(priv->rf[CRITICAL_STATUS_0 + hw_id], + &d->crit_viol); + if (ret) + return ret; + } + + if (d->up_viol || d->low_viol || d->crit_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) + const struct tsens_sensor *s, + struct tsens_irq_data *d) { int ret; @@ -292,22 +316,37 @@ static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id, ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask); if (ret) return ret; + ret = regmap_field_read(priv->rf[CRIT_INT_CLEAR_0 + hw_id], + &d->crit_irq_clear); + if (ret) + return ret; + ret = regmap_field_read(priv->rf[CRIT_INT_MASK_0 + hw_id], + &d->crit_irq_mask); + if (ret) + return ret; + + d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id); } else { /* No mask register on older TSENS */ d->up_irq_mask = 0; d->low_irq_mask = 0; + d->crit_irq_clear = 0; + d->crit_irq_mask = 0; + d->crit_thresh = 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); + dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n", + hw_id, __func__, + (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "", + d->low_viol, d->up_viol, d->crit_viol, + d->low_irq_clear, d->up_irq_clear, d->crit_irq_clear, + d->low_irq_mask, d->up_irq_mask, d->crit_irq_mask); + dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d:%d)\n", hw_id, __func__, + (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "", + d->low_thresh, d->up_thresh, d->crit_thresh); return 0; } @@ -322,6 +361,76 @@ static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver) } /** + * tsens_critical_irq_thread() - Threaded handler for critical interrupts + * @irq: irq number + * @data: tsens controller private data + * + * Check FSM watchdog bark status and clear if needed. + * Check all sensors to find ones that violated their critical threshold limits. + * Clear and then re-enable the interrupt. + * + * The level-triggered interrupt might deassert if the temperature returned to + * within the threshold limits by the time the handler got scheduled. We + * consider the irq to have been handled in that case. + * + * Return: IRQ_HANDLED + */ +irqreturn_t tsens_critical_irq_thread(int irq, void *data) +{ + struct tsens_priv *priv = data; + struct tsens_irq_data d; + int temp, ret, i; + u32 wdog_status, wdog_count; + + if (priv->feat->has_watchdog) { + ret = regmap_field_read(priv->rf[WDOG_BARK_STATUS], + &wdog_status); + if (ret) + return ret; + + if (wdog_status) { + /* Clear WDOG interrupt */ + regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 1); + regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 0); + ret = regmap_field_read(priv->rf[WDOG_BARK_COUNT], + &wdog_count); + if (ret) + return ret; + if (wdog_count) + dev_dbg(priv->dev, "%s: watchdog count: %d\n", + __func__, wdog_count); + + /* Fall through to handle critical interrupts if any */ + } + } + + for (i = 0; i < priv->num_sensors; i++) { + const struct tsens_sensor *s = &priv->sensor[i]; + u32 hw_id = s->hw_id; + + if (IS_ERR(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] %s: error reading sensor\n", + hw_id, __func__); + continue; + } + + tsens_read_irq_state(priv, hw_id, s, &d); + if (d.crit_viol && + !masked_irq(hw_id, d.crit_irq_mask, tsens_version(priv))) { + /* Mask critical interrupts, unused on Linux */ + tsens_set_interrupt(priv, hw_id, CRITICAL, false); + } + } + + return IRQ_HANDLED; +} + +/** * tsens_irq_thread - Threaded interrupt handler for uplow interrupts * @irq: irq number * @data: tsens controller private data @@ -346,10 +455,10 @@ irqreturn_t tsens_irq_thread(int irq, void *data) for (i = 0; i < priv->num_sensors; i++) { bool trigger = false; - struct tsens_sensor *s = &priv->sensor[i]; + const struct tsens_sensor *s = &priv->sensor[i]; u32 hw_id = s->hw_id; - if (IS_ERR(priv->sensor[i].tzd)) + if (IS_ERR(s->tzd)) continue; if (!tsens_threshold_violated(priv, hw_id, &d)) continue; @@ -368,7 +477,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data) 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__); + hw_id, __func__); tsens_set_interrupt(priv, hw_id, UPPER, enable); } else { trigger = true; @@ -379,7 +488,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data) 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__); + hw_id, __func__); tsens_set_interrupt(priv, hw_id, LOWER, enable); } else { trigger = true; @@ -392,7 +501,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data) 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_zone_device_update(s->tzd, THERMAL_EVENT_UNSPECIFIED); } else { dev_dbg(priv->dev, "[%u] %s: no violation: %d\n", @@ -435,7 +544,7 @@ int tsens_set_trips(void *_sensor, int low, int high) 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); + hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high); return 0; } @@ -457,7 +566,7 @@ void tsens_disable_irq(struct tsens_priv *priv) regmap_field_write(priv->rf[INT_EN], 0); } -int get_temp_tsens_valid(struct tsens_sensor *s, int *temp) +int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp) { struct tsens_priv *priv = s->priv; int hw_id = s->hw_id; @@ -486,7 +595,7 @@ int get_temp_tsens_valid(struct tsens_sensor *s, int *temp) return 0; } -int get_temp_common(struct tsens_sensor *s, int *temp) +int get_temp_common(const struct tsens_sensor *s, int *temp) { struct tsens_priv *priv = s->priv; int hw_id = s->hw_id; @@ -590,6 +699,7 @@ int __init init_common(struct tsens_priv *priv) { void __iomem *tm_base, *srot_base; struct device *dev = priv->dev; + u32 ver_minor; struct resource *res; u32 enabled; int ret, i, j; @@ -602,7 +712,7 @@ int __init init_common(struct tsens_priv *priv) /* DT with separate SROT and TM address space */ priv->tm_offset = 0; res = platform_get_resource(op, IORESOURCE_MEM, 1); - srot_base = devm_ioremap_resource(&op->dev, res); + srot_base = devm_ioremap_resource(dev, res); if (IS_ERR(srot_base)) { ret = PTR_ERR(srot_base); goto err_put_device; @@ -620,7 +730,7 @@ int __init init_common(struct tsens_priv *priv) } res = platform_get_resource(op, IORESOURCE_MEM, 0); - tm_base = devm_ioremap_resource(&op->dev, res); + tm_base = devm_ioremap_resource(dev, res); if (IS_ERR(tm_base)) { ret = PTR_ERR(tm_base); goto err_put_device; @@ -639,6 +749,9 @@ int __init init_common(struct tsens_priv *priv) if (IS_ERR(priv->rf[i])) return PTR_ERR(priv->rf[i]); } + ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor); + if (ret) + goto err_put_device; } priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map, @@ -683,12 +796,47 @@ int __init init_common(struct tsens_priv *priv) } } + if (priv->feat->crit_int) { + /* Loop might need changes if enum regfield_ids is reordered */ + for (j = CRITICAL_STATUS_0; j <= CRIT_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; + } + } + } + } + + if (tsens_version(priv) > VER_1_X && ver_minor > 2) { + /* Watchdog is present only on v2.3+ */ + priv->feat->has_watchdog = 1; + for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) { + priv->rf[i] = devm_regmap_field_alloc(dev, priv->tm_map, + priv->fields[i]); + if (IS_ERR(priv->rf[i])) { + ret = PTR_ERR(priv->rf[i]); + goto err_put_device; + } + } + /* + * Watchdog is already enabled, unmask the bark. + * Disable cycle completion monitoring + */ + regmap_field_write(priv->rf[WDOG_BARK_MASK], 0); + regmap_field_write(priv->rf[CC_MON_MASK], 1); + } + spin_lock_init(&priv->ul_lock); tsens_enable_irq(priv); tsens_debug_init(op); - return 0; - err_put_device: put_device(&op->dev); return ret; diff --git a/drivers/thermal/qcom/tsens-v0_1.c b/drivers/thermal/qcom/tsens-v0_1.c index 4b8dd6de02ce..959a9371d205 100644 --- a/drivers/thermal/qcom/tsens-v0_1.c +++ b/drivers/thermal/qcom/tsens-v0_1.c @@ -327,7 +327,7 @@ static int calibrate_8974(struct tsens_priv *priv) /* v0.1: 8916, 8974 */ -static const struct tsens_features tsens_v0_1_feat = { +static struct tsens_features tsens_v0_1_feat = { .ver_major = VER_0_1, .crit_int = 0, .adc = 1, @@ -377,7 +377,7 @@ static const struct tsens_ops ops_8916 = { .get_temp = get_temp_common, }; -const struct tsens_plat_data data_8916 = { +struct tsens_plat_data data_8916 = { .num_sensors = 5, .ops = &ops_8916, .hw_ids = (unsigned int []){0, 1, 2, 4, 5 }, @@ -392,7 +392,7 @@ static const struct tsens_ops ops_8974 = { .get_temp = get_temp_common, }; -const struct tsens_plat_data data_8974 = { +struct tsens_plat_data data_8974 = { .num_sensors = 11, .ops = &ops_8974, .feat = &tsens_v0_1_feat, diff --git a/drivers/thermal/qcom/tsens-v1.c b/drivers/thermal/qcom/tsens-v1.c index bd2ddb684a45..b682a4df0081 100644 --- a/drivers/thermal/qcom/tsens-v1.c +++ b/drivers/thermal/qcom/tsens-v1.c @@ -299,7 +299,7 @@ static int calibrate_8976(struct tsens_priv *priv) /* v1.x: msm8956,8976,qcs404,405 */ -static const struct tsens_features tsens_v1_feat = { +static struct tsens_features tsens_v1_feat = { .ver_major = VER_1_X, .crit_int = 0, .adc = 1, @@ -368,7 +368,7 @@ static const struct tsens_ops ops_generic_v1 = { .get_temp = get_temp_tsens_valid, }; -const struct tsens_plat_data data_tsens_v1 = { +struct tsens_plat_data data_tsens_v1 = { .ops = &ops_generic_v1, .feat = &tsens_v1_feat, .fields = tsens_v1_regfields, @@ -381,7 +381,7 @@ static const struct tsens_ops ops_8976 = { }; /* Valid for both MSM8956 and MSM8976. Sensor ID 3 is unused. */ -const struct tsens_plat_data data_8976 = { +struct tsens_plat_data data_8976 = { .num_sensors = 11, .ops = &ops_8976, .hw_ids = (unsigned int[]){0, 1, 2, 4, 5, 6, 7, 8, 9, 10}, diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c index a4d15e1abfdd..b293ed32174b 100644 --- a/drivers/thermal/qcom/tsens-v2.c +++ b/drivers/thermal/qcom/tsens-v2.c @@ -24,10 +24,11 @@ #define TM_Sn_CRITICAL_THRESHOLD_OFF 0x0060 #define TM_Sn_STATUS_OFF 0x00a0 #define TM_TRDY_OFF 0x00e4 +#define TM_WDOG_LOG_OFF 0x013c /* v2.x: 8996, 8998, sdm845 */ -static const struct tsens_features tsens_v2_feat = { +static struct tsens_features tsens_v2_feat = { .ver_major = VER_2_X, .crit_int = 1, .adc = 0, @@ -51,8 +52,9 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = { [INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 2), /* TEMPERATURE THRESHOLDS */ - REG_FIELD_FOR_EACH_SENSOR16(LOW_THRESH, TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 0, 11), - REG_FIELD_FOR_EACH_SENSOR16(UP_THRESH, TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 12, 23), + REG_FIELD_FOR_EACH_SENSOR16(LOW_THRESH, TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 0, 11), + REG_FIELD_FOR_EACH_SENSOR16(UP_THRESH, TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 12, 23), + REG_FIELD_FOR_EACH_SENSOR16(CRIT_THRESH, TM_Sn_CRITICAL_THRESHOLD_OFF, 0, 11), /* INTERRUPTS [CLEAR/STATUS/MASK] */ REG_FIELD_SPLIT_BITS_0_15(LOW_INT_STATUS, TM_UPPER_LOWER_INT_STATUS_OFF), @@ -61,6 +63,18 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = { REG_FIELD_SPLIT_BITS_16_31(UP_INT_STATUS, TM_UPPER_LOWER_INT_STATUS_OFF), REG_FIELD_SPLIT_BITS_16_31(UP_INT_CLEAR, TM_UPPER_LOWER_INT_CLEAR_OFF), REG_FIELD_SPLIT_BITS_16_31(UP_INT_MASK, TM_UPPER_LOWER_INT_MASK_OFF), + REG_FIELD_SPLIT_BITS_0_15(CRIT_INT_STATUS, TM_CRITICAL_INT_STATUS_OFF), + REG_FIELD_SPLIT_BITS_0_15(CRIT_INT_CLEAR, TM_CRITICAL_INT_CLEAR_OFF), + REG_FIELD_SPLIT_BITS_0_15(CRIT_INT_MASK, TM_CRITICAL_INT_MASK_OFF), + + /* WATCHDOG on v2.3 or later */ + [WDOG_BARK_STATUS] = REG_FIELD(TM_CRITICAL_INT_STATUS_OFF, 31, 31), + [WDOG_BARK_CLEAR] = REG_FIELD(TM_CRITICAL_INT_CLEAR_OFF, 31, 31), + [WDOG_BARK_MASK] = REG_FIELD(TM_CRITICAL_INT_MASK_OFF, 31, 31), + [CC_MON_STATUS] = REG_FIELD(TM_CRITICAL_INT_STATUS_OFF, 30, 30), + [CC_MON_CLEAR] = REG_FIELD(TM_CRITICAL_INT_CLEAR_OFF, 30, 30), + [CC_MON_MASK] = REG_FIELD(TM_CRITICAL_INT_MASK_OFF, 30, 30), + [WDOG_BARK_COUNT] = REG_FIELD(TM_WDOG_LOG_OFF, 0, 7), /* Sn_STATUS */ REG_FIELD_FOR_EACH_SENSOR16(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 11), @@ -81,14 +95,14 @@ static const struct tsens_ops ops_generic_v2 = { .get_temp = get_temp_tsens_valid, }; -const struct tsens_plat_data data_tsens_v2 = { +struct tsens_plat_data data_tsens_v2 = { .ops = &ops_generic_v2, .feat = &tsens_v2_feat, .fields = tsens_v2_regfields, }; /* Kept around for backward compatibility with old msm8996.dtsi */ -const struct tsens_plat_data data_8996 = { +struct tsens_plat_data data_8996 = { .num_sensors = 13, .ops = &ops_generic_v2, .feat = &tsens_v2_feat, diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index 0e7cf5236932..2f77d235cf73 100644 --- a/drivers/thermal/qcom/tsens.c +++ b/drivers/thermal/qcom/tsens.c @@ -85,11 +85,42 @@ static const struct thermal_zone_of_device_ops tsens_of_ops = { .set_trips = tsens_set_trips, }; +static int tsens_register_irq(struct tsens_priv *priv, char *irqname, + irq_handler_t thread_fn) +{ + struct platform_device *pdev; + int ret, irq; + + pdev = of_find_device_by_node(priv->dev->of_node); + if (!pdev) + return -ENODEV; + + irq = platform_get_irq_byname(pdev, irqname); + if (irq < 0) { + ret = irq; + /* For old DTs with no IRQ defined */ + if (irq == -ENXIO) + ret = 0; + } else { + ret = devm_request_threaded_irq(&pdev->dev, irq, + NULL, thread_fn, + IRQF_ONESHOT, + dev_name(&pdev->dev), priv); + if (ret) + dev_err(&pdev->dev, "%s: failed to get irq\n", + __func__); + else + enable_irq_wake(irq); + } + + put_device(&pdev->dev); + return ret; +} + static int tsens_register(struct tsens_priv *priv) { - int i, ret, irq; + int i, ret; struct thermal_zone_device *tzd; - struct platform_device *pdev; for (i = 0; i < priv->num_sensors; i++) { priv->sensor[i].priv = priv; @@ -103,32 +134,14 @@ static int tsens_register(struct tsens_priv *priv) priv->ops->enable(priv, i); } - pdev = of_find_device_by_node(priv->dev->of_node); - if (!pdev) - return -ENODEV; - - irq = platform_get_irq_byname(pdev, "uplow"); - if (irq < 0) { - ret = irq; - /* For old DTs with no IRQ defined */ - if (irq == -ENXIO) - ret = 0; - goto err_put_device; - } - - 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; - } + ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); + if (ret < 0) + return ret; - enable_irq_wake(irq); + if (priv->feat->crit_int) + ret = tsens_register_irq(priv, "critical", + tsens_critical_irq_thread); -err_put_device: - put_device(&pdev->dev); return ret; } diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h index e24a865fbc34..502acf0e6828 100644 --- a/drivers/thermal/qcom/tsens.h +++ b/drivers/thermal/qcom/tsens.h @@ -23,6 +23,7 @@ struct tsens_priv; +/* IP version numbers in ascending order */ enum tsens_ver { VER_0_1 = 0, VER_1_X, @@ -32,6 +33,7 @@ enum tsens_ver { enum tsens_irq_type { LOWER, UPPER, + CRITICAL, }; /** @@ -67,7 +69,7 @@ struct tsens_ops { /* mandatory callbacks */ int (*init)(struct tsens_priv *priv); int (*calibrate)(struct tsens_priv *priv); - int (*get_temp)(struct tsens_sensor *s, int *temp); + int (*get_temp)(const struct tsens_sensor *s, int *temp); /* optional callbacks */ int (*enable)(struct tsens_priv *priv, int i); void (*disable)(struct tsens_priv *priv); @@ -374,6 +376,82 @@ enum regfield_ids { CRITICAL_STATUS_13, CRITICAL_STATUS_14, CRITICAL_STATUS_15, + CRIT_INT_STATUS_0, /* CRITICAL interrupt status */ + CRIT_INT_STATUS_1, + CRIT_INT_STATUS_2, + CRIT_INT_STATUS_3, + CRIT_INT_STATUS_4, + CRIT_INT_STATUS_5, + CRIT_INT_STATUS_6, + CRIT_INT_STATUS_7, + CRIT_INT_STATUS_8, + CRIT_INT_STATUS_9, + CRIT_INT_STATUS_10, + CRIT_INT_STATUS_11, + CRIT_INT_STATUS_12, + CRIT_INT_STATUS_13, + CRIT_INT_STATUS_14, + CRIT_INT_STATUS_15, + CRIT_INT_CLEAR_0, /* CRITICAL interrupt clear */ + CRIT_INT_CLEAR_1, + CRIT_INT_CLEAR_2, + CRIT_INT_CLEAR_3, + CRIT_INT_CLEAR_4, + CRIT_INT_CLEAR_5, + CRIT_INT_CLEAR_6, + CRIT_INT_CLEAR_7, + CRIT_INT_CLEAR_8, + CRIT_INT_CLEAR_9, + CRIT_INT_CLEAR_10, + CRIT_INT_CLEAR_11, + CRIT_INT_CLEAR_12, + CRIT_INT_CLEAR_13, + CRIT_INT_CLEAR_14, + CRIT_INT_CLEAR_15, + CRIT_INT_MASK_0, /* CRITICAL interrupt mask */ + CRIT_INT_MASK_1, + CRIT_INT_MASK_2, + CRIT_INT_MASK_3, + CRIT_INT_MASK_4, + CRIT_INT_MASK_5, + CRIT_INT_MASK_6, + CRIT_INT_MASK_7, + CRIT_INT_MASK_8, + CRIT_INT_MASK_9, + CRIT_INT_MASK_10, + CRIT_INT_MASK_11, + CRIT_INT_MASK_12, + CRIT_INT_MASK_13, + CRIT_INT_MASK_14, + CRIT_INT_MASK_15, + CRIT_THRESH_0, /* CRITICAL threshold values */ + CRIT_THRESH_1, + CRIT_THRESH_2, + CRIT_THRESH_3, + CRIT_THRESH_4, + CRIT_THRESH_5, + CRIT_THRESH_6, + CRIT_THRESH_7, + CRIT_THRESH_8, + CRIT_THRESH_9, + CRIT_THRESH_10, + CRIT_THRESH_11, + CRIT_THRESH_12, + CRIT_THRESH_13, + CRIT_THRESH_14, + CRIT_THRESH_15, + + /* WATCHDOG */ + WDOG_BARK_STATUS, + WDOG_BARK_CLEAR, + WDOG_BARK_MASK, + WDOG_BARK_COUNT, + + /* CYCLE COMPLETION MONITOR */ + CC_MON_STATUS, + CC_MON_CLEAR, + CC_MON_MASK, + MIN_STATUS_0, /* MIN threshold violated */ MIN_STATUS_1, MIN_STATUS_2, @@ -418,6 +496,7 @@ enum regfield_ids { * @adc: do the sensors only output adc code (instead of temperature)? * @srot_split: does the IP neatly splits the register space into SROT and TM, * with SROT only being available to secure boot firmware? + * @has_watchdog: does this IP support watchdog functionality? * @max_sensors: maximum sensors supported by this version of the IP */ struct tsens_features { @@ -425,6 +504,7 @@ struct tsens_features { unsigned int crit_int:1; unsigned int adc:1; unsigned int srot_split:1; + unsigned int has_watchdog:1; unsigned int max_sensors; }; @@ -440,12 +520,14 @@ struct tsens_plat_data { const u32 num_sensors; const struct tsens_ops *ops; unsigned int *hw_ids; - const struct tsens_features *feat; + struct tsens_features *feat; const struct reg_field *fields; }; /** * struct tsens_context - Registers to be saved/restored across a context loss + * @threshold: Threshold register value + * @control: Control register value */ struct tsens_context { int threshold; @@ -460,6 +542,8 @@ 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 + * @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 * @ctx: registers to be saved and restored during suspend/resume * @feat: features of the IP @@ -481,36 +565,37 @@ struct tsens_priv { struct regmap_field *rf[MAX_REGFIELDS]; struct tsens_context ctx; - const struct tsens_features *feat; + struct tsens_features *feat; const struct reg_field *fields; const struct tsens_ops *ops; struct dentry *debug_root; struct dentry *debug; - struct tsens_sensor sensor[0]; + struct tsens_sensor sensor[]; }; char *qfprom_read(struct device *dev, const char *cname); void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mode); int init_common(struct tsens_priv *priv); -int get_temp_tsens_valid(struct tsens_sensor *s, int *temp); -int get_temp_common(struct tsens_sensor *s, int *temp); +int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp); +int get_temp_common(const struct tsens_sensor *s, int *temp); int tsens_enable_irq(struct tsens_priv *priv); void tsens_disable_irq(struct tsens_priv *priv); int tsens_set_trips(void *_sensor, int low, int high); irqreturn_t tsens_irq_thread(int irq, void *data); +irqreturn_t tsens_critical_irq_thread(int irq, void *data); /* TSENS target */ -extern const struct tsens_plat_data data_8960; +extern struct tsens_plat_data data_8960; /* TSENS v0.1 targets */ -extern const struct tsens_plat_data data_8916, data_8974; +extern struct tsens_plat_data data_8916, data_8974; /* TSENS v1 targets */ -extern const struct tsens_plat_data data_tsens_v1, data_8976; +extern struct tsens_plat_data data_tsens_v1, data_8976; /* TSENS v2 targets */ -extern const struct tsens_plat_data data_8996, data_tsens_v2; +extern struct tsens_plat_data data_8996, data_tsens_v2; #endif /* __QCOM_TSENS_H__ */ |