mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-27 08:13:37 -04:00
Merge tag 'thermal-7.3-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull more thermal control updates from Rafael Wysocki:
"This mostly consists of assorted updates of thermal drivers, including
new hardware support (Airoha AN7583, Qualcomm Master BandGap thermal
monitor, QCom PMIC5 Gen3 ADC), but it also includes two reverts of
recent cosmetic thermal core updates that went against driver core
plans to eliminate class_create():
- Fix missing bitfield include headers in Armada and QCom SPM BMG
drivers (Daniel Lezcano)
- Fix missed file when manually applying a change after a conflict
resolution for the QCom SPMI ADC TM5 Gen3 (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 to support 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 a thermal OF code comment (Marek Vasut)
- Remove unnecessary 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 auxiliary 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 on failing resume operation 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)
- Add IIO_CONSUMER namespace import to the qcom-spmi-mbg-tm thermal
driver to avoid modpost warnings that would appear after merging
the iio tree against the thermal updates (Nathan Chancellor)
- Revert two recent cosmetic updates of the thermal core conflicting
with driver core plans to eliminate class_create() (Rafael
Wysocki)"
* tag 'thermal-7.3-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (32 commits)
thermal/drivers/qcom-spmi-mbg-tm: Add module namespace import for IIO_CONSUMER
Revert "thermal/core: Allocate the thermal class dynamically"
Revert "thermal/core: Use the thermal class pointer as init guard"
thermal/drivers/armada: Fix missing bitfields include
thermal/drivers/qcom/spm mbg tm: Fix missing bitfield header
thermal/drivers/qcom: Fix missing spmi adc tm5 gen3 file
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
...
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);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*
|
||||
* Copyright (C) 2013 Marvell
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
@@ -20,41 +21,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 +135,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 +160,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 +180,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 +212,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 +229,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 +252,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 +304,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 +322,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 +341,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 +711,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 +901,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;
|
||||
}
|
||||
|
||||
434
drivers/thermal/qcom/qcom-spmi-adc-tm5-gen3.c
Normal file
434
drivers/thermal/qcom/qcom-spmi-adc-tm5-gen3.c
Normal file
@@ -0,0 +1,434 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/device/devres.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/adc/qcom-adc5-gen3-common.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/unaligned.h>
|
||||
|
||||
#include "../thermal_hwmon.h"
|
||||
|
||||
#define ADC_TM5_GEN3_CONFIG_REGS 12
|
||||
|
||||
struct device;
|
||||
struct adc_tm5_gen3_chip;
|
||||
|
||||
/**
|
||||
* struct adc_tm5_gen3_channel_props - ADC_TM channel structure
|
||||
* @common_props: structure with common ADC channel properties.
|
||||
* @chip: ADC TM device.
|
||||
* @tzd: pointer to thermal device corresponding to TM channel.
|
||||
* @sdam_index: SDAM on which this TM channel lies.
|
||||
* @timer: time period of recurring TM measurement.
|
||||
* @tm_chan_index: TM channel number used.
|
||||
* @high_thr_en: TM high threshold crossing detection enabled.
|
||||
* @low_thr_en: TM low threshold crossing detection enabled.
|
||||
*/
|
||||
struct adc_tm5_gen3_channel_props {
|
||||
struct adc5_channel_common_prop common_props;
|
||||
struct adc_tm5_gen3_chip *chip;
|
||||
struct thermal_zone_device *tzd;
|
||||
unsigned int sdam_index;
|
||||
unsigned int timer;
|
||||
unsigned int tm_chan_index;
|
||||
bool high_thr_en;
|
||||
bool low_thr_en;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adc_tm5_gen3_chip - ADC Thermal Monitoring device structure
|
||||
* @dev_data: Top-level ADC device data.
|
||||
* @chan_props: Array of ADC_TM channel structures.
|
||||
* @dev: SPMI ADC5 Gen3 device.
|
||||
* @nchannels: number of TM channels allocated
|
||||
*/
|
||||
struct adc_tm5_gen3_chip {
|
||||
struct adc5_device_data *dev_data;
|
||||
struct adc_tm5_gen3_channel_props *chan_props;
|
||||
struct device *dev;
|
||||
unsigned int nchannels;
|
||||
};
|
||||
|
||||
DEFINE_GUARD(adc5_gen3, struct adc_tm5_gen3_chip *,
|
||||
adc5_gen3_mutex_lock(_T->dev), adc5_gen3_mutex_unlock(_T->dev))
|
||||
|
||||
static int get_sdam_from_irq(struct adc_tm5_gen3_chip *adc_tm5, int irq)
|
||||
{
|
||||
for (int i = 0; i < adc_tm5->dev_data->num_sdams; i++) {
|
||||
if (adc_tm5->dev_data->base[i].irq == irq)
|
||||
return i;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static irqreturn_t adctm5_gen3_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct adc_tm5_gen3_chip *adc_tm5 = dev_id;
|
||||
int ret, sdam_num;
|
||||
u8 tm_status[2];
|
||||
u8 status, val;
|
||||
|
||||
sdam_num = get_sdam_from_irq(adc_tm5, irq);
|
||||
if (sdam_num < 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
ret = adc5_gen3_read(adc_tm5->dev_data, sdam_num, ADC5_GEN3_STATUS1,
|
||||
&status, sizeof(status));
|
||||
if (ret)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (status & ADC5_GEN3_STATUS1_CONV_FAULT) {
|
||||
val = ADC5_GEN3_CONV_ERR_CLR_REQ;
|
||||
adc5_gen3_status_clear(adc_tm5->dev_data, sdam_num,
|
||||
ADC5_GEN3_CONV_ERR_CLR, &val, 1);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
ret = adc5_gen3_read(adc_tm5->dev_data, sdam_num, ADC5_GEN3_TM_HIGH_STS,
|
||||
tm_status, sizeof(tm_status));
|
||||
if (ret)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (tm_status[0] || tm_status[1])
|
||||
return IRQ_WAKE_THREAD;
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static irqreturn_t adctm5_gen3_isr_thread(int irq, void *dev_id)
|
||||
{
|
||||
struct adc_tm5_gen3_chip *adc_tm5 = dev_id;
|
||||
u8 tm_status[2];
|
||||
int sdam_index;
|
||||
|
||||
sdam_index = get_sdam_from_irq(adc_tm5, irq);
|
||||
if (sdam_index < 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
scoped_guard(adc5_gen3, adc_tm5) {
|
||||
int ret;
|
||||
|
||||
ret = adc5_gen3_read(adc_tm5->dev_data, sdam_index, ADC5_GEN3_TM_HIGH_STS,
|
||||
tm_status, sizeof(tm_status));
|
||||
if (ret)
|
||||
return IRQ_NONE;
|
||||
|
||||
ret = adc5_gen3_status_clear(adc_tm5->dev_data, sdam_index,
|
||||
ADC5_GEN3_TM_HIGH_STS_CLR, tm_status,
|
||||
sizeof(tm_status));
|
||||
if (ret)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
for (int i = 0; i < adc_tm5->nchannels; i++) {
|
||||
struct adc_tm5_gen3_channel_props *chan_prop = &adc_tm5->chan_props[i];
|
||||
int offset = chan_prop->tm_chan_index;
|
||||
bool upper_set, lower_set;
|
||||
|
||||
if (chan_prop->sdam_index != sdam_index)
|
||||
continue;
|
||||
|
||||
upper_set = ((tm_status[0] & BIT(offset)) && chan_prop->high_thr_en);
|
||||
lower_set = ((tm_status[1] & BIT(offset)) && chan_prop->low_thr_en);
|
||||
|
||||
if (!(upper_set || lower_set))
|
||||
continue;
|
||||
|
||||
thermal_zone_device_update(chan_prop->tzd, THERMAL_TRIP_VIOLATED);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int adc_tm5_gen3_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
{
|
||||
struct adc_tm5_gen3_channel_props *prop = thermal_zone_device_priv(tz);
|
||||
struct adc_tm5_gen3_chip *adc_tm5;
|
||||
|
||||
if (!prop || !prop->chip)
|
||||
return -EINVAL;
|
||||
|
||||
adc_tm5 = prop->chip;
|
||||
|
||||
return adc5_gen3_get_scaled_reading(adc_tm5->dev, &prop->common_props, temp);
|
||||
}
|
||||
|
||||
static int adc_tm5_gen3_disable_channel(struct adc_tm5_gen3_channel_props *prop)
|
||||
{
|
||||
struct adc_tm5_gen3_chip *adc_tm5 = prop->chip;
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
prop->high_thr_en = false;
|
||||
prop->low_thr_en = false;
|
||||
|
||||
ret = adc5_gen3_poll_wait_hs(adc_tm5->dev_data, prop->sdam_index);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = BIT(prop->tm_chan_index);
|
||||
ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index,
|
||||
ADC5_GEN3_TM_HIGH_STS_CLR, &val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index,
|
||||
ADC5_GEN3_TM_LOW_STS_CLR, &val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = MEAS_INT_DISABLE;
|
||||
ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index,
|
||||
ADC5_GEN3_TIMER_SEL, &val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* To indicate there is an actual conversion request */
|
||||
val = ADC5_GEN3_CHAN_CONV_REQ | prop->tm_chan_index;
|
||||
ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index,
|
||||
ADC5_GEN3_PERPH_CH, &val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = ADC5_GEN3_CONV_REQ_REQ;
|
||||
return adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index,
|
||||
ADC5_GEN3_CONV_REQ, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static int adc_tm5_gen3_configure(struct adc_tm5_gen3_channel_props *prop,
|
||||
int low_temp, int high_temp)
|
||||
{
|
||||
struct adc_tm5_gen3_chip *adc_tm5 = prop->chip;
|
||||
u8 buf[ADC_TM5_GEN3_CONFIG_REGS];
|
||||
u8 conv_req;
|
||||
u16 adc_code;
|
||||
int ret;
|
||||
|
||||
ret = adc5_gen3_poll_wait_hs(adc_tm5->dev_data, prop->sdam_index);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adc5_gen3_read(adc_tm5->dev_data, prop->sdam_index,
|
||||
ADC5_GEN3_SID, buf, sizeof(buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Write SID */
|
||||
buf[0] = FIELD_PREP(ADC5_GEN3_SID_MASK, prop->common_props.sid);
|
||||
|
||||
/* Select TM channel and indicate there is an actual conversion request */
|
||||
buf[1] = ADC5_GEN3_CHAN_CONV_REQ | prop->tm_chan_index;
|
||||
|
||||
buf[2] = prop->timer;
|
||||
|
||||
/* Digital param selection */
|
||||
adc5_gen3_update_dig_param(&prop->common_props, &buf[3]);
|
||||
|
||||
/* Update fast average sample value */
|
||||
buf[4] = FIELD_PREP(ADC5_GEN3_FAST_AVG_CTL_SAMPLES_MASK,
|
||||
prop->common_props.avg_samples) | ADC5_GEN3_FAST_AVG_CTL_EN;
|
||||
|
||||
/* Select ADC channel */
|
||||
buf[5] = prop->common_props.channel;
|
||||
|
||||
/* Select HW settle delay for channel */
|
||||
buf[6] = FIELD_PREP(ADC5_GEN3_HW_SETTLE_DELAY_MASK,
|
||||
prop->common_props.hw_settle_time_us);
|
||||
|
||||
buf[7] = 0;
|
||||
|
||||
/* High temperature corresponds to low voltage threshold */
|
||||
prop->low_thr_en = (high_temp != INT_MAX);
|
||||
if (prop->low_thr_en) {
|
||||
adc_code = qcom_adc_tm5_gen2_temp_res_scale(high_temp);
|
||||
put_unaligned_le16(adc_code, &buf[8]);
|
||||
buf[7] |= ADC5_GEN3_LOW_THR_INT_EN;
|
||||
}
|
||||
|
||||
/* Low temperature corresponds to high voltage threshold */
|
||||
prop->high_thr_en = (low_temp != -INT_MAX);
|
||||
if (prop->high_thr_en) {
|
||||
adc_code = qcom_adc_tm5_gen2_temp_res_scale(low_temp);
|
||||
put_unaligned_le16(adc_code, &buf[10]);
|
||||
buf[7] |= ADC5_GEN3_HIGH_THR_INT_EN;
|
||||
}
|
||||
|
||||
ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, ADC5_GEN3_SID,
|
||||
buf, sizeof(buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
conv_req = ADC5_GEN3_CONV_REQ_REQ;
|
||||
return adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index,
|
||||
ADC5_GEN3_CONV_REQ, &conv_req, sizeof(conv_req));
|
||||
}
|
||||
|
||||
static int adc_tm5_gen3_set_trip_temp(struct thermal_zone_device *tz,
|
||||
int low_temp, int high_temp)
|
||||
{
|
||||
struct adc_tm5_gen3_channel_props *prop = thermal_zone_device_priv(tz);
|
||||
struct adc_tm5_gen3_chip *adc_tm5;
|
||||
|
||||
if (!prop || !prop->chip)
|
||||
return -EINVAL;
|
||||
|
||||
adc_tm5 = prop->chip;
|
||||
|
||||
dev_dbg(adc_tm5->dev, "channel:%s, low_temp(mdegC):%d, high_temp(mdegC):%d\n",
|
||||
prop->common_props.label, low_temp, high_temp);
|
||||
|
||||
guard(adc5_gen3)(adc_tm5);
|
||||
|
||||
return adc_tm5_gen3_configure(prop, low_temp, high_temp);
|
||||
}
|
||||
|
||||
static const struct thermal_zone_device_ops adc_tm_ops = {
|
||||
.get_temp = adc_tm5_gen3_get_temp,
|
||||
.set_trips = adc_tm5_gen3_set_trip_temp,
|
||||
};
|
||||
|
||||
static int adc_tm5_register_tzd(struct adc_tm5_gen3_chip *adc_tm5)
|
||||
{
|
||||
struct thermal_zone_device *tzd;
|
||||
unsigned int channel;
|
||||
int ret;
|
||||
|
||||
for (int i = 0; i < adc_tm5->nchannels; i++) {
|
||||
channel = ADC5_GEN3_V_CHAN(adc_tm5->chan_props[i].common_props);
|
||||
tzd = devm_thermal_of_zone_register(adc_tm5->dev, channel,
|
||||
&adc_tm5->chan_props[i],
|
||||
&adc_tm_ops);
|
||||
if (IS_ERR(tzd)) {
|
||||
if (PTR_ERR(tzd) == -ENODEV) {
|
||||
dev_dbg(adc_tm5->dev,
|
||||
"thermal sensor on channel %d is not used\n",
|
||||
channel);
|
||||
continue;
|
||||
}
|
||||
return PTR_ERR(tzd);
|
||||
}
|
||||
adc_tm5->chan_props[i].tzd = tzd;
|
||||
ret = devm_thermal_add_hwmon_sysfs(adc_tm5->dev, tzd);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adc5_gen3_disable(void *data)
|
||||
{
|
||||
struct adc_tm5_gen3_chip *adc_tm5 = data;
|
||||
|
||||
guard(adc5_gen3)(adc_tm5);
|
||||
|
||||
/* Disable all available TM channels */
|
||||
for (int i = 0; i < adc_tm5->nchannels; i++)
|
||||
adc_tm5_gen3_disable_channel(&adc_tm5->chan_props[i]);
|
||||
}
|
||||
|
||||
static int adc_tm5_probe(struct auxiliary_device *aux_dev,
|
||||
const struct auxiliary_device_id *id)
|
||||
{
|
||||
struct adc_tm5_gen3_chip *adc_tm5;
|
||||
struct tm5_aux_dev_wrapper *aux_dev_wrapper;
|
||||
struct device *dev = &aux_dev->dev;
|
||||
int ret;
|
||||
|
||||
adc_tm5 = devm_kzalloc(dev, sizeof(*adc_tm5), GFP_KERNEL);
|
||||
if (!adc_tm5)
|
||||
return -ENOMEM;
|
||||
|
||||
aux_dev_wrapper = container_of(aux_dev, struct tm5_aux_dev_wrapper, aux_dev);
|
||||
|
||||
adc_tm5->dev = dev;
|
||||
adc_tm5->dev_data = aux_dev_wrapper->dev_data;
|
||||
adc_tm5->nchannels = aux_dev_wrapper->n_tm_channels;
|
||||
adc_tm5->chan_props = devm_kcalloc(dev, aux_dev_wrapper->n_tm_channels,
|
||||
sizeof(*adc_tm5->chan_props), GFP_KERNEL);
|
||||
if (!adc_tm5->chan_props)
|
||||
return -ENOMEM;
|
||||
|
||||
for (int i = 0; i < adc_tm5->nchannels; i++) {
|
||||
/*
|
||||
* Since the first channel of the first SDAM is reserved for
|
||||
* immediate ADC conversions, TM channel count must start from
|
||||
* the channel just after it. The variable tm_count is used to
|
||||
* calculate SDAM and TM channel index on that SDAM correctly
|
||||
* for each TM channel.
|
||||
*/
|
||||
int tm_count = i + 1;
|
||||
|
||||
adc_tm5->chan_props[i].common_props = aux_dev_wrapper->tm_props[i];
|
||||
adc_tm5->chan_props[i].timer = MEAS_INT_1S;
|
||||
adc_tm5->chan_props[i].sdam_index = tm_count / 8;
|
||||
adc_tm5->chan_props[i].tm_chan_index = tm_count % 8;
|
||||
adc_tm5->chan_props[i].chip = adc_tm5;
|
||||
}
|
||||
|
||||
/*
|
||||
* ADC_TM channels are enabled in the loop in adc_tm5_register_tzd() as
|
||||
* part of the set_trips calls during thermal zone registration. This
|
||||
* action is to disable them all in case of probe failure.
|
||||
*/
|
||||
ret = devm_add_action(dev, adc5_gen3_disable, adc_tm5);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adc_tm5_register_tzd(adc_tm5);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (int i = 0; i < adc_tm5->dev_data->num_sdams; i++) {
|
||||
u32 irq_flags = IRQF_ONESHOT;
|
||||
|
||||
/*
|
||||
* First SDAM's interrupt is shared between main ADC driver and
|
||||
* auxiliary TM driver, so its flags must include IRQF_SHARED.
|
||||
* This is not needed for other SDAMs as they will be used only
|
||||
* for TM functionality.
|
||||
*/
|
||||
if (i == 0)
|
||||
irq_flags |= IRQF_SHARED;
|
||||
|
||||
ret = devm_request_threaded_irq(dev,
|
||||
adc_tm5->dev_data->base[i].irq,
|
||||
adctm5_gen3_isr,
|
||||
adctm5_gen3_isr_thread,
|
||||
irq_flags,
|
||||
adc_tm5->dev_data->base[i].irq_name,
|
||||
adc_tm5);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct auxiliary_device_id adctm5_auxiliary_id_table[] = {
|
||||
{ .name = "qcom_spmi_adc5_gen3.adc5_tm_gen3" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, adctm5_auxiliary_id_table);
|
||||
|
||||
static struct auxiliary_driver adctm5gen3_auxiliary_driver = {
|
||||
.id_table = adctm5_auxiliary_id_table,
|
||||
.probe = adc_tm5_probe,
|
||||
};
|
||||
module_auxiliary_driver(adctm5gen3_auxiliary_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SPMI PMIC Thermal Monitor ADC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS("QCOM_SPMI_ADC5_GEN3");
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
257
drivers/thermal/qcom/qcom-spmi-mbg-tm.c
Normal file
257
drivers/thermal/qcom/qcom-spmi-mbg-tm.c
Normal file
@@ -0,0 +1,257 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#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");
|
||||
MODULE_IMPORT_NS("IIO_CONSUMER");
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
@@ -889,7 +889,10 @@ static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz,
|
||||
kfree(pos);
|
||||
}
|
||||
|
||||
static struct class *thermal_class __ro_after_init;
|
||||
static const struct class thermal_class = {
|
||||
.name = "thermal",
|
||||
};
|
||||
static bool thermal_class_unavailable __ro_after_init = true;
|
||||
|
||||
static inline
|
||||
void print_bind_err_msg(struct thermal_zone_device *tz,
|
||||
@@ -973,7 +976,7 @@ thermal_cooling_device_alloc(const char *type, const struct thermal_cooling_devi
|
||||
!ops->set_cur_state)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!thermal_class)
|
||||
if (thermal_class_unavailable)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
cdev = kzalloc_obj(*cdev);
|
||||
@@ -1010,7 +1013,7 @@ int thermal_cooling_device_add(struct thermal_cooling_device *cdev, void *devdat
|
||||
mutex_init(&cdev->lock);
|
||||
INIT_LIST_HEAD(&cdev->thermal_instances);
|
||||
cdev->updated = false;
|
||||
cdev->device.class = thermal_class;
|
||||
cdev->device.class = &thermal_class;
|
||||
cdev->device.release = thermal_cdev_release;
|
||||
device_initialize(&cdev->device);
|
||||
cdev->devdata = devdata;
|
||||
@@ -1447,7 +1450,7 @@ thermal_zone_device_register_with_trips(const char *type,
|
||||
if (polling_delay && passive_delay > polling_delay)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!thermal_class)
|
||||
if (thermal_class_unavailable)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
tz = kzalloc_flex(*tz, trips, num_trips);
|
||||
@@ -1483,7 +1486,7 @@ thermal_zone_device_register_with_trips(const char *type,
|
||||
if (!tz->ops.critical)
|
||||
tz->ops.critical = thermal_zone_device_critical;
|
||||
|
||||
tz->device.class = thermal_class;
|
||||
tz->device.class = &thermal_class;
|
||||
tz->device.release = thermal_zone_device_release;
|
||||
tz->devdata = devdata;
|
||||
tz->num_trips = num_trips;
|
||||
@@ -1745,7 +1748,7 @@ static void __thermal_pm_prepare(void)
|
||||
|
||||
void thermal_pm_prepare(void)
|
||||
{
|
||||
if (!thermal_class)
|
||||
if (thermal_class_unavailable)
|
||||
return;
|
||||
|
||||
__thermal_pm_prepare();
|
||||
@@ -1776,7 +1779,7 @@ void thermal_pm_complete(void)
|
||||
{
|
||||
struct thermal_zone_device *tz;
|
||||
|
||||
if (!thermal_class)
|
||||
if (thermal_class_unavailable)
|
||||
return;
|
||||
|
||||
guard(mutex)(&thermal_list_lock);
|
||||
@@ -1789,7 +1792,6 @@ void thermal_pm_complete(void)
|
||||
|
||||
static int __init thermal_init(void)
|
||||
{
|
||||
struct class *tc;
|
||||
int result;
|
||||
|
||||
thermal_debug_init();
|
||||
@@ -1808,13 +1810,12 @@ static int __init thermal_init(void)
|
||||
if (result)
|
||||
goto unregister_governors;
|
||||
|
||||
tc = class_create("thermal");
|
||||
if (IS_ERR(tc)) {
|
||||
result = PTR_ERR(tc);
|
||||
result = class_register(&thermal_class);
|
||||
if (result)
|
||||
goto unregister_governors;
|
||||
}
|
||||
|
||||
thermal_class = tc;
|
||||
thermal_class_unavailable = false;
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_governors:
|
||||
|
||||
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