mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 09:20:13 -04:00
Merge tag 'thermal-v7.3-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/thermal/linux
Pull thermal driver updates for 7.3-rc1 from Daniel Lezcano: "- Move thermal_zone_device_enable() to the right place in order to prevent calling it if the thermal zone registration failed (Dan Carpenter) - Improve bitfield manipulations on Armada (Bryan B. Lima) - Remove unneeded 'fast_io' on Sun8i and Armada (Wolfram Sang) - Fix wrong boundary when clamping the low values in the set_trips() callback and fix wrong mask when setting the temperature interval on Airoha (Christian Marangi) - Make use of the regmap API in order to support the Airoha AN7583 (Christian Marangi) - Fix adc_tm5_get_temp() return check value on the QCom SPMI ADC sensor (Rakesh Kota) - Fix unbalanced clock enablement when the resume fails on the iMX driver (Can Peng) - Add Qualcomm Master BandGap thermal monitor support (Satya Priya Kakitapalli) - Add Maili Temperature bindings compatible (Haritha S K) - Add a devm action to clean hardware interrupts, sampling, and control registers on Spacemit K1 (Pei Xiao) - Fix trivial typo in an OF code comment (Marek Vasut) - Remove unecessary print on Qcom SPMI ADC driver when a call to devm_request_threaded_irq() fails as this one already prints a message (Jishnu Prakash) - Add support for QCom PMIC5 Gen3 ADC by using auxialiary driver and shared interrupt with the IIO driver (Jishnu Prakash) - Make resets optional on MT8196 and add the corresponding property in the DT bindings (AngeloGioacchino Del Regno) - Fix clock staying enabled with the resume operation fails on Qoriq (Can Peng) - Fix wrong closing brace position in thermal library header (Andreas Haufler) - Fix low and high trip point validation by moving the check after the clamp on the spacemit driver (surendra) - Remove redundant error messages on IRQ request failure (Pan Chuang) * tag 'thermal-v7.3-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/thermal/linux: (26 commits) thermal/drivers: Remove redundant error messages on IRQ request failure thermal/drivers/spacemit: Validate clamped trip thresholds tools/lib/thermal: Fix misplaced extern "C" closing brace thermal/drivers/qoriq: Disable clock on resume failure thermal/drivers/mediatek/lvts_thermal: Make reset optional for MT8196 dt-bindings: thermal: mediatek: Make resets optional for MT8196 thermal/drivers/qcom: add support for PMIC5 Gen3 ADC thermal monitoring iio: adc: qcom-spmi-adc5-gen3: Share SDAM0 IRQ with ADC_TM auxiliary driver iio: adc: qcom-spmi-adc5-gen3: Remove an unnecessary print thermal/of: Fix trivial enabled typo thermal/drivers/spacemit/k1: Add shutdown action and reorder registration order dt-bindings: thermal: qcom-tsens: Document the Maili Temperature Sensor thermal/drivers/qcom: Add support for Qualcomm MBG thermal monitoring dt-bindings: thermal: Add Qualcomm MBG thermal monitor support thermal/drivers/imx: Disable clock on runtime resume failure thermal/drivers/qcom-spmi-adc-tm5: Drop IIO_VAL_INT check in adc_tm5_get_temp thermal/drivers/airoha: Add support for AN7583 Thermal Sensor dt-bindings: arm: airoha: Add the chip-scu node for AN7583 SoC thermal/drivers/airoha: Generalize get_thermal_ADC and set_mux function thermal/drivers/airoha: Generalize probe function ...
This commit is contained in:
@@ -19,15 +19,29 @@ properties:
|
||||
items:
|
||||
- enum:
|
||||
- airoha,en7581-chip-scu
|
||||
- airoha,an7583-chip-scu
|
||||
- const: syscon
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
'#thermal-sensor-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: airoha,en7581-chip-scu
|
||||
|
||||
then:
|
||||
properties:
|
||||
'#thermal-sensor-cells': false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
||||
@@ -193,6 +193,10 @@ patternProperties:
|
||||
type: object
|
||||
$ref: /schemas/thermal/qcom,spmi-temp-alarm.yaml#
|
||||
|
||||
"^temperature-sensor@[0-9a-f]+$":
|
||||
type: object
|
||||
$ref: /schemas/thermal/qcom,pm8775-mbg-tm.yaml#
|
||||
|
||||
"^typec@[0-9a-f]+$":
|
||||
type: object
|
||||
$ref: /schemas/usb/qcom,pmic-typec.yaml#
|
||||
|
||||
@@ -94,12 +94,23 @@ allOf:
|
||||
nvmem-cell-names:
|
||||
minItems: 2
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
not:
|
||||
contains:
|
||||
enum:
|
||||
- mediatek,mt8196-lvts-ap
|
||||
- mediatek,mt8196-lvts-mcu
|
||||
then:
|
||||
required:
|
||||
- resets
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- resets
|
||||
- nvmem-cells
|
||||
- nvmem-cell-names
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/thermal/qcom,pm8775-mbg-tm.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm's SPMI PMIC MBG Thermal Monitoring
|
||||
|
||||
maintainers:
|
||||
- Jishnu Prakash <jishnu.prakash@oss.qualcomm.com>
|
||||
- Kamal Wadhwa <kamal.wadhwa@oss.qualcomm.com>
|
||||
|
||||
description:
|
||||
Qualcomm's MBG(Master Bandgap) temperature alarm monitors the die
|
||||
temperature and generates an interrupt if the PMIC die temperature is
|
||||
over a set of programmable temperature thresholds. It allows monitoring
|
||||
for both hot and cold, LVL1 and LVL2 thresholds, which makes it different
|
||||
from the existing temp alarm peripheral. The interrupt comes over SPMI
|
||||
and the MBG's fault status register gives details to understand whether
|
||||
it is a hot/cold and LVL1/LVL2 violation.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,pm8775-mbg-tm
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
io-channels:
|
||||
items:
|
||||
- description: ADC channel, which reports chip die temperature.
|
||||
|
||||
io-channel-names:
|
||||
items:
|
||||
- const: thermal
|
||||
|
||||
'#thermal-sensor-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- io-channels
|
||||
- io-channel-names
|
||||
|
||||
allOf:
|
||||
- $ref: thermal-sensor.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
pmic {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
temperature-sensor@d700 {
|
||||
compatible = "qcom,pm8775-mbg-tm";
|
||||
reg = <0xd700>;
|
||||
interrupts = <0x1 0xd7 0x0 IRQ_TYPE_EDGE_RISING>;
|
||||
io-channels = <&pm8775_adc 0x3>;
|
||||
io-channel-names = "thermal";
|
||||
#thermal-sensor-cells = <0>;
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -58,6 +58,7 @@ properties:
|
||||
- qcom,glymur-tsens
|
||||
- qcom,hawi-tsens
|
||||
- qcom,kaanapali-tsens
|
||||
- qcom,maili-tsens
|
||||
- qcom,milos-tsens
|
||||
- qcom,nord-tsens
|
||||
- qcom,msm8953-tsens
|
||||
|
||||
@@ -55,9 +55,6 @@ struct adc5_channel_prop {
|
||||
* requests from multiple clients.
|
||||
* @data: software configuration data.
|
||||
* @n_tm_channels: number of ADC channels used for TM measurements.
|
||||
* @handler: TM callback to be called for threshold violation interrupt
|
||||
* on first SDAM.
|
||||
* @tm_aux: pointer to auxiliary TM device.
|
||||
*/
|
||||
struct adc5_chip {
|
||||
struct device *dev;
|
||||
@@ -69,8 +66,6 @@ struct adc5_chip {
|
||||
struct mutex lock;
|
||||
const struct adc5_data *data;
|
||||
unsigned int n_tm_channels;
|
||||
void (*handler)(struct auxiliary_device *tm_aux);
|
||||
struct auxiliary_device *tm_aux;
|
||||
};
|
||||
|
||||
int adc5_gen3_read(struct adc5_device_data *adc, unsigned int sdam_index,
|
||||
@@ -286,23 +281,21 @@ static irqreturn_t adc5_gen3_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct adc5_chip *adc = dev_id;
|
||||
struct device *dev = adc->dev;
|
||||
struct auxiliary_device *adev;
|
||||
u8 status, eoc_status, val;
|
||||
u8 tm_status[2];
|
||||
int ret;
|
||||
|
||||
ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM,
|
||||
ADC5_GEN3_STATUS1, &status, sizeof(status));
|
||||
if (ret) {
|
||||
dev_err(dev, "adc read status1 failed with %d\n", ret);
|
||||
return IRQ_HANDLED;
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM,
|
||||
ADC5_GEN3_EOC_STS, &eoc_status, sizeof(eoc_status));
|
||||
if (ret) {
|
||||
dev_err(dev, "adc read eoc status failed with %d\n", ret);
|
||||
return IRQ_HANDLED;
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (status & ADC5_GEN3_STATUS1_CONV_FAULT) {
|
||||
@@ -315,30 +308,13 @@ static irqreturn_t adc5_gen3_isr(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Interrupt status:%#x, EOC status:%#x\n", status, eoc_status);
|
||||
|
||||
/* CHAN0 is the preconfigured channel for immediate conversion */
|
||||
if (eoc_status & ADC5_GEN3_EOC_CHAN_0)
|
||||
complete(&adc->complete);
|
||||
|
||||
ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM,
|
||||
ADC5_GEN3_TM_HIGH_STS, tm_status, sizeof(tm_status));
|
||||
if (ret) {
|
||||
dev_err(dev, "adc read TM status failed with %d\n", ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Interrupt status:%#x, EOC status:%#x, high:%#x, low:%#x\n",
|
||||
status, eoc_status, tm_status[0], tm_status[1]);
|
||||
|
||||
if (tm_status[0] || tm_status[1]) {
|
||||
adev = adc->tm_aux;
|
||||
if (!adev || !adev->dev.driver) {
|
||||
dev_err(dev, "adc_tm auxiliary device not initialized\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
adc->handler(adev);
|
||||
}
|
||||
if (!(eoc_status & ADC5_GEN3_EOC_CHAN_0))
|
||||
return IRQ_NONE;
|
||||
|
||||
complete(&adc->complete);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@@ -683,8 +659,6 @@ static int adc5_gen3_add_aux_tm_device(struct adc5_chip *adc)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adc->tm_aux = &aux_device->aux_dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -740,16 +714,6 @@ int adc5_gen3_therm_code_to_temp(struct device *dev,
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(adc5_gen3_therm_code_to_temp, "QCOM_SPMI_ADC5_GEN3");
|
||||
|
||||
void adc5_gen3_register_tm_event_notifier(struct device *dev,
|
||||
void (*handler)(struct auxiliary_device *))
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev->parent);
|
||||
struct adc5_chip *adc = iio_priv(indio_dev);
|
||||
|
||||
adc->handler = handler;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(adc5_gen3_register_tm_event_notifier, "QCOM_SPMI_ADC5_GEN3");
|
||||
|
||||
static int adc5_gen3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@@ -816,14 +780,19 @@ static int adc5_gen3_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq,
|
||||
adc5_gen3_isr, 0,
|
||||
adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq_name,
|
||||
adc);
|
||||
/*
|
||||
* This interrupt is shared with the ADC_TM auxiliary driver, which
|
||||
* is threaded and uses IRQF_ONESHOT. Since shared interrupts need
|
||||
* to agree on IRQF_ONESHOT configuration and there is a kernel
|
||||
* warning for using IRQF_ONESHOT with non-threaded interrupts,
|
||||
* make this also a threaded IRQ.
|
||||
*/
|
||||
ret = devm_request_threaded_irq(dev, adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq,
|
||||
NULL, adc5_gen3_isr, IRQF_ONESHOT | IRQF_SHARED,
|
||||
adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq_name,
|
||||
adc);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to request SDAM%d irq\n",
|
||||
ADC5_GEN3_VADC_SDAM);
|
||||
return ret;
|
||||
|
||||
ret = adc5_get_fw_data(adc);
|
||||
if (ret)
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
#define EN7581_DOUT_TADC 0x2f8
|
||||
#define EN7581_DOUT_TADC_MASK GENMASK(15, 0)
|
||||
|
||||
#define AN7583_MUX_SENSOR 0x2a0
|
||||
#define AN7583_LOAD_ADJ GENMASK(3, 2)
|
||||
#define AN7583_MUX_TADC 0x2e4
|
||||
#define AN7583_MUX_TADC_MASK GENMASK(3, 1)
|
||||
#define AN7583_DOUT_TADC 0x2f0
|
||||
|
||||
/* PTP_THERMAL regs */
|
||||
#define EN7581_TEMPMONCTL0 0x800
|
||||
#define EN7581_SENSE3_EN BIT(3)
|
||||
@@ -181,6 +187,11 @@
|
||||
#define EN7581_SCU_THERMAL_PROTECT_KEY 0x12
|
||||
#define EN7581_SCU_THERMAL_MUX_DIODE1 0x7
|
||||
|
||||
#define AN7583_SCU_THERMAL_PROTECT_KEY 0x80
|
||||
#define AN7583_NUM_SENSOR 3
|
||||
|
||||
#define AIROHA_THERMAL_NO_MUX_SENSOR -1
|
||||
|
||||
/* Convert temp to raw value as read from ADC ((((temp / 100) - init) * slope) / 1000) + offset */
|
||||
#define TEMP_TO_RAW(priv, temp) ((((((temp) / 100) - (priv)->init_temp) * \
|
||||
(priv)->default_slope) / 1000) + \
|
||||
@@ -193,42 +204,131 @@
|
||||
|
||||
#define AIROHA_MAX_SAMPLES 6
|
||||
|
||||
/*
|
||||
* AN7583 supports all these ADC mux but the original driver
|
||||
* always checked temp with the AN7583_BGP_TEMP_SENSOR.
|
||||
* Assume using the other sensor temperature is invalid and
|
||||
* always read from AN7583_BGP_TEMP_SENSOR.
|
||||
*
|
||||
* On top of this it's defined that AN7583 supports 3
|
||||
* sensor: AN7583_BGP_TEMP_SENSOR, AN7583_GBE_TEMP_SENSOR,
|
||||
* AN7583_CPU_TEMP_SENSOR.
|
||||
*
|
||||
* Provide the ADC mux for reference.
|
||||
*/
|
||||
enum an7583_thermal_adc_mux {
|
||||
AN7583_BGP_TEMP_SENSOR,
|
||||
AN7583_PAD_AVS,
|
||||
AN7583_CORE_POWER,
|
||||
AN7583_AVSDAC_OUT,
|
||||
AN7583_VCM,
|
||||
AN7583_GBE_TEMP_SENSOR,
|
||||
AN7583_CPU_TEMP_SENSOR,
|
||||
|
||||
AN7583_ADC_MUX_MAX,
|
||||
};
|
||||
|
||||
enum an7583_thermal_diode_mux {
|
||||
AN7583_D0_TADC,
|
||||
AN7583_ZERO_TADC,
|
||||
AN7583_D1_TADC,
|
||||
};
|
||||
|
||||
enum airoha_thermal_chip_scu_field {
|
||||
AIROHA_THERMAL_DOUT_TADC,
|
||||
AIROHA_THERMAL_MUX_SENSOR,
|
||||
AIROHA_THERMAL_MUX_TADC,
|
||||
|
||||
/* keep last */
|
||||
AIROHA_THERMAL_FIELD_MAX,
|
||||
};
|
||||
|
||||
struct airoha_thermal_priv {
|
||||
void __iomem *base;
|
||||
struct regmap *map;
|
||||
struct regmap *chip_scu;
|
||||
struct regmap_field *chip_scu_fields[AIROHA_THERMAL_FIELD_MAX];
|
||||
struct resource scu_adc_res;
|
||||
|
||||
u32 pllrg_protect;
|
||||
int current_adc;
|
||||
|
||||
struct thermal_zone_device *tz;
|
||||
int init_temp;
|
||||
int default_slope;
|
||||
int default_offset;
|
||||
};
|
||||
|
||||
struct airoha_thermal_soc_data {
|
||||
u32 pllrg_protect;
|
||||
|
||||
const struct thermal_zone_device_ops *thdev_ops;
|
||||
int (*probe)(struct platform_device *pdev,
|
||||
struct airoha_thermal_priv *priv);
|
||||
int (*post_probe)(struct platform_device *pdev);
|
||||
};
|
||||
|
||||
static const unsigned int an7583_thermal_coeff[AN7583_ADC_MUX_MAX] = {
|
||||
[AN7583_BGP_TEMP_SENSOR] = 973,
|
||||
[AN7583_GBE_TEMP_SENSOR] = 995,
|
||||
[AN7583_CPU_TEMP_SENSOR] = 1035,
|
||||
};
|
||||
|
||||
static const unsigned int an7583_thermal_slope[AN7583_ADC_MUX_MAX] = {
|
||||
[AN7583_BGP_TEMP_SENSOR] = 7440,
|
||||
[AN7583_GBE_TEMP_SENSOR] = 7620,
|
||||
[AN7583_CPU_TEMP_SENSOR] = 8390,
|
||||
};
|
||||
|
||||
static const unsigned int an7583_thermal_offset[AN7583_ADC_MUX_MAX] = {
|
||||
[AN7583_BGP_TEMP_SENSOR] = 294,
|
||||
[AN7583_GBE_TEMP_SENSOR] = 298,
|
||||
[AN7583_CPU_TEMP_SENSOR] = 344,
|
||||
};
|
||||
|
||||
static int airoha_get_thermal_ADC(struct airoha_thermal_priv *priv)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
regmap_read(priv->chip_scu, EN7581_DOUT_TADC, &val);
|
||||
return FIELD_GET(EN7581_DOUT_TADC_MASK, val);
|
||||
regmap_field_read(priv->chip_scu_fields[AIROHA_THERMAL_DOUT_TADC],
|
||||
&val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void airoha_init_thermal_ADC_mode(struct airoha_thermal_priv *priv)
|
||||
static void airoha_set_thermal_mux(struct airoha_thermal_priv *priv,
|
||||
int tdac_idx, int sensor_idx)
|
||||
{
|
||||
u32 adc_mux, pllrg;
|
||||
u32 pllrg;
|
||||
|
||||
/* Save PLLRG current value */
|
||||
regmap_read(priv->chip_scu, EN7581_PLLRG_PROTECT, &pllrg);
|
||||
|
||||
/* Give access to thermal regs */
|
||||
regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, EN7581_SCU_THERMAL_PROTECT_KEY);
|
||||
adc_mux = FIELD_PREP(EN7581_MUX_TADC, EN7581_SCU_THERMAL_MUX_DIODE1);
|
||||
regmap_write(priv->chip_scu, EN7581_PWD_TADC, adc_mux);
|
||||
/* Give access to Thermal regs */
|
||||
regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT,
|
||||
priv->pllrg_protect);
|
||||
|
||||
/*
|
||||
* Configure Thermal Sensor mux to sensor_idx.
|
||||
* (if not supported, sensor_idx is AIROHA_THERMAL_NO_MUX_SENSOR)
|
||||
*/
|
||||
if (sensor_idx != AIROHA_THERMAL_NO_MUX_SENSOR)
|
||||
regmap_field_write(priv->chip_scu_fields[AIROHA_THERMAL_MUX_SENSOR],
|
||||
sensor_idx);
|
||||
|
||||
/* Configure Thermal ADC mux to tdac_idx */
|
||||
if (priv->current_adc != tdac_idx) {
|
||||
regmap_field_write(priv->chip_scu_fields[AIROHA_THERMAL_MUX_TADC],
|
||||
tdac_idx);
|
||||
priv->current_adc = tdac_idx;
|
||||
}
|
||||
|
||||
/* Restore PLLRG value on exit */
|
||||
regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, pllrg);
|
||||
|
||||
/* Sleep 10 ms for Thermal ADC to enable */
|
||||
usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC);
|
||||
}
|
||||
|
||||
static int airoha_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
static int en7581_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
{
|
||||
struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz);
|
||||
int min_value, max_value, avg_value, value;
|
||||
@@ -253,7 +353,7 @@ static int airoha_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int airoha_thermal_set_trips(struct thermal_zone_device *tz, int low,
|
||||
static int en7581_thermal_set_trips(struct thermal_zone_device *tz, int low,
|
||||
int high)
|
||||
{
|
||||
struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz);
|
||||
@@ -265,44 +365,44 @@ static int airoha_thermal_set_trips(struct thermal_zone_device *tz, int low,
|
||||
RAW_TO_TEMP(priv, FIELD_MAX(EN7581_DOUT_TADC_MASK)));
|
||||
|
||||
/* We offset the high temp of 1°C to trigger correct event */
|
||||
writel(TEMP_TO_RAW(priv, high) >> 4,
|
||||
priv->base + EN7581_TEMPOFFSETH);
|
||||
regmap_write(priv->map, EN7581_TEMPOFFSETH,
|
||||
TEMP_TO_RAW(priv, high) >> 4);
|
||||
|
||||
enable_monitor = true;
|
||||
}
|
||||
|
||||
if (low != -INT_MAX) {
|
||||
/* Validate low and clamp it to a supported value */
|
||||
low = clamp_t(int, high, RAW_TO_TEMP(priv, 0),
|
||||
low = clamp_t(int, low, RAW_TO_TEMP(priv, 0),
|
||||
RAW_TO_TEMP(priv, FIELD_MAX(EN7581_DOUT_TADC_MASK)));
|
||||
|
||||
/* We offset the low temp of 1°C to trigger correct event */
|
||||
writel(TEMP_TO_RAW(priv, low) >> 4,
|
||||
priv->base + EN7581_TEMPOFFSETL);
|
||||
regmap_write(priv->map, EN7581_TEMPOFFSETL,
|
||||
TEMP_TO_RAW(priv, low) >> 4);
|
||||
|
||||
enable_monitor = true;
|
||||
}
|
||||
|
||||
/* Enable sensor 0 monitor after trip are set */
|
||||
if (enable_monitor)
|
||||
writel(EN7581_SENSE0_EN, priv->base + EN7581_TEMPMONCTL0);
|
||||
regmap_write(priv->map, EN7581_TEMPMONCTL0, EN7581_SENSE0_EN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_device_ops thdev_ops = {
|
||||
.get_temp = airoha_thermal_get_temp,
|
||||
.set_trips = airoha_thermal_set_trips,
|
||||
static const struct thermal_zone_device_ops en7581_thdev_ops = {
|
||||
.get_temp = en7581_thermal_get_temp,
|
||||
.set_trips = en7581_thermal_set_trips,
|
||||
};
|
||||
|
||||
static irqreturn_t airoha_thermal_irq(int irq, void *data)
|
||||
static irqreturn_t en7581_thermal_irq(int irq, void *data)
|
||||
{
|
||||
struct airoha_thermal_priv *priv = data;
|
||||
enum thermal_notify_event event;
|
||||
bool update = false;
|
||||
u32 status;
|
||||
u32 status = 0;
|
||||
|
||||
status = readl(priv->base + EN7581_TEMPMONINTSTS);
|
||||
regmap_read(priv->map, EN7581_TEMPMONINTSTS, &status);
|
||||
switch (status & (EN7581_HOFSINTSTS0 | EN7581_LOFSINTSTS0)) {
|
||||
case EN7581_HOFSINTSTS0:
|
||||
event = THERMAL_TRIP_VIOLATED;
|
||||
@@ -318,7 +418,7 @@ static irqreturn_t airoha_thermal_irq(int irq, void *data)
|
||||
}
|
||||
|
||||
/* Reset Interrupt */
|
||||
writel(status, priv->base + EN7581_TEMPMONINTSTS);
|
||||
regmap_write(priv->map, EN7581_TEMPMONINTSTS, status);
|
||||
|
||||
if (update)
|
||||
thermal_zone_device_update(priv->tz, event);
|
||||
@@ -326,21 +426,21 @@ static irqreturn_t airoha_thermal_irq(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void airoha_thermal_setup_adc_val(struct device *dev,
|
||||
static void en7581_thermal_setup_adc_val(struct device *dev,
|
||||
struct airoha_thermal_priv *priv)
|
||||
{
|
||||
u32 efuse_calib_info, cpu_sensor;
|
||||
u32 efuse_calib_info = 0;
|
||||
u32 cpu_sensor = 0;
|
||||
|
||||
/* Setup thermal sensor to ADC mode and setup the mux to DIODE1 */
|
||||
airoha_init_thermal_ADC_mode(priv);
|
||||
/* sleep 10 ms for ADC to enable */
|
||||
usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC);
|
||||
/* Setup Thermal Sensor to ADC mode and setup the mux to DIODE1 */
|
||||
airoha_set_thermal_mux(priv, EN7581_SCU_THERMAL_MUX_DIODE1,
|
||||
AIROHA_THERMAL_NO_MUX_SENSOR);
|
||||
|
||||
efuse_calib_info = readl(priv->base + EN7581_EFUSE_TEMP_OFFSET_REG);
|
||||
regmap_read(priv->map, EN7581_EFUSE_TEMP_OFFSET_REG, &efuse_calib_info);
|
||||
if (efuse_calib_info) {
|
||||
priv->default_offset = FIELD_GET(EN7581_EFUSE_TEMP_OFFSET, efuse_calib_info);
|
||||
/* Different slope are applied if the sensor is used for CPU or for package */
|
||||
cpu_sensor = readl(priv->base + EN7581_EFUSE_TEMP_CPU_SENSOR_REG);
|
||||
regmap_read(priv->map, EN7581_EFUSE_TEMP_CPU_SENSOR_REG, &cpu_sensor);
|
||||
if (cpu_sensor) {
|
||||
priv->default_slope = EN7581_SLOPE_X100_DIO_DEFAULT;
|
||||
priv->init_temp = EN7581_INIT_TEMP_FTK_X10;
|
||||
@@ -356,11 +456,11 @@ static void airoha_thermal_setup_adc_val(struct device *dev,
|
||||
}
|
||||
}
|
||||
|
||||
static void airoha_thermal_setup_monitor(struct airoha_thermal_priv *priv)
|
||||
static void en7581_thermal_setup_monitor(struct airoha_thermal_priv *priv)
|
||||
{
|
||||
/* Set measure mode */
|
||||
writel(FIELD_PREP(EN7581_MSRCTL0, EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4),
|
||||
priv->base + EN7581_TEMPMSRCTL0);
|
||||
regmap_write(priv->map, EN7581_TEMPMSRCTL0,
|
||||
FIELD_PREP(EN7581_MSRCTL0, EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4));
|
||||
|
||||
/*
|
||||
* Configure ADC valid reading addr
|
||||
@@ -375,15 +475,15 @@ static void airoha_thermal_setup_monitor(struct airoha_thermal_priv *priv)
|
||||
* We set valid instead of volt as we don't enable valid/volt
|
||||
* split reading and AHB read valid addr in such case.
|
||||
*/
|
||||
writel(priv->scu_adc_res.start + EN7581_DOUT_TADC,
|
||||
priv->base + EN7581_TEMPADCVALIDADDR);
|
||||
regmap_write(priv->map, EN7581_TEMPADCVALIDADDR,
|
||||
priv->scu_adc_res.start + EN7581_DOUT_TADC);
|
||||
|
||||
/*
|
||||
* Configure valid bit on a fake value of bit 16. The ADC outputs
|
||||
* max of 2 bytes for voltage.
|
||||
*/
|
||||
writel(FIELD_PREP(EN7581_ADV_RD_VALID_POS, 16),
|
||||
priv->base + EN7581_TEMPADCVALIDMASK);
|
||||
regmap_write(priv->map, EN7581_TEMPADCVALIDMASK,
|
||||
FIELD_PREP(EN7581_ADV_RD_VALID_POS, 16));
|
||||
|
||||
/*
|
||||
* AHB supports max 12 bytes for ADC voltage. Shift the read
|
||||
@@ -391,40 +491,53 @@ static void airoha_thermal_setup_monitor(struct airoha_thermal_priv *priv)
|
||||
* in the order of half a °C and is acceptable in the context
|
||||
* of triggering interrupt in critical condition.
|
||||
*/
|
||||
writel(FIELD_PREP(EN7581_ADC_VOLTAGE_SHIFT, 4),
|
||||
priv->base + EN7581_TEMPADCVOLTAGESHIFT);
|
||||
regmap_write(priv->map, EN7581_TEMPADCVOLTAGESHIFT,
|
||||
FIELD_PREP(EN7581_ADC_VOLTAGE_SHIFT, 4));
|
||||
|
||||
/* BUS clock is 300MHz counting unit is 3 * 68.64 * 256 = 52.715us */
|
||||
writel(FIELD_PREP(EN7581_PERIOD_UNIT, 3),
|
||||
priv->base + EN7581_TEMPMONCTL1);
|
||||
regmap_write(priv->map, EN7581_TEMPMONCTL1,
|
||||
FIELD_PREP(EN7581_PERIOD_UNIT, 3));
|
||||
|
||||
/*
|
||||
* filt interval is 1 * 52.715us = 52.715us,
|
||||
* sen interval is 379 * 52.715us = 19.97ms
|
||||
*/
|
||||
writel(FIELD_PREP(EN7581_FILT_INTERVAL, 1) |
|
||||
FIELD_PREP(EN7581_FILT_INTERVAL, 379),
|
||||
priv->base + EN7581_TEMPMONCTL2);
|
||||
regmap_write(priv->map, EN7581_TEMPMONCTL2,
|
||||
FIELD_PREP(EN7581_FILT_INTERVAL, 1) |
|
||||
FIELD_PREP(EN7581_SEN_INTERVAL, 379));
|
||||
|
||||
/* AHB poll is set to 146 * 68.64 = 10.02us */
|
||||
writel(FIELD_PREP(EN7581_ADC_POLL_INTVL, 146),
|
||||
priv->base + EN7581_TEMPAHBPOLL);
|
||||
regmap_write(priv->map, EN7581_TEMPAHBPOLL,
|
||||
FIELD_PREP(EN7581_ADC_POLL_INTVL, 146));
|
||||
}
|
||||
|
||||
static int airoha_thermal_probe(struct platform_device *pdev)
|
||||
static const struct regmap_config en7581_thermal_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
};
|
||||
|
||||
static const struct reg_field en7581_chip_scu_fields[AIROHA_THERMAL_FIELD_MAX] = {
|
||||
[AIROHA_THERMAL_DOUT_TADC] = REG_FIELD(EN7581_DOUT_TADC, 0, 15),
|
||||
[AIROHA_THERMAL_MUX_TADC] = REG_FIELD(EN7581_PWD_TADC, 1, 3),
|
||||
};
|
||||
|
||||
static int en7581_thermal_probe(struct platform_device *pdev,
|
||||
struct airoha_thermal_priv *priv)
|
||||
{
|
||||
struct airoha_thermal_priv *priv;
|
||||
struct device_node *chip_scu_np;
|
||||
struct device *dev = &pdev->dev;
|
||||
int irq, ret;
|
||||
void __iomem *base;
|
||||
int i, irq, ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
priv->map = devm_regmap_init_mmio(dev, base,
|
||||
&en7581_thermal_regmap_config);
|
||||
if (IS_ERR(priv->map))
|
||||
return PTR_ERR(priv->map);
|
||||
|
||||
chip_scu_np = of_parse_phandle(dev->of_node, "airoha,chip-scu", 0);
|
||||
if (!chip_scu_np)
|
||||
@@ -434,6 +547,23 @@ static int airoha_thermal_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->chip_scu))
|
||||
return PTR_ERR(priv->chip_scu);
|
||||
|
||||
for (i = 0; i < AIROHA_THERMAL_FIELD_MAX; i++) {
|
||||
struct regmap_field *field;
|
||||
|
||||
/* Skip registering MUX_SENSOR field as not supported */
|
||||
if (i == AIROHA_THERMAL_MUX_SENSOR)
|
||||
continue;
|
||||
|
||||
field = devm_regmap_field_alloc(dev, priv->chip_scu,
|
||||
en7581_chip_scu_fields[i]);
|
||||
if (IS_ERR(field)) {
|
||||
of_node_put(chip_scu_np);
|
||||
return PTR_ERR(field);
|
||||
}
|
||||
|
||||
priv->chip_scu_fields[i] = field;
|
||||
}
|
||||
|
||||
of_address_to_resource(chip_scu_np, 0, &priv->scu_adc_res);
|
||||
of_node_put(chip_scu_np);
|
||||
|
||||
@@ -442,18 +572,122 @@ static int airoha_thermal_probe(struct platform_device *pdev)
|
||||
return irq;
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
airoha_thermal_irq, IRQF_ONESHOT,
|
||||
en7581_thermal_irq, IRQF_ONESHOT,
|
||||
pdev->name, priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "Can't get interrupt working.\n");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
en7581_thermal_setup_monitor(priv);
|
||||
en7581_thermal_setup_adc_val(dev, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int en7581_thermal_post_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct airoha_thermal_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
/* Enable LOW and HIGH interrupt (if supported) */
|
||||
regmap_write(priv->map, EN7581_TEMPMONINT,
|
||||
EN7581_HOFSINTEN0 | EN7581_LOFSINTEN0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an7583_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
{
|
||||
struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz);
|
||||
int sensor_idx;
|
||||
int delta_diode, delta_gain;
|
||||
int coeff, slope, offset;
|
||||
|
||||
int diode_zero, diode_d0, diode_d1;
|
||||
|
||||
/* Always read sensor AN7583_BGP_TEMP_SENSOR */
|
||||
sensor_idx = AN7583_BGP_TEMP_SENSOR;
|
||||
|
||||
coeff = an7583_thermal_coeff[sensor_idx];
|
||||
slope = an7583_thermal_slope[sensor_idx];
|
||||
offset = an7583_thermal_offset[sensor_idx];
|
||||
|
||||
airoha_set_thermal_mux(priv, AN7583_ZERO_TADC, sensor_idx);
|
||||
diode_zero = airoha_get_thermal_ADC(priv);
|
||||
airoha_set_thermal_mux(priv, AN7583_D0_TADC, sensor_idx);
|
||||
diode_d0 = airoha_get_thermal_ADC(priv);
|
||||
airoha_set_thermal_mux(priv, AN7583_D1_TADC, sensor_idx);
|
||||
diode_d1 = airoha_get_thermal_ADC(priv);
|
||||
|
||||
delta_diode = diode_d1 - diode_d0;
|
||||
delta_gain = (delta_diode * coeff) / 100 + (diode_zero - diode_d1);
|
||||
if (!delta_gain)
|
||||
return -EINVAL;
|
||||
|
||||
*temp = (slope * delta_diode * 10) / delta_gain - offset * 10;
|
||||
*temp *= 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_device_ops an7583_tz_ops = {
|
||||
.get_temp = an7583_thermal_get_temp,
|
||||
};
|
||||
|
||||
static const struct reg_field an7583_chip_scu_fields[AIROHA_THERMAL_FIELD_MAX] = {
|
||||
[AIROHA_THERMAL_DOUT_TADC] = REG_FIELD(AN7583_DOUT_TADC, 0, 31),
|
||||
[AIROHA_THERMAL_MUX_TADC] = REG_FIELD(AN7583_MUX_TADC, 1, 3),
|
||||
[AIROHA_THERMAL_MUX_SENSOR] = REG_FIELD(AN7583_MUX_SENSOR, 2, 3),
|
||||
};
|
||||
|
||||
static int an7583_thermal_probe(struct platform_device *pdev,
|
||||
struct airoha_thermal_priv *priv)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int i;
|
||||
|
||||
priv->chip_scu = device_node_to_regmap(dev->of_node);
|
||||
if (IS_ERR(priv->chip_scu))
|
||||
return PTR_ERR(priv->chip_scu);
|
||||
|
||||
for (i = 0; i < AIROHA_THERMAL_FIELD_MAX; i++) {
|
||||
struct regmap_field *field;
|
||||
|
||||
field = devm_regmap_field_alloc(dev, priv->chip_scu,
|
||||
an7583_chip_scu_fields[i]);
|
||||
if (IS_ERR(field))
|
||||
return PTR_ERR(field);
|
||||
|
||||
priv->chip_scu_fields[i] = field;
|
||||
}
|
||||
|
||||
airoha_thermal_setup_monitor(priv);
|
||||
airoha_thermal_setup_adc_val(dev, priv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int airoha_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct airoha_thermal_soc_data *soc_data;
|
||||
struct airoha_thermal_priv *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
soc_data = device_get_match_data(dev);
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->pllrg_protect = soc_data->pllrg_protect;
|
||||
priv->current_adc = -1;
|
||||
|
||||
if (!soc_data->probe)
|
||||
return -EINVAL;
|
||||
|
||||
ret = soc_data->probe(pdev, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* register of thermal sensor and get info from DT */
|
||||
priv->tz = devm_thermal_of_zone_register(dev, 0, priv, &thdev_ops);
|
||||
priv->tz = devm_thermal_of_zone_register(dev, 0, priv,
|
||||
soc_data->thdev_ops);
|
||||
if (IS_ERR(priv->tz)) {
|
||||
dev_err(dev, "register thermal zone sensor failed\n");
|
||||
return PTR_ERR(priv->tz);
|
||||
@@ -461,15 +695,25 @@ static int airoha_thermal_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
/* Enable LOW and HIGH interrupt */
|
||||
writel(EN7581_HOFSINTEN0 | EN7581_LOFSINTEN0,
|
||||
priv->base + EN7581_TEMPMONINT);
|
||||
|
||||
return 0;
|
||||
return soc_data->post_probe ? soc_data->post_probe(pdev) : 0;
|
||||
}
|
||||
|
||||
static const struct airoha_thermal_soc_data en7581_data = {
|
||||
.pllrg_protect = EN7581_SCU_THERMAL_PROTECT_KEY,
|
||||
.thdev_ops = &en7581_thdev_ops,
|
||||
.probe = &en7581_thermal_probe,
|
||||
.post_probe = &en7581_thermal_post_probe,
|
||||
};
|
||||
|
||||
static const struct airoha_thermal_soc_data an7583_data = {
|
||||
.pllrg_protect = AN7583_SCU_THERMAL_PROTECT_KEY,
|
||||
.thdev_ops = &an7583_tz_ops,
|
||||
.probe = &an7583_thermal_probe,
|
||||
};
|
||||
|
||||
static const struct of_device_id airoha_thermal_match[] = {
|
||||
{ .compatible = "airoha,en7581-thermal" },
|
||||
{ .compatible = "airoha,en7581-thermal", .data = &en7581_data },
|
||||
{ .compatible = "airoha,an7583-chip-scu", .data = &an7583_data },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, airoha_thermal_match);
|
||||
|
||||
@@ -20,41 +20,35 @@
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
/* Thermal Manager Control and Status Register */
|
||||
#define PMU_TDC0_SW_RST_MASK (0x1 << 1)
|
||||
#define PMU_TM_DISABLE_OFFS 0
|
||||
#define PMU_TM_DISABLE_MASK (0x1 << PMU_TM_DISABLE_OFFS)
|
||||
#define PMU_TDC0_REF_CAL_CNT_OFFS 11
|
||||
#define PMU_TDC0_REF_CAL_CNT_MASK (0x1ff << PMU_TDC0_REF_CAL_CNT_OFFS)
|
||||
#define PMU_TDC0_OTF_CAL_MASK (0x1 << 30)
|
||||
#define PMU_TDC0_START_CAL_MASK (0x1 << 25)
|
||||
#define PMU_TDC0_SW_RST_MASK BIT(1)
|
||||
#define PMU_TM_DISABLE_MASK BIT(0)
|
||||
#define PMU_TDC0_REF_CAL_CNT_MASK GENMASK(19, 11)
|
||||
#define PMU_TDC0_OTF_CAL_MASK BIT(30)
|
||||
#define PMU_TDC0_START_CAL_MASK BIT(25)
|
||||
|
||||
#define A375_UNIT_CONTROL_SHIFT 27
|
||||
#define A375_UNIT_CONTROL_MASK 0x7
|
||||
#define A375_UNIT_CONTROL_MASK GENMASK(29, 27)
|
||||
#define A375_READOUT_INVERT BIT(15)
|
||||
#define A375_HW_RESETn BIT(8)
|
||||
|
||||
/* Errata fields */
|
||||
#define CONTROL0_TSEN_TC_TRIM_MASK 0x7
|
||||
#define CONTROL0_TSEN_TC_TRIM_MASK GENMASK(2, 0)
|
||||
#define CONTROL0_TSEN_TC_TRIM_VAL 0x3
|
||||
|
||||
#define CONTROL0_TSEN_START BIT(0)
|
||||
#define CONTROL0_TSEN_RESET BIT(1)
|
||||
#define CONTROL0_TSEN_ENABLE BIT(2)
|
||||
#define CONTROL0_TSEN_AVG_BYPASS BIT(6)
|
||||
#define CONTROL0_TSEN_CHAN_SHIFT 13
|
||||
#define CONTROL0_TSEN_CHAN_MASK 0xF
|
||||
#define CONTROL0_TSEN_OSR_SHIFT 24
|
||||
#define CONTROL0_TSEN_OSR_MAX 0x3
|
||||
#define CONTROL0_TSEN_MODE_SHIFT 30
|
||||
#define CONTROL0_TSEN_CHAN_MASK GENMASK(16, 13)
|
||||
#define CONTROL0_TSEN_OSR_MASK GENMASK(25, 24)
|
||||
#define CONTROL0_TSEN_OSR_MAX FIELD_MAX(CONTROL0_TSEN_OSR_MASK)
|
||||
#define CONTROL0_TSEN_MODE_MASK GENMASK(31, 30)
|
||||
#define CONTROL0_TSEN_MODE_EXTERNAL 0x2
|
||||
#define CONTROL0_TSEN_MODE_MASK 0x3
|
||||
|
||||
#define CONTROL1_TSEN_AVG_MASK 0x7
|
||||
#define CONTROL1_TSEN_AVG_MASK GENMASK(2, 0)
|
||||
#define CONTROL1_EXT_TSEN_SW_RESET BIT(7)
|
||||
#define CONTROL1_EXT_TSEN_HW_RESETn BIT(8)
|
||||
#define CONTROL1_TSEN_INT_EN BIT(25)
|
||||
#define CONTROL1_TSEN_SELECT_OFF 21
|
||||
#define CONTROL1_TSEN_SELECT_MASK 0x3
|
||||
#define CONTROL1_TSEN_SELECT_MASK GENMASK(22, 21)
|
||||
|
||||
#define STATUS_POLL_PERIOD_US 1000
|
||||
#define STATUS_POLL_TIMEOUT_US 100000
|
||||
@@ -140,23 +134,21 @@ static void armadaxp_init(struct platform_device *pdev,
|
||||
u32 reg;
|
||||
|
||||
regmap_read(priv->syscon, data->syscon_control1_off, ®);
|
||||
reg |= PMU_TDC0_OTF_CAL_MASK;
|
||||
FIELD_MODIFY(PMU_TDC0_OTF_CAL_MASK, ®, 1);
|
||||
|
||||
/* Reference calibration value */
|
||||
reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;
|
||||
reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS);
|
||||
FIELD_MODIFY(PMU_TDC0_REF_CAL_CNT_MASK, ®, 0xf1);
|
||||
|
||||
/* Reset the sensor */
|
||||
reg |= PMU_TDC0_SW_RST_MASK;
|
||||
|
||||
FIELD_MODIFY(PMU_TDC0_SW_RST_MASK, ®, 1);
|
||||
regmap_write(priv->syscon, data->syscon_control1_off, reg);
|
||||
|
||||
reg &= ~PMU_TDC0_SW_RST_MASK;
|
||||
FIELD_MODIFY(PMU_TDC0_SW_RST_MASK, ®, 0);
|
||||
regmap_write(priv->syscon, data->syscon_control1_off, reg);
|
||||
|
||||
/* Enable the sensor */
|
||||
regmap_read(priv->syscon, data->syscon_status_off, ®);
|
||||
reg &= ~PMU_TM_DISABLE_MASK;
|
||||
FIELD_MODIFY(PMU_TM_DISABLE_MASK, ®, 0);
|
||||
regmap_write(priv->syscon, data->syscon_status_off, reg);
|
||||
}
|
||||
|
||||
@@ -167,14 +159,13 @@ static void armada370_init(struct platform_device *pdev,
|
||||
u32 reg;
|
||||
|
||||
regmap_read(priv->syscon, data->syscon_control1_off, ®);
|
||||
reg |= PMU_TDC0_OTF_CAL_MASK;
|
||||
FIELD_MODIFY(PMU_TDC0_OTF_CAL_MASK, ®, 1);
|
||||
|
||||
/* Reference calibration value */
|
||||
reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;
|
||||
reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS);
|
||||
FIELD_MODIFY(PMU_TDC0_REF_CAL_CNT_MASK, ®, 0xf1);
|
||||
|
||||
/* Reset the sensor */
|
||||
reg &= ~PMU_TDC0_START_CAL_MASK;
|
||||
FIELD_MODIFY(PMU_TDC0_START_CAL_MASK, ®, 0);
|
||||
|
||||
regmap_write(priv->syscon, data->syscon_control1_off, reg);
|
||||
|
||||
@@ -188,14 +179,14 @@ static void armada375_init(struct platform_device *pdev,
|
||||
u32 reg;
|
||||
|
||||
regmap_read(priv->syscon, data->syscon_control1_off, ®);
|
||||
reg &= ~(A375_UNIT_CONTROL_MASK << A375_UNIT_CONTROL_SHIFT);
|
||||
reg &= ~A375_READOUT_INVERT;
|
||||
reg &= ~A375_HW_RESETn;
|
||||
FIELD_MODIFY(A375_UNIT_CONTROL_MASK, ®, 0);
|
||||
FIELD_MODIFY(A375_READOUT_INVERT, ®, 0);
|
||||
FIELD_MODIFY(A375_HW_RESETn, ®, 0);
|
||||
regmap_write(priv->syscon, data->syscon_control1_off, reg);
|
||||
|
||||
msleep(20);
|
||||
|
||||
reg |= A375_HW_RESETn;
|
||||
FIELD_MODIFY(A375_HW_RESETn, ®, 1);
|
||||
regmap_write(priv->syscon, data->syscon_control1_off, reg);
|
||||
|
||||
msleep(50);
|
||||
@@ -220,14 +211,13 @@ static void armada380_init(struct platform_device *pdev,
|
||||
|
||||
/* Disable the HW/SW reset */
|
||||
regmap_read(priv->syscon, data->syscon_control1_off, ®);
|
||||
reg |= CONTROL1_EXT_TSEN_HW_RESETn;
|
||||
reg &= ~CONTROL1_EXT_TSEN_SW_RESET;
|
||||
FIELD_MODIFY(CONTROL1_EXT_TSEN_HW_RESETn, ®, 1);
|
||||
FIELD_MODIFY(CONTROL1_EXT_TSEN_SW_RESET, ®, 0);
|
||||
regmap_write(priv->syscon, data->syscon_control1_off, reg);
|
||||
|
||||
/* Set Tsen Tc Trim to correct default value (errata #132698) */
|
||||
regmap_read(priv->syscon, data->syscon_control0_off, ®);
|
||||
reg &= ~CONTROL0_TSEN_TC_TRIM_MASK;
|
||||
reg |= CONTROL0_TSEN_TC_TRIM_VAL;
|
||||
FIELD_MODIFY(CONTROL0_TSEN_TC_TRIM_MASK, ®, CONTROL0_TSEN_TC_TRIM_VAL);
|
||||
regmap_write(priv->syscon, data->syscon_control0_off, reg);
|
||||
}
|
||||
|
||||
@@ -238,14 +228,15 @@ static void armada_ap80x_init(struct platform_device *pdev,
|
||||
u32 reg;
|
||||
|
||||
regmap_read(priv->syscon, data->syscon_control0_off, ®);
|
||||
reg &= ~CONTROL0_TSEN_RESET;
|
||||
reg |= CONTROL0_TSEN_START | CONTROL0_TSEN_ENABLE;
|
||||
FIELD_MODIFY(CONTROL0_TSEN_RESET, ®, 0);
|
||||
FIELD_MODIFY(CONTROL0_TSEN_START, ®, 1);
|
||||
FIELD_MODIFY(CONTROL0_TSEN_ENABLE, ®, 1);
|
||||
|
||||
/* Sample every ~2ms */
|
||||
reg |= CONTROL0_TSEN_OSR_MAX << CONTROL0_TSEN_OSR_SHIFT;
|
||||
FIELD_MODIFY(CONTROL0_TSEN_OSR_MASK, ®, CONTROL0_TSEN_OSR_MAX);
|
||||
|
||||
/* Enable average (2 samples by default) */
|
||||
reg &= ~CONTROL0_TSEN_AVG_BYPASS;
|
||||
FIELD_MODIFY(CONTROL0_TSEN_AVG_BYPASS, ®, 0);
|
||||
|
||||
regmap_write(priv->syscon, data->syscon_control0_off, reg);
|
||||
}
|
||||
@@ -260,13 +251,12 @@ static void armada_cp110_init(struct platform_device *pdev,
|
||||
|
||||
/* Sample every ~2ms */
|
||||
regmap_read(priv->syscon, data->syscon_control0_off, ®);
|
||||
reg |= CONTROL0_TSEN_OSR_MAX << CONTROL0_TSEN_OSR_SHIFT;
|
||||
FIELD_MODIFY(CONTROL0_TSEN_OSR_MASK, ®, CONTROL0_TSEN_OSR_MAX);
|
||||
regmap_write(priv->syscon, data->syscon_control0_off, reg);
|
||||
|
||||
/* Average the output value over 2^1 = 2 samples */
|
||||
regmap_read(priv->syscon, data->syscon_control1_off, ®);
|
||||
reg &= ~CONTROL1_TSEN_AVG_MASK;
|
||||
reg |= 1;
|
||||
FIELD_MODIFY(CONTROL1_TSEN_AVG_MASK, ®, 1);
|
||||
regmap_write(priv->syscon, data->syscon_control1_off, reg);
|
||||
}
|
||||
|
||||
@@ -313,7 +303,7 @@ armada_disable_overheat_interrupt(struct armada_thermal_priv *priv)
|
||||
u32 reg;
|
||||
|
||||
regmap_read(priv->syscon, data->syscon_control1_off, ®);
|
||||
reg &= ~CONTROL1_TSEN_INT_EN;
|
||||
FIELD_MODIFY(CONTROL1_TSEN_INT_EN, ®, 0);
|
||||
regmap_write(priv->syscon, data->syscon_control1_off, reg);
|
||||
}
|
||||
|
||||
@@ -331,20 +321,18 @@ static int armada_select_channel(struct armada_thermal_priv *priv, int channel)
|
||||
|
||||
/* Stop the measurements */
|
||||
regmap_read(priv->syscon, data->syscon_control0_off, &ctrl0);
|
||||
ctrl0 &= ~CONTROL0_TSEN_START;
|
||||
FIELD_MODIFY(CONTROL0_TSEN_START, &ctrl0, 0);
|
||||
regmap_write(priv->syscon, data->syscon_control0_off, ctrl0);
|
||||
|
||||
/* Reset the mode, internal sensor will be automatically selected */
|
||||
ctrl0 &= ~(CONTROL0_TSEN_MODE_MASK << CONTROL0_TSEN_MODE_SHIFT);
|
||||
|
||||
/* Other channels are external and should be selected accordingly */
|
||||
if (channel) {
|
||||
/* Change the mode to external */
|
||||
ctrl0 |= CONTROL0_TSEN_MODE_EXTERNAL <<
|
||||
CONTROL0_TSEN_MODE_SHIFT;
|
||||
FIELD_MODIFY(CONTROL0_TSEN_MODE_MASK, &ctrl0, CONTROL0_TSEN_MODE_EXTERNAL);
|
||||
/* Select the sensor */
|
||||
ctrl0 &= ~(CONTROL0_TSEN_CHAN_MASK << CONTROL0_TSEN_CHAN_SHIFT);
|
||||
ctrl0 |= (channel - 1) << CONTROL0_TSEN_CHAN_SHIFT;
|
||||
FIELD_MODIFY(CONTROL0_TSEN_CHAN_MASK, &ctrl0, channel - 1);
|
||||
} else {
|
||||
/* Reset the mode, internal sensor will be automatically selected */
|
||||
FIELD_MODIFY(CONTROL0_TSEN_MODE_MASK, &ctrl0, 0);
|
||||
}
|
||||
|
||||
/* Actually set the mode/channel */
|
||||
@@ -352,7 +340,7 @@ static int armada_select_channel(struct armada_thermal_priv *priv, int channel)
|
||||
priv->current_channel = channel;
|
||||
|
||||
/* Re-start the measurements */
|
||||
ctrl0 |= CONTROL0_TSEN_START;
|
||||
FIELD_MODIFY(CONTROL0_TSEN_START, &ctrl0, 1);
|
||||
regmap_write(priv->syscon, data->syscon_control0_off, ctrl0);
|
||||
|
||||
/*
|
||||
@@ -722,7 +710,6 @@ static const struct regmap_config armada_thermal_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static int armada_thermal_probe_legacy(struct platform_device *pdev,
|
||||
@@ -913,11 +900,8 @@ static int armada_thermal_probe(struct platform_device *pdev)
|
||||
armada_overheat_isr,
|
||||
armada_overheat_isr_thread,
|
||||
0, NULL, priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot request threaded IRQ %d\n",
|
||||
irq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -356,8 +356,7 @@ static int brcmstb_thermal_probe(struct platform_device *pdev)
|
||||
IRQF_ONESHOT,
|
||||
DRV_NAME, priv);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"could not request IRQ\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "registered AVS TMON of-sensor driver\n");
|
||||
|
||||
@@ -167,10 +167,8 @@ static int db8500_thermal_probe(struct platform_device *pdev)
|
||||
ret = devm_request_threaded_irq(dev, low_irq, NULL,
|
||||
prcmu_low_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
"dbx500_temp_low", th);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to allocate temp low irq\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
high_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_HIGH");
|
||||
if (high_irq < 0)
|
||||
@@ -179,10 +177,8 @@ static int db8500_thermal_probe(struct platform_device *pdev)
|
||||
ret = devm_request_threaded_irq(dev, high_irq, NULL,
|
||||
prcmu_high_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
"dbx500_temp_high", th);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to allocate temp high irq\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* register of thermal sensor and get info from DT */
|
||||
th->tz = devm_thermal_of_zone_register(dev, 0, th, &thdev_ops);
|
||||
|
||||
@@ -578,10 +578,8 @@ static int hisi_thermal_probe(struct platform_device *pdev)
|
||||
hisi_thermal_alarm_irq_thread,
|
||||
IRQF_ONESHOT, sensor->irq_name,
|
||||
sensor);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to request alarm irq: %d\n", ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = data->ops->enable_sensor(sensor);
|
||||
if (ret) {
|
||||
|
||||
@@ -331,7 +331,7 @@ static int imx91_tmu_probe(struct platform_device *pdev)
|
||||
IRQF_ONESHOT, "imx91_thermal", tmu);
|
||||
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "failed to request alarm irq\n");
|
||||
return ret;
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
|
||||
@@ -732,10 +732,8 @@ static int imx_thermal_probe(struct platform_device *pdev)
|
||||
ret = devm_request_threaded_irq(dev, data->irq,
|
||||
imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread,
|
||||
0, "imx_thermal", data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to request alarm irq: %d\n", ret);
|
||||
if (ret < 0)
|
||||
goto thermal_zone_unregister;
|
||||
}
|
||||
|
||||
pm_runtime_put(data->dev);
|
||||
|
||||
@@ -832,12 +830,12 @@ static int imx_thermal_runtime_resume(struct device *dev)
|
||||
ret = regmap_write(map, socdata->sensor_ctrl + REG_CLR,
|
||||
socdata->power_down_mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto disable_clk;
|
||||
|
||||
ret = regmap_write(map, socdata->sensor_ctrl + REG_SET,
|
||||
socdata->measure_temp_mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto disable_clk;
|
||||
|
||||
/*
|
||||
* According to the temp sensor designers, it may require up to ~17us
|
||||
@@ -846,6 +844,11 @@ static int imx_thermal_runtime_resume(struct device *dev)
|
||||
usleep_range(20, 50);
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
clk_disable_unprepare(data->thermal_clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops imx_thermal_pm_ops = {
|
||||
|
||||
@@ -173,7 +173,7 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL, loongson2_thermal_irq_thread,
|
||||
IRQF_ONESHOT, "loongson2_thermal", tzd);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "failed to request alarm irq\n");
|
||||
return ret;
|
||||
|
||||
devm_thermal_add_hwmon_sysfs(dev, tzd);
|
||||
|
||||
|
||||
@@ -121,19 +121,15 @@ static int max77620_thermal_probe(struct platform_device *pdev)
|
||||
max77620_thermal_irq,
|
||||
IRQF_ONESHOT | IRQF_SHARED,
|
||||
dev_name(&pdev->dev), mtherm);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to request irq1: %d\n", ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, mtherm->irq_tjalarm2, NULL,
|
||||
max77620_thermal_irq,
|
||||
IRQF_ONESHOT | IRQF_SHARED,
|
||||
dev_name(&pdev->dev), mtherm);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to request irq2: %d\n", ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1470,7 +1470,20 @@ static int lvts_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(lvts_td->base))
|
||||
return dev_err_probe(dev, PTR_ERR(lvts_td->base), "Failed to map io resource\n");
|
||||
|
||||
lvts_td->reset = devm_reset_control_get_by_index(dev, 0);
|
||||
/*
|
||||
* Depending on the SoC+Firmware combination, the LVTS hardware may be
|
||||
* may be actively used by one or even multiple concurrent MCUs!
|
||||
* In this case, resetting it may produce either a severe slowdown of
|
||||
* the entire system, or even a thermal protection AP reset, as some
|
||||
* MCU(s) may be reading a very high or very low temperature while the
|
||||
* LVTS is being reset.
|
||||
*
|
||||
* On those, don't fail if no reset is found as that may be omitted on
|
||||
* purpose, but still check if there's one, because some board(s) may
|
||||
* be running on a different bootchain with reduced firmwares or using
|
||||
* firmwares with reduced functionality.
|
||||
*/
|
||||
lvts_td->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
|
||||
if (IS_ERR(lvts_td->reset))
|
||||
return dev_err_probe(dev, PTR_ERR(lvts_td->reset), "Failed to get reset control\n");
|
||||
|
||||
@@ -1491,7 +1504,7 @@ static int lvts_probe(struct platform_device *pdev)
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL, lvts_irq_handler,
|
||||
IRQF_ONESHOT, dev_name(dev), lvts_td);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request interrupt\n");
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, lvts_td);
|
||||
|
||||
|
||||
@@ -21,6 +21,29 @@ config QCOM_SPMI_ADC_TM5
|
||||
Thermal client sets threshold temperature for both warm and cool and
|
||||
gets updated when a threshold is reached.
|
||||
|
||||
config QCOM_SPMI_MBG_TM
|
||||
tristate "Qualcomm SPMI PMIC MBG Temperature monitor"
|
||||
depends on QCOM_SPMI_ADC5_GEN3
|
||||
select REGMAP_SPMI
|
||||
help
|
||||
This enables Qualcomm PMIC MBG (Master Bandgap) thermal monitor.
|
||||
|
||||
The MBG block monitors PMIC die temperature in hardware and raises an
|
||||
interrupt when the programmed threshold is crossed.
|
||||
|
||||
Temperature is read from the associated ADC channel, and threshold
|
||||
interrupts are forwarded to the thermal framework as trip-violation
|
||||
events. Current support handles a single LVL1 upper (hot) trip.
|
||||
|
||||
config QCOM_SPMI_ADC_TM5_GEN3
|
||||
tristate "Qualcomm SPMI PMIC Thermal Monitor ADC5 Gen3"
|
||||
depends on QCOM_SPMI_ADC5_GEN3
|
||||
help
|
||||
This enables the auxiliary thermal driver for the ADC5 Gen3 thermal
|
||||
monitoring device. It shows up as a thermal zone with multiple trip points.
|
||||
Thermal client sets threshold temperature for both warm and cool and
|
||||
gets updated when a threshold is reached.
|
||||
|
||||
config QCOM_SPMI_TEMP_ALARM
|
||||
tristate "Qualcomm SPMI PMIC Temperature Alarm"
|
||||
depends on OF && SPMI && IIO
|
||||
|
||||
@@ -4,5 +4,7 @@ obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
|
||||
qcom_tsens-y += tsens.o tsens-v2.o tsens-v1.o tsens-v0_1.o \
|
||||
tsens-8960.o
|
||||
obj-$(CONFIG_QCOM_SPMI_ADC_TM5) += qcom-spmi-adc-tm5.o
|
||||
obj-$(CONFIG_QCOM_SPMI_ADC_TM5_GEN3) += qcom-spmi-adc-tm5-gen3.o
|
||||
obj-$(CONFIG_QCOM_SPMI_MBG_TM) += qcom-spmi-mbg-tm.o
|
||||
obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o
|
||||
obj-$(CONFIG_QCOM_LMH) += lmh.o
|
||||
|
||||
@@ -223,7 +223,6 @@ static int lmh_probe(struct platform_device *pdev)
|
||||
IRQF_NO_THREAD | IRQF_NO_SUSPEND,
|
||||
"lmh-irq", lmh_data);
|
||||
if (ret) {
|
||||
dev_err(dev, "Error %d registering irq %x\n", ret, lmh_data->irq);
|
||||
irq_domain_remove(lmh_data->domain);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -369,9 +369,6 @@ static int adc_tm5_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret != IIO_VAL_INT)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
256
drivers/thermal/qcom/qcom-spmi-mbg-tm.c
Normal file
256
drivers/thermal/qcom/qcom-spmi-mbg-tm.c
Normal file
@@ -0,0 +1,256 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
|
||||
#define MBG_TEMP_MON2_FAULT_STATUS 0x50
|
||||
|
||||
#define MON_FAULT_STATUS_MASK GENMASK(7, 4)
|
||||
#define MON_FAULT_LVL1_UPR 0x5
|
||||
|
||||
#define MON2_LVL1_UP_THRESH 0x59
|
||||
|
||||
#define MBG_TEMP_MON2_MISC_CFG 0x5f
|
||||
#define MON2_UP_THRESH_EN BIT(1)
|
||||
|
||||
#define MBG_TEMP_STEP_MV 8
|
||||
#define MBG_TEMP_DEFAULT_TEMP_MV 600
|
||||
#define MBG_TEMP_CONSTANT 1000
|
||||
#define MBG_MIN_TRIP_TEMP 25000
|
||||
#define MBG_MAX_SUPPORTED_TEMP 160000
|
||||
|
||||
/**
|
||||
* struct mbg_tm_chip - MBG thermal monitor device data.
|
||||
* @map: regmap for accessing MBG thermal registers.
|
||||
* @dev: mbg_tm_chip device.
|
||||
* @tz_dev: thermal zone device registered with the thermal framework.
|
||||
* @lock: mbg_tm_chip lock for set trip temperature.
|
||||
* @base: base register offset for this MBG instance
|
||||
* @irq: interrupt line used to signal threshold events
|
||||
* @last_temp: last measured temperature.
|
||||
* @last_thres_crossed: indicates whether the last interrupt crossed a threshold
|
||||
* @adc: IIO ADC channel used for temperature sensing
|
||||
*/
|
||||
struct mbg_tm_chip {
|
||||
struct regmap *map;
|
||||
struct device *dev;
|
||||
struct thermal_zone_device *tz_dev;
|
||||
struct mutex lock;
|
||||
unsigned int base;
|
||||
int irq;
|
||||
int last_temp;
|
||||
bool last_thres_crossed;
|
||||
struct iio_channel *adc;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mbg_map_table - temperature to voltage mapping entry
|
||||
* @min_temp: minimum temperature supported by this mapping entry
|
||||
* @vtemp0: reference voltage or ADC code corresponding to the temperature
|
||||
* @tc: temperature coefficient used for conversion calculations
|
||||
*/
|
||||
struct mbg_map_table {
|
||||
int min_temp;
|
||||
int vtemp0;
|
||||
int tc;
|
||||
};
|
||||
|
||||
static const struct mbg_map_table map_table[] = {
|
||||
{ -60000, 4337, 1967 },
|
||||
{ -40000, 4731, 1964 },
|
||||
{ -20000, 5124, 1957 },
|
||||
{ 0, 5515, 1949 },
|
||||
{ 20000, 5905, 1940 },
|
||||
{ 40000, 6293, 1930 },
|
||||
{ 60000, 6679, 1921 },
|
||||
{ 80000, 7064, 1910 },
|
||||
{ 100000, 7446, 1896 },
|
||||
{ 120000, 7825, 1878 },
|
||||
{ 140000, 8201, 1859 },
|
||||
};
|
||||
|
||||
static int mbg_tm_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
{
|
||||
struct mbg_tm_chip *chip = thermal_zone_device_priv(tz);
|
||||
int ret, milli_celsius;
|
||||
|
||||
scoped_guard(mutex, &chip->lock) {
|
||||
if (chip->last_thres_crossed) {
|
||||
dev_dbg(chip->dev, "last_temp: %d\n", chip->last_temp);
|
||||
chip->last_thres_crossed = false;
|
||||
*temp = chip->last_temp;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ret = iio_read_channel_processed(chip->adc, &milli_celsius);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to read iio channel with %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*temp = milli_celsius;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int temp_to_vtemp_mv(int temp)
|
||||
{
|
||||
int idx, vtemp, tc = 0, t0 = 0, vtemp0 = 0;
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(map_table); idx++)
|
||||
if (temp >= map_table[idx].min_temp &&
|
||||
temp < (map_table[idx].min_temp + 20000)) {
|
||||
tc = map_table[idx].tc;
|
||||
t0 = map_table[idx].min_temp;
|
||||
vtemp0 = map_table[idx].vtemp0;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Formula to calculate vtemp(mV) from a given temp
|
||||
* vtemp = (temp - minT) * tc + vtemp0
|
||||
* tc, t0 and vtemp0 values are mentioned in the map_table array.
|
||||
*/
|
||||
vtemp = ((temp - t0) * tc + vtemp0 * 100000) / 1000000;
|
||||
|
||||
/* step size is 8mV */
|
||||
return abs(vtemp - MBG_TEMP_DEFAULT_TEMP_MV) / MBG_TEMP_STEP_MV;
|
||||
}
|
||||
|
||||
static int mbg_tm_set_trip_temp(struct thermal_zone_device *tz, int low_temp,
|
||||
int temp)
|
||||
{
|
||||
struct mbg_tm_chip *chip = thermal_zone_device_priv(tz);
|
||||
int ret = 0;
|
||||
|
||||
guard(mutex)(&chip->lock);
|
||||
|
||||
/* The HW has a limitation that the trip set must be above 25C */
|
||||
if (temp > MBG_MIN_TRIP_TEMP && temp < MBG_MAX_SUPPORTED_TEMP) {
|
||||
ret = regmap_write(chip->map, chip->base + MON2_LVL1_UP_THRESH,
|
||||
temp_to_vtemp_mv(temp));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_set_bits(chip->map, chip->base + MBG_TEMP_MON2_MISC_CFG,
|
||||
MON2_UP_THRESH_EN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
dev_err(chip->dev, "Set trip b/w 25C and 160C\n");
|
||||
ret = regmap_clear_bits(chip->map, chip->base + MBG_TEMP_MON2_MISC_CFG,
|
||||
MON2_UP_THRESH_EN);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the last_temp one degree higher, to ensure the
|
||||
* violated temp is returned to thermal framework when it reads
|
||||
* temperature for the first time after the violation happens.
|
||||
* This is needed to account for the inaccuracy in the conversion
|
||||
* formula used which leads to the thermal framework setting back
|
||||
* the same thresholds in case the temperature it reads does not
|
||||
* show violation.
|
||||
*/
|
||||
chip->last_temp = temp + MBG_TEMP_CONSTANT;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_device_ops mbg_tm_ops = {
|
||||
.get_temp = mbg_tm_get_temp,
|
||||
.set_trips = mbg_tm_set_trip_temp,
|
||||
};
|
||||
|
||||
static irqreturn_t mbg_tm_isr(int irq, void *data)
|
||||
{
|
||||
struct mbg_tm_chip *chip = data;
|
||||
int ret, val;
|
||||
|
||||
scoped_guard(mutex, &chip->lock) {
|
||||
ret = regmap_read(chip->map, chip->base + MBG_TEMP_MON2_FAULT_STATUS, &val);
|
||||
if (ret < 0)
|
||||
return IRQ_HANDLED;
|
||||
if (FIELD_GET(MON_FAULT_STATUS_MASK, val) == MON_FAULT_LVL1_UPR)
|
||||
chip->last_thres_crossed = true;
|
||||
}
|
||||
|
||||
if (FIELD_GET(MON_FAULT_STATUS_MASK, val) == MON_FAULT_LVL1_UPR) {
|
||||
dev_dbg(chip->dev, "Notifying Thermal, fault status=%d\n", val);
|
||||
thermal_zone_device_update(chip->tz_dev, THERMAL_TRIP_VIOLATED);
|
||||
} else {
|
||||
dev_dbg(chip->dev, "Lvl1 upper threshold not violated, ignoring interrupt\n");
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mbg_tm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mbg_tm_chip *chip;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
u32 res;
|
||||
int ret;
|
||||
|
||||
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->dev = &pdev->dev;
|
||||
|
||||
mutex_init(&chip->lock);
|
||||
|
||||
chip->map = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!chip->map)
|
||||
return -ENXIO;
|
||||
|
||||
ret = device_property_read_u32(chip->dev, "reg", &res);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(chip->dev, ret, "Couldn't read reg property\n");
|
||||
|
||||
chip->base = res;
|
||||
|
||||
chip->irq = platform_get_irq(pdev, 0);
|
||||
if (chip->irq < 0)
|
||||
return dev_err_probe(chip->dev, chip->irq, "Failed to get irq\n");
|
||||
|
||||
chip->adc = devm_iio_channel_get(&pdev->dev, "thermal");
|
||||
if (IS_ERR(chip->adc))
|
||||
return dev_err_probe(chip->dev, PTR_ERR(chip->adc), "Failed to get adc channel\n");
|
||||
|
||||
chip->tz_dev = devm_thermal_of_zone_register(chip->dev, 0, chip, &mbg_tm_ops);
|
||||
if (IS_ERR(chip->tz_dev))
|
||||
return dev_err_probe(chip->dev, PTR_ERR(chip->tz_dev),
|
||||
"Failed to register sensor\n");
|
||||
|
||||
return devm_request_threaded_irq(&pdev->dev, chip->irq, NULL, mbg_tm_isr, IRQF_ONESHOT,
|
||||
node->name, chip);
|
||||
}
|
||||
|
||||
static const struct of_device_id mbg_tm_match_table[] = {
|
||||
{ .compatible = "qcom,pm8775-mbg-tm" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mbg_tm_match_table);
|
||||
|
||||
static struct platform_driver mbg_tm_driver = {
|
||||
.driver = {
|
||||
.name = "qcom-spmi-mbg-tm",
|
||||
.of_match_table = mbg_tm_match_table,
|
||||
},
|
||||
.probe = mbg_tm_probe,
|
||||
};
|
||||
module_platform_driver(mbg_tm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("PMIC MBG Temperature monitor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -1258,10 +1258,7 @@ static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
|
||||
dev_name(&pdev->dev),
|
||||
priv);
|
||||
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "%s: failed to get irq\n",
|
||||
__func__);
|
||||
else
|
||||
if (!ret)
|
||||
*irq_num = irq;
|
||||
}
|
||||
|
||||
|
||||
@@ -415,11 +415,20 @@ static int qoriq_tmu_resume(struct device *dev)
|
||||
if (data->ver > TMU_VER1) {
|
||||
ret = regmap_clear_bits(data->regmap, REGS_TMR, TMR_CMD);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
/* Enable monitoring */
|
||||
return regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, TMR_ME);
|
||||
ret = regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, TMR_ME);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
clk_disable_unprepare(data->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
|
||||
|
||||
@@ -446,10 +446,8 @@ static int rcar_thermal_probe(struct platform_device *pdev)
|
||||
|
||||
ret = devm_request_irq(dev, irq, rcar_thermal_irq,
|
||||
IRQF_SHARED, dev_name(dev), common);
|
||||
if (ret) {
|
||||
dev_err(dev, "irq request failed\n");
|
||||
if (ret)
|
||||
goto error_unregister;
|
||||
}
|
||||
|
||||
/* update ENR bits */
|
||||
if (chip->irq_per_ch)
|
||||
@@ -492,12 +490,6 @@ static int rcar_thermal_probe(struct platform_device *pdev)
|
||||
"rcar_thermal", trips, ARRAY_SIZE(trips), priv,
|
||||
&rcar_thermal_zone_ops, NULL, 0,
|
||||
idle);
|
||||
|
||||
ret = thermal_zone_device_enable(priv->zone);
|
||||
if (ret) {
|
||||
thermal_zone_device_unregister(priv->zone);
|
||||
priv->zone = ERR_PTR(ret);
|
||||
}
|
||||
}
|
||||
if (IS_ERR(priv->zone)) {
|
||||
dev_err(dev, "can't register thermal zone\n");
|
||||
@@ -506,11 +498,12 @@ static int rcar_thermal_probe(struct platform_device *pdev)
|
||||
goto error_unregister;
|
||||
}
|
||||
|
||||
if (chip->use_of_thermal) {
|
||||
if (chip->use_of_thermal)
|
||||
ret = thermal_add_hwmon_sysfs(priv->zone);
|
||||
if (ret)
|
||||
goto error_unregister;
|
||||
}
|
||||
else
|
||||
ret = thermal_zone_device_enable(priv->zone);
|
||||
if (ret)
|
||||
goto error_unregister;
|
||||
|
||||
rcar_thermal_irq_enable(priv);
|
||||
|
||||
|
||||
@@ -461,10 +461,8 @@ static int rzg3e_thermal_probe(struct platform_device *pdev)
|
||||
ret = devm_request_threaded_irq(dev, irq, rzg3e_thermal_irq,
|
||||
rzg3e_thermal_irq_thread,
|
||||
IRQF_ONESHOT, "rzg3e_thermal", priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to request IRQ: %d\n", ret);
|
||||
if (ret)
|
||||
goto err_pm_put;
|
||||
}
|
||||
|
||||
/* Add hwmon sysfs interface */
|
||||
ret = devm_thermal_add_hwmon_sysfs(dev, priv->zone);
|
||||
|
||||
@@ -1773,8 +1773,7 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
|
||||
IRQF_ONESHOT,
|
||||
"rockchip_thermal", thermal);
|
||||
if (error)
|
||||
return dev_err_probe(&pdev->dev, error,
|
||||
"failed to request tsadc irq.\n");
|
||||
return error;
|
||||
|
||||
thermal->chip->control(thermal->regs, true);
|
||||
|
||||
|
||||
@@ -1102,10 +1102,8 @@ static int exynos_tmu_probe(struct platform_device *pdev)
|
||||
IRQF_TRIGGER_RISING
|
||||
| IRQF_SHARED | IRQF_ONESHOT,
|
||||
dev_name(dev), data);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to request irq: %d\n", data->irq);
|
||||
if (ret)
|
||||
goto err_sclk;
|
||||
}
|
||||
|
||||
exynos_tmu_control(pdev, true);
|
||||
return 0;
|
||||
|
||||
@@ -156,13 +156,12 @@ static int k1_tsensor_set_trips(struct thermal_zone_device *tz, int low, int hig
|
||||
struct k1_tsensor *ts = ch->ts;
|
||||
u32 val;
|
||||
|
||||
if (low >= high)
|
||||
return -EINVAL;
|
||||
|
||||
low = clamp_val(low / 1000 + TEMPERATURE_OFFSET, TEMPERATURE_OFFSET,
|
||||
FIELD_MAX(K1_TSENSOR_THRSH_LOW_MASK));
|
||||
high = clamp_val(high / 1000 + TEMPERATURE_OFFSET, TEMPERATURE_OFFSET,
|
||||
FIELD_MAX(K1_TSENSOR_THRSH_HIGH_MASK));
|
||||
if (low >= high)
|
||||
return -EINVAL;
|
||||
|
||||
val = readl(ts->base + K1_TSENSOR_THRSH_REG(ch->id));
|
||||
|
||||
@@ -199,6 +198,39 @@ static irqreturn_t k1_tsensor_irq_thread(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void k1_tsensor_shutdown(struct k1_tsensor *ts)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Disable all interrupts */
|
||||
writel(0xffffffff, ts->base + K1_TSENSOR_INT_EN_REG);
|
||||
|
||||
/* Disable all sensors */
|
||||
val = readl(ts->base + K1_TSENSOR_EN_REG);
|
||||
val &= ~K1_TSENSOR_EN_ALL;
|
||||
writel(val, ts->base + K1_TSENSOR_EN_REG);
|
||||
|
||||
/* Clear the sampling configuration set by k1_tsensor_init() */
|
||||
val = readl(ts->base + K1_TSENSOR_TIME_REG);
|
||||
val &= ~(K1_TSENSOR_TIME_FILTER_PERIOD |
|
||||
K1_TSENSOR_TIME_ADC_CNT_RST |
|
||||
K1_TSENSOR_TIME_WAIT_REF_CNT);
|
||||
writel(val, ts->base + K1_TSENSOR_TIME_REG);
|
||||
|
||||
/* Clear the control bits configured by k1_tsensor_init() */
|
||||
val = readl(ts->base + K1_TSENSOR_PCTRL_REG);
|
||||
val &= ~(K1_TSENSOR_PCTRL_RAW_SEL |
|
||||
K1_TSENSOR_PCTRL_TEMP_MODE |
|
||||
K1_TSENSOR_PCTRL_HW_AUTO_MODE |
|
||||
K1_TSENSOR_PCTRL_ENABLE);
|
||||
writel(val, ts->base + K1_TSENSOR_PCTRL_REG);
|
||||
}
|
||||
|
||||
static void k1_tsensor_shutdown_action(void *data)
|
||||
{
|
||||
k1_tsensor_shutdown(data);
|
||||
}
|
||||
|
||||
static int k1_tsensor_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@@ -229,34 +261,48 @@ static int k1_tsensor_probe(struct platform_device *pdev)
|
||||
|
||||
k1_tsensor_init(ts);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
k1_tsensor_irq_thread,
|
||||
IRQF_ONESHOT, "k1_tsensor", ts);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < MAX_SENSOR_NUMBER; ++i) {
|
||||
ts->ch[i].id = i;
|
||||
ts->ch[i].ts = ts;
|
||||
ts->ch[i].tzd = devm_thermal_of_zone_register(dev, i, ts->ch + i, &k1_tsensor_ops);
|
||||
if (IS_ERR(ts->ch[i].tzd))
|
||||
return PTR_ERR(ts->ch[i].tzd);
|
||||
if (IS_ERR(ts->ch[i].tzd)) {
|
||||
ret = PTR_ERR(ts->ch[i].tzd);
|
||||
goto err_shutdown;
|
||||
}
|
||||
|
||||
/* Attach sysfs hwmon attributes for userspace monitoring */
|
||||
ret = devm_thermal_add_hwmon_sysfs(dev, ts->ch[i].tzd);
|
||||
if (ret)
|
||||
dev_warn(dev, "Failed to add hwmon sysfs attributes\n");
|
||||
|
||||
k1_tsensor_enable_irq(ts->ch + i);
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto err_shutdown;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
k1_tsensor_irq_thread,
|
||||
IRQF_ONESHOT, "k1_tsensor", ts);
|
||||
if (ret < 0)
|
||||
goto err_shutdown;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, k1_tsensor_shutdown_action, ts);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable interrupts only after all zones and the handler are ready */
|
||||
for (i = 0; i < MAX_SENSOR_NUMBER; ++i)
|
||||
k1_tsensor_enable_irq(ts->ch + i);
|
||||
|
||||
platform_set_drvdata(pdev, ts);
|
||||
|
||||
return 0;
|
||||
|
||||
err_shutdown:
|
||||
k1_tsensor_shutdown(ts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id k1_tsensor_dt_ids[] = {
|
||||
|
||||
@@ -101,10 +101,8 @@ static int st_mmap_register_enable_irq(struct st_thermal_sensor *sensor)
|
||||
NULL, st_mmap_thermal_trip_handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
dev->driver->name, sensor);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register IRQ %d\n", sensor->irq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return st_mmap_enable_irq(sensor);
|
||||
}
|
||||
|
||||
@@ -390,11 +390,8 @@ static int stm_register_irq(struct stm_thermal_sensor *sensor)
|
||||
stm_thermal_irq_handler,
|
||||
IRQF_ONESHOT,
|
||||
dev->driver->name, sensor);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: Failed to register IRQ %d\n", __func__,
|
||||
sensor->irq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s: thermal IRQ registered", __func__);
|
||||
|
||||
|
||||
@@ -149,7 +149,6 @@ static const struct regmap_config config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.fast_io = true,
|
||||
.max_register = 0xfc,
|
||||
};
|
||||
|
||||
|
||||
@@ -2007,10 +2007,8 @@ static int soctherm_interrupts_init(struct platform_device *pdev,
|
||||
IRQF_ONESHOT,
|
||||
dev_name(&pdev->dev),
|
||||
tegra);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "request_irq 'thermal_irq' failed.\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev,
|
||||
tegra->edp_irq,
|
||||
@@ -2019,10 +2017,8 @@ static int soctherm_interrupts_init(struct platform_device *pdev,
|
||||
IRQF_ONESHOT,
|
||||
"soctherm_edp",
|
||||
tegra);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "request_irq 'edp_irq' failed.\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -602,8 +602,7 @@ static int tegra_tsensor_probe(struct platform_device *pdev)
|
||||
tegra_tsensor_isr, IRQF_ONESHOT,
|
||||
"tegra_tsensor", ts);
|
||||
if (err)
|
||||
return dev_err_probe(&pdev->dev, err,
|
||||
"failed to request interrupt\n");
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
12
drivers/thermal/thermal-fw.c
Normal file
12
drivers/thermal/thermal-fw.c
Normal file
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* of-thermal-testing.c - Generic Thermal Management device tree testing support
|
||||
*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/fwnode.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
|
||||
|
||||
97
drivers/thermal/thermal_fw.c
Normal file
97
drivers/thermal/thermal_fw.c
Normal file
@@ -0,0 +1,97 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* thermal-fw.c - Thermal components creation from firmware description
|
||||
*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
#include <linux/fwnode.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
/**
|
||||
* thermal_fwnode_cooling_device_register() - register an thermal cooling device
|
||||
* @np: a pointer to a device tree node.
|
||||
* @of_index: a cooling device index in the cooling controller
|
||||
* @type: the thermal cooling device type.
|
||||
* @devdata: device private data.
|
||||
* @ops: standard thermal cooling devices callbacks.
|
||||
*
|
||||
* This function will register a cooling device with device tree node reference.
|
||||
* This interface function adds a new thermal cooling device (fan/processor/...)
|
||||
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
|
||||
* to all the thermal zone devices registered at the same time.
|
||||
*
|
||||
* Return: a pointer to the created struct thermal_cooling_device or an
|
||||
* ERR_PTR. Caller must check return value with IS_ERR*() helpers.
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
thermal_fwnode_cooling_device_register(struct fwnode_handle *fwnode, int fwn_index,
|
||||
const char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
{
|
||||
struct thermal_cooling_device *cdev;
|
||||
|
||||
cdev = __thermal_cooling_device_register(type, devdata, ops);
|
||||
if (IS_ERR(cdev))
|
||||
return cdev;
|
||||
|
||||
cdev->np = (struct device_node *)fwnode;
|
||||
cdev->of_index = fwn_index;
|
||||
thermal_cooling_device_init_complete(cdev);
|
||||
|
||||
return cdev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_fwnode_cooling_device_register);
|
||||
|
||||
static struct thermal_cooling_device *
|
||||
__devm_thermal_fwnode_cooling_device_register(struct device *dev, struct fwnode_handle *fwnode,
|
||||
int fwn_index, const char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
{
|
||||
struct thermal_cooling_device **ptr, *tcd;
|
||||
|
||||
ptr = devres_alloc(thermal_cooling_device_release, sizeof(*ptr),
|
||||
GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
tcd = thermal_fwnode_cooling_device_register(fwnode, fwn_index, type, devdata, ops);
|
||||
if (IS_ERR(tcd)) {
|
||||
devres_free(ptr);
|
||||
return tcd;
|
||||
}
|
||||
|
||||
*ptr = tcd;
|
||||
devres_add(dev, ptr);
|
||||
|
||||
return tcd;
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_thermal_fwnode_cooling_device_register() - register a thermal cooling device
|
||||
* @dev: a valid struct device pointer of a sensor device.
|
||||
* @fw_index: a cooling device index in the cooling controller
|
||||
* @type: the thermal cooling device type.
|
||||
* @devdata: device private data.
|
||||
* @ops: standard thermal cooling devices callbacks.
|
||||
*
|
||||
* This function will register a cooling device with a firmware node reference.
|
||||
* This interface function adds a new thermal cooling device (fan/processor/...)
|
||||
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
|
||||
* to all the thermal zone devices registered at the same time.
|
||||
*
|
||||
* Return: a pointer to the created struct thermal_cooling_device or an
|
||||
* ERR_PTR. Caller must check return value with IS_ERR*() helpers.
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
devm_thermal_fwnode_cooling_device_register(struct device *dev, int fwn_index,
|
||||
const char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
{
|
||||
return __devm_thermal_fwnode_cooling_device_register(dev, dev_fwnode(dev), fwn_index,
|
||||
type, devdata, ops);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_thermal_fwnode_cooling_device_register);
|
||||
|
||||
|
||||
@@ -444,7 +444,7 @@ static struct thermal_zone_device *thermal_of_zone_register(struct device_node *
|
||||
|
||||
ret = thermal_zone_device_enable(tz);
|
||||
if (ret) {
|
||||
pr_err("Failed to enabled thermal zone '%s', id=%d: %d\n",
|
||||
pr_err("Failed to enable thermal zone '%s', id=%d: %d\n",
|
||||
tz->type, tz->id, ret);
|
||||
thermal_of_zone_unregister(tz);
|
||||
return ERR_PTR(ret);
|
||||
|
||||
@@ -205,7 +205,5 @@ int adc5_gen3_get_scaled_reading(struct device *dev,
|
||||
int adc5_gen3_therm_code_to_temp(struct device *dev,
|
||||
struct adc5_channel_common_prop *common_props,
|
||||
u16 code, int *val);
|
||||
void adc5_gen3_register_tm_event_notifier(struct device *dev,
|
||||
void (*handler)(struct auxiliary_device *));
|
||||
|
||||
#endif /* QCOM_ADC5_GEN3_COMMON_H */
|
||||
|
||||
@@ -175,8 +175,8 @@ LIBTHERMAL_API thermal_error_t thermal_sampling_handle(struct thermal_handler *t
|
||||
|
||||
LIBTHERMAL_API int thermal_sampling_fd(struct thermal_handler *th);
|
||||
|
||||
#endif /* __LIBTHERMAL_H */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __LIBTHERMAL_H */
|
||||
|
||||
Reference in New Issue
Block a user