Thermal control updates for 6.17-rc1

- Convert EAGAIN into ENODATA in temp_show() to prevent user space from
    polling the sysfs file in vain after a failing O_NONBLOCK read under
    the assumption that the read would have blocked (Hsin-Te Yuan)
 
  - Add Wildcat Lake PCI ID to the int340x Intel processor thermal driver
    (Srinivas Pandruvada)
 
  - Add debugfs interface to override the temperature set by the firmware
    in the Intel platform temperature control (PTC) interface and add a
    new sysfs control attribute called thermal_tolerance to it (Srinivas
    Pandruvada)
 
  - Enable the stage 2 shutdown in the qcom-spmi-temp-alarm thermal
    driver and add support for more SPMI variants to it (Anjelique
    Melendez)
 
  - Constify the thermal_zone_device_ops structure where possible in
    several assorted thermal drivers (Christophe Jaillet)
 
  - Use the dev_fwnode() helper instead of of_fwnode_handle(), as it is
    more adequate, wherever possible in thermal drivers (Jiri Slaby)
 
  - Implement and document One-Time Programmable fuse support in the
    Rockchip thermal driver in order to increase the precision of the
    measurements (Nicolas Frattaroli)
 
  - Change the way the Mediatek LTVS thermal driver stores the
    initialization data sequence to support different sequences
    matching different platforms. Introduce mt7988 support with
    a new initialization sequence (Mason Chang)
 
  - Document the QCom TSens Milos Temperature Sensor DT bindings (Luca
    Weiss)
 
  - Add the fallback compatible string for MT7981 and MT8516 DT bindings
    (Aleksander Jan Bajkowski)
 
  - Add the compatible string for the Tegra210B01 SOC_THERM driver (Aaron
    Kling)
 -----BEGIN PGP SIGNATURE-----
 
 iQFGBAABCAAwFiEEcM8Aw/RY0dgsiRUR7l+9nS/U47UFAmh/j4ESHHJqd0Byand5
 c29ja2kubmV0AAoJEO5fvZ0v1OO1r54IAImMVSzbSfMvvB0b1PDa2nEbBVt8xNEg
 2BPwZv1CrdJXq95VJQDJQRmsdZVzzNIL0gRruyaXnSDa4zhyoNb3nXhjY7eNqhlP
 2CBANscs7zgQ3DiAFuz3VJ7ns10KCAAEj2r3gdTxkm0MveOrDhOn3bLdr9+NN7P8
 n0zo9p4W2RDXXXjTMJul0qliZpJoadDqN+fl2V/u7vPLcjlp1JBV84sZPKt8mGIe
 o44Sp9NSjZ20GDpOPzXKJnK9RRQsxxs40x4d3C3kpmiagzMjyV3ku1K2e0FAucUv
 Y0V91HiHCgdNSLa/Z4laIdFJc6Wa8tsEb7mItg96cPFmSCaFvcZPucM=
 =ysPD
 -----END PGP SIGNATURE-----

Merge tag 'thermal-6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull thermal control updates from Rafael Wysocki:
 "These update the thermal control sysfs interface and multiple thermal
  control drivers:

   - Convert EAGAIN into ENODATA in temp_show() to prevent user space
     from polling the sysfs file in vain after a failing O_NONBLOCK read
     under the assumption that the read would have blocked (Hsin-Te
     Yuan)

   - Add Wildcat Lake PCI ID to the int340x Intel processor thermal
     driver (Srinivas Pandruvada)

   - Add debugfs interface to override the temperature set by the
     firmware in the Intel platform temperature control (PTC) interface
     and add a new sysfs control attribute called thermal_tolerance to
     it (Srinivas Pandruvada)

   - Enable the stage 2 shutdown in the qcom-spmi-temp-alarm thermal
     driver and add support for more SPMI variants to it (Anjelique
     Melendez)

   - Constify the thermal_zone_device_ops structure where possible in
     several assorted thermal drivers (Christophe Jaillet)

   - Use the dev_fwnode() helper instead of of_fwnode_handle(), as it is
     more adequate, wherever possible in thermal drivers (Jiri Slaby)

   - Implement and document One-Time Programmable fuse support in the
     Rockchip thermal driver in order to increase the precision of the
     measurements (Nicolas Frattaroli)

   - Change the way the Mediatek LTVS thermal driver stores the
     initialization data sequence to support different sequences
     matching different platforms. Introduce mt7988 support with a new
     initialization sequence (Mason Chang)

   - Document the QCom TSens Milos Temperature Sensor DT bindings (Luca
     Weiss)

   - Add the fallback compatible string for MT7981 and MT8516 DT
     bindings (Aleksander Jan Bajkowski)

   - Add the compatible string for the Tegra210B01 SOC_THERM driver
     (Aaron Kling)"

* tag 'thermal-6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (23 commits)
  dt-bindings: thermal: tegra: Document Tegra210B01
  dt-bindings: thermal: mediatek: Add fallback compatible string for MT7981 and MT8516
  dt-bindings: thermal: qcom-tsens: document the Milos Temperature Sensor
  thermal/drivers/mediatek/lvts_thermal: Add mt7988 lvts commands
  thermal/drivers/mediatek/lvts_thermal: Add lvts commands and their sizes to driver data
  thermal/drivers/mediatek/lvts_thermal: Change lvts commands array to static const
  thermal/drivers/rockchip: Support reading trim values from OTP
  dt-bindings: thermal: rockchip: document otp thermal trim
  thermal/drivers/rockchip: Support RK3576 SoC in the thermal driver
  dt-bindings: rockchip-thermal: Add RK3576 compatible
  thermal/drivers/rockchip: Rename rk_tsadcv3_tshut_mode
  thermal: Use dev_fwnode()
  thermal: Constify struct thermal_zone_device_ops
  thermal/drivers/loongson2: Constify struct thermal_zone_device_ops
  thermal/drivers/qcom-spmi-temp-alarm: Add support for LITE PMIC peripherals
  thermal/drivers/qcom-spmi-temp-alarm: Add support for GEN2 rev 2 PMIC peripherals
  thermal/drivers/qcom-spmi-temp-alarm: Prepare to support additional Temp Alarm subtypes
  thermal/drivers/qcom-spmi-temp-alarm: Add temp alarm data struct based on HW subtype
  thermal/drivers/qcom-spmi-temp-alarm: Enable stage 2 shutdown when required
  thermal: sysfs: Return ENODATA instead of EAGAIN for reads
  ...
This commit is contained in:
Linus Torvalds 2025-07-28 21:02:08 -07:00
commit e30fc09082
26 changed files with 1014 additions and 150 deletions

View File

@ -20,16 +20,23 @@ allOf:
properties:
compatible:
enum:
- mediatek,mt2701-thermal
- mediatek,mt2712-thermal
- mediatek,mt7622-thermal
- mediatek,mt7981-thermal
- mediatek,mt7986-thermal
- mediatek,mt8173-thermal
- mediatek,mt8183-thermal
- mediatek,mt8365-thermal
- mediatek,mt8516-thermal
oneOf:
- enum:
- mediatek,mt2701-thermal
- mediatek,mt2712-thermal
- mediatek,mt7622-thermal
- mediatek,mt7986-thermal
- mediatek,mt8173-thermal
- mediatek,mt8183-thermal
- mediatek,mt8365-thermal
- items:
- enum:
- mediatek,mt8516-thermal
- const: mediatek,mt2701-thermal
- items:
- enum:
- mediatek,mt7981-thermal
- const: mediatek,mt7986-thermal
reg:
maxItems: 1

View File

@ -21,6 +21,7 @@ properties:
- nvidia,tegra124-soctherm
- nvidia,tegra132-soctherm
- nvidia,tegra210-soctherm
- nvidia,tegra210b01-soctherm
reg:
maxItems: 2
@ -207,6 +208,7 @@ allOf:
enum:
- nvidia,tegra124-soctherm
- nvidia,tegra210-soctherm
- nvidia,tegra210b01-soctherm
then:
properties:
reg:

View File

@ -49,6 +49,7 @@ properties:
- description: v2 of TSENS
items:
- enum:
- qcom,milos-tsens
- qcom,msm8953-tsens
- qcom,msm8996-tsens
- qcom,msm8998-tsens

View File

@ -21,6 +21,7 @@ properties:
- rockchip,rk3368-tsadc
- rockchip,rk3399-tsadc
- rockchip,rk3568-tsadc
- rockchip,rk3576-tsadc
- rockchip,rk3588-tsadc
- rockchip,rv1108-tsadc
@ -39,6 +40,17 @@ properties:
- const: tsadc
- const: apb_pclk
nvmem-cells:
items:
- description: cell handle to where the trim's base temperature is stored
- description:
cell handle to where the trim's tenths of Celsius base value is stored
nvmem-cell-names:
items:
- const: trim_base
- const: trim_base_frac
resets:
minItems: 1
maxItems: 3
@ -50,6 +62,12 @@ properties:
- const: tsadc
- const: tsadc-phy
"#address-cells":
const: 1
"#size-cells":
const: 0
"#thermal-sensor-cells":
const: 1
@ -71,6 +89,27 @@ properties:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
patternProperties:
"@[0-9a-f]+$":
type: object
properties:
reg:
maxItems: 1
description: sensor ID, a.k.a. channel number
nvmem-cells:
items:
- description: handle of cell containing calibration data
nvmem-cell-names:
items:
- const: trim
required:
- reg
unevaluatedProperties: false
required:
- compatible
- reg
@ -79,6 +118,29 @@ required:
- clock-names
- resets
allOf:
- if:
not:
properties:
compatible:
contains:
const: rockchip,rk3568-tsadc
then:
properties:
nvmem-cells: false
nvmem-cell-names: false
- if:
not:
properties:
compatible:
contains:
enum:
- rockchip,rk3568-tsadc
- rockchip,rk3576-tsadc
then:
patternProperties:
"@[0-9a-f]+$": false
unevaluatedProperties: false
examples:

View File

@ -206,6 +206,15 @@ All these controls needs admin privilege to update.
Update a new temperature target in milli degree celsius for hardware to
use for the temperature control.
``thermal_tolerance`` (RW)
This attribute ranges from 0 to 7, where 0 represents
the most aggressive control to avoid any temperature overshoots, and
7 represents a more graceful approach, favoring performance even at
the expense of temperature overshoots.
Note: This level may not scale linearly. For example, a value of 3 does
not necessarily imply a 50% improvement in performance compared to a
value of 0.
Given that this is platform temperature control, it is expected that a
single user-level manager owns and manages the controls. If multiple
user-level software applications attempt to write different targets, it

View File

@ -408,7 +408,7 @@ static int armada_get_temp_legacy(struct thermal_zone_device *thermal,
return ret;
}
static struct thermal_zone_device_ops legacy_ops = {
static const struct thermal_zone_device_ops legacy_ops = {
.get_temp = armada_get_temp_legacy,
};

View File

@ -137,7 +137,7 @@ static int da9062_thermal_get_temp(struct thermal_zone_device *z,
return 0;
}
static struct thermal_zone_device_ops da9062_thermal_ops = {
static const struct thermal_zone_device_ops da9062_thermal_ops = {
.get_temp = da9062_thermal_get_temp,
};

View File

@ -106,7 +106,7 @@ static int dove_get_temp(struct thermal_zone_device *thermal,
return 0;
}
static struct thermal_zone_device_ops ops = {
static const struct thermal_zone_device_ops ops = {
.get_temp = dove_get_temp,
};

View File

@ -361,7 +361,7 @@ static bool imx_should_bind(struct thermal_zone_device *tz,
return trip->type == THERMAL_TRIP_PASSIVE;
}
static struct thermal_zone_device_ops imx_tz_ops = {
static const struct thermal_zone_device_ops imx_tz_ops = {
.should_bind = imx_should_bind,
.get_temp = imx_get_temp,
.change_mode = imx_change_mode,

View File

@ -515,7 +515,7 @@ static int int3400_thermal_change_mode(struct thermal_zone_device *thermal,
return result;
}
static struct thermal_zone_device_ops int3400_thermal_ops = {
static const struct thermal_zone_device_ops int3400_thermal_ops = {
.get_temp = int3400_thermal_get_temp,
.change_mode = int3400_thermal_change_mode,
};

View File

@ -38,6 +38,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/pci.h>
#include "processor_thermal_device.h"
@ -49,14 +50,16 @@ struct mmio_reg {
};
#define MAX_ATTR_GROUP_NAME_LEN 32
#define PTC_MAX_ATTRS 3
#define PTC_MAX_ATTRS 4
struct ptc_data {
u32 offset;
struct pci_dev *pdev;
struct attribute_group ptc_attr_group;
struct attribute *ptc_attrs[PTC_MAX_ATTRS];
struct device_attribute temperature_target_attr;
struct device_attribute enable_attr;
struct device_attribute thermal_tolerance_attr;
char group_name[MAX_ATTR_GROUP_NAME_LEN];
};
@ -78,6 +81,7 @@ static u32 ptc_offsets[PTC_MAX_INSTANCES] = {0x5B20, 0x5B28, 0x5B30};
static const char * const ptc_strings[] = {
"temperature_target",
"enable",
"thermal_tolerance",
NULL
};
@ -177,6 +181,8 @@ PTC_SHOW(temperature_target);
PTC_STORE(temperature_target);
PTC_SHOW(enable);
PTC_STORE(enable);
PTC_SHOW(thermal_tolerance);
PTC_STORE(thermal_tolerance);
#define ptc_init_attribute(_name)\
do {\
@ -193,9 +199,11 @@ static int ptc_create_groups(struct pci_dev *pdev, int instance, struct ptc_data
ptc_init_attribute(temperature_target);
ptc_init_attribute(enable);
ptc_init_attribute(thermal_tolerance);
data->ptc_attrs[index++] = &data->temperature_target_attr.attr;
data->ptc_attrs[index++] = &data->enable_attr.attr;
data->ptc_attrs[index++] = &data->thermal_tolerance_attr.attr;
data->ptc_attrs[index] = NULL;
snprintf(data->group_name, MAX_ATTR_GROUP_NAME_LEN,
@ -209,6 +217,63 @@ static int ptc_create_groups(struct pci_dev *pdev, int instance, struct ptc_data
}
static struct ptc_data ptc_instance[PTC_MAX_INSTANCES];
static struct dentry *ptc_debugfs;
#define PTC_TEMP_OVERRIDE_ENABLE_INDEX 4
#define PTC_TEMP_OVERRIDE_INDEX 5
static ssize_t ptc_temperature_write(struct file *file, const char __user *data,
size_t count, loff_t *ppos)
{
struct ptc_data *ptc_instance = file->private_data;
struct pci_dev *pdev = ptc_instance->pdev;
char buf[32];
ssize_t len;
u32 value;
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, data, len))
return -EFAULT;
buf[len] = '\0';
if (kstrtouint(buf, 0, &value))
return -EINVAL;
if (ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].units)
value /= ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].units;
if (value > ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].mask)
return -EINVAL;
if (!value) {
ptc_mmio_write(pdev, ptc_instance->offset, PTC_TEMP_OVERRIDE_ENABLE_INDEX, 0);
} else {
ptc_mmio_write(pdev, ptc_instance->offset, PTC_TEMP_OVERRIDE_INDEX, value);
ptc_mmio_write(pdev, ptc_instance->offset, PTC_TEMP_OVERRIDE_ENABLE_INDEX, 1);
}
return count;
}
static const struct file_operations ptc_fops = {
.open = simple_open,
.write = ptc_temperature_write,
.llseek = generic_file_llseek,
};
static void ptc_create_debugfs(void)
{
ptc_debugfs = debugfs_create_dir("platform_temperature_control", NULL);
debugfs_create_file("temperature_0", 0200, ptc_debugfs, &ptc_instance[0], &ptc_fops);
debugfs_create_file("temperature_1", 0200, ptc_debugfs, &ptc_instance[1], &ptc_fops);
debugfs_create_file("temperature_2", 0200, ptc_debugfs, &ptc_instance[2], &ptc_fops);
}
static void ptc_delete_debugfs(void)
{
debugfs_remove_recursive(ptc_debugfs);
}
int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
{
@ -217,8 +282,11 @@ int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_
for (i = 0; i < PTC_MAX_INSTANCES; i++) {
ptc_instance[i].offset = ptc_offsets[i];
ptc_instance[i].pdev = pdev;
ptc_create_groups(pdev, i, &ptc_instance[i]);
}
ptc_create_debugfs();
}
return 0;
@ -234,6 +302,8 @@ void proc_thermal_ptc_remove(struct pci_dev *pdev)
for (i = 0; i < PTC_MAX_INSTANCES; i++)
sysfs_remove_group(&pdev->dev.kobj, &ptc_instance[i].ptc_attr_group);
ptc_delete_debugfs();
}
}
EXPORT_SYMBOL_GPL(proc_thermal_ptc_remove);

View File

@ -31,6 +31,7 @@
#define PCI_DEVICE_ID_INTEL_SKL_THERMAL 0x1903
#define PCI_DEVICE_ID_INTEL_TGL_THERMAL 0x9A03
#define PCI_DEVICE_ID_INTEL_PTL_THERMAL 0xB01D
#define PCI_DEVICE_ID_INTEL_WCL_THERMAL 0xFD1D
struct power_config {
u32 index;

View File

@ -499,6 +499,10 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS |
PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_WT_HINT |
PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC) },
{ PCI_DEVICE_DATA(INTEL, WCL_THERMAL, PROC_THERMAL_FEATURE_MSI_SUPPORT |
PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR |
PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_HINT |
PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC) },
{ },
};

View File

@ -442,6 +442,7 @@ int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc
switch (pdev->device) {
case PCI_DEVICE_ID_INTEL_LNLM_THERMAL:
case PCI_DEVICE_ID_INTEL_PTL_THERMAL:
case PCI_DEVICE_ID_INTEL_WCL_THERMAL:
dlvr_mmio_regs_table = lnl_dlvr_mmio_regs;
dlvr_mapping = lnl_dlvr_mapping;
break;

View File

@ -48,7 +48,7 @@ static int kirkwood_get_temp(struct thermal_zone_device *thermal,
return 0;
}
static struct thermal_zone_device_ops ops = {
static const struct thermal_zone_device_ops ops = {
.get_temp = kirkwood_get_temp,
};

View File

@ -112,13 +112,19 @@ static int loongson2_thermal_set_trips(struct thermal_zone_device *tz, int low,
return loongson2_thermal_set(data, low/MILLI, high/MILLI, true);
}
static struct thermal_zone_device_ops loongson2_of_thermal_ops = {
static const struct thermal_zone_device_ops loongson2_2k1000_of_thermal_ops = {
.get_temp = loongson2_2k1000_get_temp,
.set_trips = loongson2_thermal_set_trips,
};
static const struct thermal_zone_device_ops loongson2_2k2000_of_thermal_ops = {
.get_temp = loongson2_2k2000_get_temp,
.set_trips = loongson2_thermal_set_trips,
};
static int loongson2_thermal_probe(struct platform_device *pdev)
{
const struct thermal_zone_device_ops *thermal_ops;
struct device *dev = &pdev->dev;
struct loongson2_thermal_data *data;
struct thermal_zone_device *tzd;
@ -140,7 +146,9 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
if (IS_ERR(data->temp_reg))
return PTR_ERR(data->temp_reg);
loongson2_of_thermal_ops.get_temp = loongson2_2k2000_get_temp;
thermal_ops = &loongson2_2k2000_of_thermal_ops;
} else {
thermal_ops = &loongson2_2k1000_of_thermal_ops;
}
irq = platform_get_irq(pdev, 0);
@ -152,8 +160,7 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
loongson2_thermal_set(data, 0, 0, false);
for (i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
tzd = devm_thermal_of_zone_register(dev, i, data,
&loongson2_of_thermal_ops);
tzd = devm_thermal_of_zone_register(dev, i, data, thermal_ops);
if (!IS_ERR(tzd))
break;

View File

@ -125,7 +125,11 @@ struct lvts_ctrl_data {
struct lvts_data {
const struct lvts_ctrl_data *lvts_ctrl;
const u32 *conn_cmd;
const u32 *init_cmd;
int num_lvts_ctrl;
int num_conn_cmd;
int num_init_cmd;
int temp_factor;
int temp_offset;
int gt_calib_bit_offset;
@ -571,7 +575,7 @@ static irqreturn_t lvts_irq_handler(int irq, void *data)
return iret;
}
static struct thermal_zone_device_ops lvts_ops = {
static const struct thermal_zone_device_ops lvts_ops = {
.get_temp = lvts_get_temp,
.set_trips = lvts_set_trips,
};
@ -902,7 +906,7 @@ static void lvts_ctrl_monitor_enable(struct device *dev, struct lvts_ctrl *lvts_
* each write in the configuration register must be separated by a
* delay of 2 us.
*/
static void lvts_write_config(struct lvts_ctrl *lvts_ctrl, u32 *cmds, int nr_cmds)
static void lvts_write_config(struct lvts_ctrl *lvts_ctrl, const u32 *cmds, int nr_cmds)
{
int i;
@ -985,9 +989,10 @@ static int lvts_ctrl_set_enable(struct lvts_ctrl *lvts_ctrl, int enable)
static int lvts_ctrl_connect(struct device *dev, struct lvts_ctrl *lvts_ctrl)
{
u32 id, cmds[] = { 0xC103FFFF, 0xC502FF55 };
const struct lvts_data *lvts_data = lvts_ctrl->lvts_data;
u32 id;
lvts_write_config(lvts_ctrl, cmds, ARRAY_SIZE(cmds));
lvts_write_config(lvts_ctrl, lvts_data->conn_cmd, lvts_data->num_conn_cmd);
/*
* LVTS_ID : Get ID and status of the thermal controller
@ -1006,17 +1011,9 @@ static int lvts_ctrl_connect(struct device *dev, struct lvts_ctrl *lvts_ctrl)
static int lvts_ctrl_initialize(struct device *dev, struct lvts_ctrl *lvts_ctrl)
{
/*
* Write device mask: 0xC1030000
*/
u32 cmds[] = {
0xC1030E01, 0xC1030CFC, 0xC1030A8C, 0xC103098D, 0xC10308F1,
0xC10307A6, 0xC10306B8, 0xC1030500, 0xC1030420, 0xC1030300,
0xC1030030, 0xC10300F6, 0xC1030050, 0xC1030060, 0xC10300AC,
0xC10300FC, 0xC103009D, 0xC10300F1, 0xC10300E1
};
const struct lvts_data *lvts_data = lvts_ctrl->lvts_data;
lvts_write_config(lvts_ctrl, cmds, ARRAY_SIZE(cmds));
lvts_write_config(lvts_ctrl, lvts_data->init_cmd, lvts_data->num_init_cmd);
return 0;
}
@ -1445,6 +1442,25 @@ static int lvts_resume(struct device *dev)
return 0;
}
static const u32 default_conn_cmds[] = { 0xC103FFFF, 0xC502FF55 };
static const u32 mt7988_conn_cmds[] = { 0xC103FFFF, 0xC502FC55 };
/*
* Write device mask: 0xC1030000
*/
static const u32 default_init_cmds[] = {
0xC1030E01, 0xC1030CFC, 0xC1030A8C, 0xC103098D, 0xC10308F1,
0xC10307A6, 0xC10306B8, 0xC1030500, 0xC1030420, 0xC1030300,
0xC1030030, 0xC10300F6, 0xC1030050, 0xC1030060, 0xC10300AC,
0xC10300FC, 0xC103009D, 0xC10300F1, 0xC10300E1
};
static const u32 mt7988_init_cmds[] = {
0xC1030300, 0xC1030420, 0xC1030500, 0xC10307A6, 0xC1030CFC,
0xC1030A8C, 0xC103098D, 0xC10308F1, 0xC1030B04, 0xC1030E01,
0xC10306B8
};
/*
* The MT8186 calibration data is stored as packed 3-byte little-endian
* values using a weird layout that makes sense only when viewed as a 32-bit
@ -1739,7 +1755,11 @@ static const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = {
static const struct lvts_data mt7988_lvts_ap_data = {
.lvts_ctrl = mt7988_lvts_ap_data_ctrl,
.conn_cmd = mt7988_conn_cmds,
.init_cmd = mt7988_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt7988_lvts_ap_data_ctrl),
.num_conn_cmd = ARRAY_SIZE(mt7988_conn_cmds),
.num_init_cmd = ARRAY_SIZE(mt7988_init_cmds),
.temp_factor = LVTS_COEFF_A_MT7988,
.temp_offset = LVTS_COEFF_B_MT7988,
.gt_calib_bit_offset = 24,
@ -1747,7 +1767,11 @@ static const struct lvts_data mt7988_lvts_ap_data = {
static const struct lvts_data mt8186_lvts_data = {
.lvts_ctrl = mt8186_lvts_data_ctrl,
.conn_cmd = default_conn_cmds,
.init_cmd = default_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt8186_lvts_data_ctrl),
.num_conn_cmd = ARRAY_SIZE(default_conn_cmds),
.num_init_cmd = ARRAY_SIZE(default_init_cmds),
.temp_factor = LVTS_COEFF_A_MT7988,
.temp_offset = LVTS_COEFF_B_MT7988,
.gt_calib_bit_offset = 24,
@ -1756,7 +1780,11 @@ static const struct lvts_data mt8186_lvts_data = {
static const struct lvts_data mt8188_lvts_mcu_data = {
.lvts_ctrl = mt8188_lvts_mcu_data_ctrl,
.conn_cmd = default_conn_cmds,
.init_cmd = default_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt8188_lvts_mcu_data_ctrl),
.num_conn_cmd = ARRAY_SIZE(default_conn_cmds),
.num_init_cmd = ARRAY_SIZE(default_init_cmds),
.temp_factor = LVTS_COEFF_A_MT8195,
.temp_offset = LVTS_COEFF_B_MT8195,
.gt_calib_bit_offset = 20,
@ -1765,7 +1793,11 @@ static const struct lvts_data mt8188_lvts_mcu_data = {
static const struct lvts_data mt8188_lvts_ap_data = {
.lvts_ctrl = mt8188_lvts_ap_data_ctrl,
.conn_cmd = default_conn_cmds,
.init_cmd = default_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt8188_lvts_ap_data_ctrl),
.num_conn_cmd = ARRAY_SIZE(default_conn_cmds),
.num_init_cmd = ARRAY_SIZE(default_init_cmds),
.temp_factor = LVTS_COEFF_A_MT8195,
.temp_offset = LVTS_COEFF_B_MT8195,
.gt_calib_bit_offset = 20,
@ -1774,7 +1806,11 @@ static const struct lvts_data mt8188_lvts_ap_data = {
static const struct lvts_data mt8192_lvts_mcu_data = {
.lvts_ctrl = mt8192_lvts_mcu_data_ctrl,
.conn_cmd = default_conn_cmds,
.init_cmd = default_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt8192_lvts_mcu_data_ctrl),
.num_conn_cmd = ARRAY_SIZE(default_conn_cmds),
.num_init_cmd = ARRAY_SIZE(default_init_cmds),
.temp_factor = LVTS_COEFF_A_MT8195,
.temp_offset = LVTS_COEFF_B_MT8195,
.gt_calib_bit_offset = 24,
@ -1783,7 +1819,11 @@ static const struct lvts_data mt8192_lvts_mcu_data = {
static const struct lvts_data mt8192_lvts_ap_data = {
.lvts_ctrl = mt8192_lvts_ap_data_ctrl,
.conn_cmd = default_conn_cmds,
.init_cmd = default_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt8192_lvts_ap_data_ctrl),
.num_conn_cmd = ARRAY_SIZE(default_conn_cmds),
.num_init_cmd = ARRAY_SIZE(default_init_cmds),
.temp_factor = LVTS_COEFF_A_MT8195,
.temp_offset = LVTS_COEFF_B_MT8195,
.gt_calib_bit_offset = 24,
@ -1792,7 +1832,11 @@ static const struct lvts_data mt8192_lvts_ap_data = {
static const struct lvts_data mt8195_lvts_mcu_data = {
.lvts_ctrl = mt8195_lvts_mcu_data_ctrl,
.conn_cmd = default_conn_cmds,
.init_cmd = default_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt8195_lvts_mcu_data_ctrl),
.num_conn_cmd = ARRAY_SIZE(default_conn_cmds),
.num_init_cmd = ARRAY_SIZE(default_init_cmds),
.temp_factor = LVTS_COEFF_A_MT8195,
.temp_offset = LVTS_COEFF_B_MT8195,
.gt_calib_bit_offset = 24,
@ -1801,7 +1845,11 @@ static const struct lvts_data mt8195_lvts_mcu_data = {
static const struct lvts_data mt8195_lvts_ap_data = {
.lvts_ctrl = mt8195_lvts_ap_data_ctrl,
.conn_cmd = default_conn_cmds,
.init_cmd = default_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt8195_lvts_ap_data_ctrl),
.num_conn_cmd = ARRAY_SIZE(default_conn_cmds),
.num_init_cmd = ARRAY_SIZE(default_init_cmds),
.temp_factor = LVTS_COEFF_A_MT8195,
.temp_offset = LVTS_COEFF_B_MT8195,
.gt_calib_bit_offset = 24,

View File

@ -209,8 +209,7 @@ static int lmh_probe(struct platform_device *pdev)
}
lmh_data->irq = platform_get_irq(pdev, 0);
lmh_data->domain = irq_domain_create_linear(of_fwnode_handle(np), 1, &lmh_irq_ops,
lmh_data);
lmh_data->domain = irq_domain_create_linear(dev_fwnode(dev), 1, &lmh_irq_ops, lmh_data);
if (!lmh_data->domain) {
dev_err(dev, "Error adding irq_domain\n");
return -EINVAL;

View File

@ -1,8 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2011-2015, 2017, 2020, The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/err.h>
@ -16,31 +18,51 @@
#include "../thermal_hwmon.h"
#define QPNP_TM_REG_DIG_MINOR 0x00
#define QPNP_TM_REG_DIG_MAJOR 0x01
#define QPNP_TM_REG_TYPE 0x04
#define QPNP_TM_REG_SUBTYPE 0x05
#define QPNP_TM_REG_STATUS 0x08
#define QPNP_TM_REG_IRQ_STATUS 0x10
#define QPNP_TM_REG_SHUTDOWN_CTRL1 0x40
#define QPNP_TM_REG_ALARM_CTRL 0x46
/* TEMP_DAC_STGx registers are only present for TEMP_GEN2 v2.0 */
#define QPNP_TM_REG_TEMP_DAC_STG1 0x47
#define QPNP_TM_REG_TEMP_DAC_STG2 0x48
#define QPNP_TM_REG_TEMP_DAC_STG3 0x49
#define QPNP_TM_REG_LITE_TEMP_CFG1 0x50
#define QPNP_TM_REG_LITE_TEMP_CFG2 0x51
#define QPNP_TM_TYPE 0x09
#define QPNP_TM_SUBTYPE_GEN1 0x08
#define QPNP_TM_SUBTYPE_GEN2 0x09
#define QPNP_TM_SUBTYPE_LITE 0xC0
#define STATUS_GEN1_STAGE_MASK GENMASK(1, 0)
#define STATUS_GEN2_STATE_MASK GENMASK(6, 4)
#define STATUS_GEN2_STATE_SHIFT 4
#define SHUTDOWN_CTRL1_OVERRIDE_S2 BIT(6)
/* IRQ status only needed for TEMP_ALARM_LITE */
#define IRQ_STATUS_MASK BIT(0)
#define SHUTDOWN_CTRL1_OVERRIDE_STAGE2 BIT(6)
#define SHUTDOWN_CTRL1_THRESHOLD_MASK GENMASK(1, 0)
#define SHUTDOWN_CTRL1_RATE_25HZ BIT(3)
#define ALARM_CTRL_FORCE_ENABLE BIT(7)
#define LITE_TEMP_CFG_THRESHOLD_MASK GENMASK(3, 2)
#define THRESH_COUNT 4
#define STAGE_COUNT 3
enum overtemp_stage {
STAGE1 = 0,
STAGE2,
STAGE3,
};
/* Over-temperature trip point values in mC */
static const long temp_map_gen1[THRESH_COUNT][STAGE_COUNT] = {
{ 105000, 125000, 145000 },
@ -63,24 +85,68 @@ static const long temp_map_gen2_v1[THRESH_COUNT][STAGE_COUNT] = {
#define TEMP_STAGE_HYSTERESIS 2000
/*
* For TEMP_GEN2 v2.0, TEMP_DAC_STG1/2/3 registers are used to set the threshold
* for each stage independently.
* TEMP_DAC_STG* = 0 --> 80 C
* Each 8 step increase in TEMP_DAC_STG* value corresponds to 5 C (5000 mC).
*/
#define TEMP_DAC_MIN 80000
#define TEMP_DAC_SCALE_NUM 8
#define TEMP_DAC_SCALE_DEN 5000
#define TEMP_DAC_TEMP_TO_REG(temp) \
(((temp) - TEMP_DAC_MIN) * TEMP_DAC_SCALE_NUM / TEMP_DAC_SCALE_DEN)
#define TEMP_DAC_REG_TO_TEMP(reg) \
(TEMP_DAC_MIN + (reg) * TEMP_DAC_SCALE_DEN / TEMP_DAC_SCALE_NUM)
static const long temp_dac_max[STAGE_COUNT] = {
119375, 159375, 159375
};
/*
* TEMP_ALARM_LITE has two stages: warning and shutdown with independently
* configured threshold temperatures.
*/
static const long temp_lite_warning_map[THRESH_COUNT] = {
115000, 125000, 135000, 145000
};
static const long temp_lite_shutdown_map[THRESH_COUNT] = {
135000, 145000, 160000, 175000
};
/* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
#define DEFAULT_TEMP 37000
struct qpnp_tm_chip;
struct spmi_temp_alarm_data {
const struct thermal_zone_device_ops *ops;
const long (*temp_map)[THRESH_COUNT][STAGE_COUNT];
int (*sync_thresholds)(struct qpnp_tm_chip *chip);
int (*get_temp_stage)(struct qpnp_tm_chip *chip);
int (*configure_trip_temps)(struct qpnp_tm_chip *chip);
};
struct qpnp_tm_chip {
struct regmap *map;
struct device *dev;
struct thermal_zone_device *tz_dev;
const struct spmi_temp_alarm_data *data;
unsigned int subtype;
long temp;
unsigned int thresh;
unsigned int stage;
unsigned int base;
unsigned int ntrips;
/* protects .thresh, .stage and chip registers */
struct mutex lock;
bool initialized;
bool require_stage2_shutdown;
long temp_thresh_map[STAGE_COUNT];
struct iio_channel *adc;
const long (*temp_map)[THRESH_COUNT][STAGE_COUNT];
};
/* This array maps from GEN2 alarm state to GEN1 alarm stage */
@ -114,34 +180,66 @@ static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data)
*/
static long qpnp_tm_decode_temp(struct qpnp_tm_chip *chip, unsigned int stage)
{
if (!chip->temp_map || chip->thresh >= THRESH_COUNT || stage == 0 ||
stage > STAGE_COUNT)
if (stage == 0 || stage > STAGE_COUNT)
return 0;
return (*chip->temp_map)[chip->thresh][stage - 1];
return chip->temp_thresh_map[stage - 1];
}
/**
* qpnp_tm_get_temp_stage() - return over-temperature stage
* qpnp_tm_gen1_get_temp_stage() - return over-temperature stage
* @chip: Pointer to the qpnp_tm chip
*
* Return: stage (GEN1) or state (GEN2) on success, or errno on failure.
* Return: stage on success, or errno on failure.
*/
static int qpnp_tm_get_temp_stage(struct qpnp_tm_chip *chip)
static int qpnp_tm_gen1_get_temp_stage(struct qpnp_tm_chip *chip)
{
int ret;
u8 reg = 0;
u8 reg;
ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg);
if (ret < 0)
return ret;
if (chip->subtype == QPNP_TM_SUBTYPE_GEN1)
ret = reg & STATUS_GEN1_STAGE_MASK;
else
ret = (reg & STATUS_GEN2_STATE_MASK) >> STATUS_GEN2_STATE_SHIFT;
return FIELD_GET(STATUS_GEN1_STAGE_MASK, reg);
}
return ret;
/**
* qpnp_tm_gen2_get_temp_stage() - return over-temperature stage
* @chip: Pointer to the qpnp_tm chip
*
* Return: stage on success, or errno on failure.
*/
static int qpnp_tm_gen2_get_temp_stage(struct qpnp_tm_chip *chip)
{
int ret;
u8 reg;
ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg);
if (ret < 0)
return ret;
ret = FIELD_GET(STATUS_GEN2_STATE_MASK, reg);
return alarm_state_map[ret];
}
/**
* qpnp_tm_lite_get_temp_stage() - return over-temperature stage
* @chip: Pointer to the qpnp_tm chip
*
* Return: alarm interrupt state on success, or errno on failure.
*/
static int qpnp_tm_lite_get_temp_stage(struct qpnp_tm_chip *chip)
{
u8 reg = 0;
int ret;
ret = qpnp_tm_read(chip, QPNP_TM_REG_IRQ_STATUS, &reg);
if (ret < 0)
return ret;
return FIELD_GET(IRQ_STATUS_MASK, reg);
}
/*
@ -150,23 +248,16 @@ static int qpnp_tm_get_temp_stage(struct qpnp_tm_chip *chip)
*/
static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
{
unsigned int stage, stage_new, stage_old;
unsigned int stage_new, stage_old;
int ret;
WARN_ON(!mutex_is_locked(&chip->lock));
ret = qpnp_tm_get_temp_stage(chip);
ret = chip->data->get_temp_stage(chip);
if (ret < 0)
return ret;
stage = ret;
if (chip->subtype == QPNP_TM_SUBTYPE_GEN1) {
stage_new = stage;
stage_old = chip->stage;
} else {
stage_new = alarm_state_map[stage];
stage_old = alarm_state_map[chip->stage];
}
stage_new = ret;
stage_old = chip->stage;
if (stage_new > stage_old) {
/* increasing stage, use lower bound */
@ -178,7 +269,7 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
- TEMP_STAGE_HYSTERESIS;
}
chip->stage = stage;
chip->stage = stage_new;
return 0;
}
@ -218,35 +309,35 @@ static int qpnp_tm_get_temp(struct thermal_zone_device *tz, int *temp)
static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
int temp)
{
long stage2_threshold_min = (*chip->temp_map)[THRESH_MIN][1];
long stage2_threshold_max = (*chip->temp_map)[THRESH_MAX][1];
bool disable_s2_shutdown = false;
u8 reg;
long stage2_threshold_min = (*chip->data->temp_map)[THRESH_MIN][STAGE2];
long stage2_threshold_max = (*chip->data->temp_map)[THRESH_MAX][STAGE2];
bool disable_stage2_shutdown = false;
u8 reg, threshold;
WARN_ON(!mutex_is_locked(&chip->lock));
/*
* Default: S2 and S3 shutdown enabled, thresholds at
* Default: Stage 2 and Stage 3 shutdown enabled, thresholds at
* lowest threshold set, monitoring at 25Hz
*/
reg = SHUTDOWN_CTRL1_RATE_25HZ;
if (temp == THERMAL_TEMP_INVALID ||
temp < stage2_threshold_min) {
chip->thresh = THRESH_MIN;
threshold = THRESH_MIN;
goto skip;
}
if (temp <= stage2_threshold_max) {
chip->thresh = THRESH_MAX -
threshold = THRESH_MAX -
((stage2_threshold_max - temp) /
TEMP_THRESH_STEP);
disable_s2_shutdown = true;
disable_stage2_shutdown = true;
} else {
chip->thresh = THRESH_MAX;
threshold = THRESH_MAX;
if (chip->adc)
disable_s2_shutdown = true;
disable_stage2_shutdown = true;
else
dev_warn(chip->dev,
"No ADC is configured and critical temperature %d mC is above the maximum stage 2 threshold of %ld mC! Configuring stage 2 shutdown at %ld mC.\n",
@ -254,9 +345,11 @@ static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
}
skip:
reg |= chip->thresh;
if (disable_s2_shutdown)
reg |= SHUTDOWN_CTRL1_OVERRIDE_S2;
memcpy(chip->temp_thresh_map, chip->data->temp_map[threshold],
sizeof(chip->temp_thresh_map));
reg |= threshold;
if (disable_stage2_shutdown && !chip->require_stage2_shutdown)
reg |= SHUTDOWN_CTRL1_OVERRIDE_STAGE2;
return qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
}
@ -282,6 +375,146 @@ static const struct thermal_zone_device_ops qpnp_tm_sensor_ops = {
.set_trip_temp = qpnp_tm_set_trip_temp,
};
static int qpnp_tm_gen2_rev2_set_temp_thresh(struct qpnp_tm_chip *chip, unsigned int trip, int temp)
{
int ret, temp_cfg;
u8 reg;
WARN_ON(!mutex_is_locked(&chip->lock));
if (trip >= STAGE_COUNT) {
dev_err(chip->dev, "invalid TEMP_DAC trip = %d\n", trip);
return -EINVAL;
} else if (temp < TEMP_DAC_MIN || temp > temp_dac_max[trip]) {
dev_err(chip->dev, "invalid TEMP_DAC temp = %d\n", temp);
return -EINVAL;
}
reg = TEMP_DAC_TEMP_TO_REG(temp);
temp_cfg = TEMP_DAC_REG_TO_TEMP(reg);
ret = qpnp_tm_write(chip, QPNP_TM_REG_TEMP_DAC_STG1 + trip, reg);
if (ret < 0) {
dev_err(chip->dev, "TEMP_DAC_STG write failed, ret=%d\n", ret);
return ret;
}
chip->temp_thresh_map[trip] = temp_cfg;
return 0;
}
static int qpnp_tm_gen2_rev2_set_trip_temp(struct thermal_zone_device *tz,
const struct thermal_trip *trip, int temp)
{
unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv);
struct qpnp_tm_chip *chip = thermal_zone_device_priv(tz);
int ret;
mutex_lock(&chip->lock);
ret = qpnp_tm_gen2_rev2_set_temp_thresh(chip, trip_index, temp);
mutex_unlock(&chip->lock);
return ret;
}
static const struct thermal_zone_device_ops qpnp_tm_gen2_rev2_sensor_ops = {
.get_temp = qpnp_tm_get_temp,
.set_trip_temp = qpnp_tm_gen2_rev2_set_trip_temp,
};
static int qpnp_tm_lite_set_temp_thresh(struct qpnp_tm_chip *chip, unsigned int trip, int temp)
{
int ret, temp_cfg, i;
const long *temp_map;
u8 reg, thresh;
u16 addr;
WARN_ON(!mutex_is_locked(&chip->lock));
if (trip >= STAGE_COUNT) {
dev_err(chip->dev, "invalid TEMP_LITE trip = %d\n", trip);
return -EINVAL;
}
switch (trip) {
case 0:
temp_map = temp_lite_warning_map;
addr = QPNP_TM_REG_LITE_TEMP_CFG1;
break;
case 1:
/*
* The second trip point is purely in software to facilitate
* a controlled shutdown after the warning threshold is crossed
* but before the automatic hardware shutdown threshold is
* crossed.
*/
return 0;
case 2:
temp_map = temp_lite_shutdown_map;
addr = QPNP_TM_REG_LITE_TEMP_CFG2;
break;
default:
return 0;
}
if (temp < temp_map[THRESH_MIN] || temp > temp_map[THRESH_MAX]) {
dev_err(chip->dev, "invalid TEMP_LITE temp = %d\n", temp);
return -EINVAL;
}
thresh = 0;
temp_cfg = temp_map[thresh];
for (i = THRESH_MAX; i >= THRESH_MIN; i--) {
if (temp >= temp_map[i]) {
thresh = i;
temp_cfg = temp_map[i];
break;
}
}
if (temp_cfg == chip->temp_thresh_map[trip])
return 0;
ret = qpnp_tm_read(chip, addr, &reg);
if (ret < 0) {
dev_err(chip->dev, "LITE_TEMP_CFG read failed, ret=%d\n", ret);
return ret;
}
reg &= ~LITE_TEMP_CFG_THRESHOLD_MASK;
reg |= FIELD_PREP(LITE_TEMP_CFG_THRESHOLD_MASK, thresh);
ret = qpnp_tm_write(chip, addr, reg);
if (ret < 0) {
dev_err(chip->dev, "LITE_TEMP_CFG write failed, ret=%d\n", ret);
return ret;
}
chip->temp_thresh_map[trip] = temp_cfg;
return 0;
}
static int qpnp_tm_lite_set_trip_temp(struct thermal_zone_device *tz,
const struct thermal_trip *trip, int temp)
{
unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv);
struct qpnp_tm_chip *chip = thermal_zone_device_priv(tz);
int ret;
mutex_lock(&chip->lock);
ret = qpnp_tm_lite_set_temp_thresh(chip, trip_index, temp);
mutex_unlock(&chip->lock);
return ret;
}
static const struct thermal_zone_device_ops qpnp_tm_lite_sensor_ops = {
.get_temp = qpnp_tm_get_temp,
.set_trip_temp = qpnp_tm_lite_set_trip_temp,
};
static irqreturn_t qpnp_tm_isr(int irq, void *data)
{
struct qpnp_tm_chip *chip = data;
@ -291,49 +524,227 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data)
return IRQ_HANDLED;
}
/*
* This function initializes the internal temp value based on only the
* current thermal stage and threshold. Setup threshold control and
* disable shutdown override.
*/
static int qpnp_tm_init(struct qpnp_tm_chip *chip)
/* Read the hardware default stage threshold temperatures */
static int qpnp_tm_sync_thresholds(struct qpnp_tm_chip *chip)
{
unsigned int stage;
u8 reg, threshold;
int ret;
u8 reg = 0;
int crit_temp;
mutex_lock(&chip->lock);
ret = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, &reg);
if (ret < 0)
goto out;
return ret;
chip->thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK;
chip->temp = DEFAULT_TEMP;
threshold = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK;
memcpy(chip->temp_thresh_map, chip->data->temp_map[threshold],
sizeof(chip->temp_thresh_map));
ret = qpnp_tm_get_temp_stage(chip);
if (ret < 0)
goto out;
chip->stage = ret;
return ret;
}
stage = chip->subtype == QPNP_TM_SUBTYPE_GEN1
? chip->stage : alarm_state_map[chip->stage];
if (stage)
chip->temp = qpnp_tm_decode_temp(chip, stage);
mutex_unlock(&chip->lock);
static int qpnp_tm_configure_trip_temp(struct qpnp_tm_chip *chip)
{
int crit_temp, ret;
ret = thermal_zone_get_crit_temp(chip->tz_dev, &crit_temp);
if (ret)
crit_temp = THERMAL_TEMP_INVALID;
mutex_lock(&chip->lock);
ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp);
mutex_unlock(&chip->lock);
return ret;
}
/* Configure TEMP_DAC registers based on DT thermal_zone trips */
static int qpnp_tm_gen2_rev2_configure_trip_temps_cb(struct thermal_trip *trip, void *data)
{
struct qpnp_tm_chip *chip = data;
int ret;
mutex_lock(&chip->lock);
trip->priv = THERMAL_INT_TO_TRIP_PRIV(chip->ntrips);
ret = qpnp_tm_gen2_rev2_set_temp_thresh(chip, chip->ntrips, trip->temperature);
chip->ntrips++;
mutex_unlock(&chip->lock);
return ret;
}
static int qpnp_tm_gen2_rev2_configure_trip_temps(struct qpnp_tm_chip *chip)
{
int ret, i;
ret = thermal_zone_for_each_trip(chip->tz_dev,
qpnp_tm_gen2_rev2_configure_trip_temps_cb, chip);
if (ret < 0)
goto out;
return ret;
/* Verify that trips are strictly increasing. */
for (i = 1; i < STAGE_COUNT; i++) {
if (chip->temp_thresh_map[i] <= chip->temp_thresh_map[i - 1]) {
dev_err(chip->dev, "Threshold %d=%ld <= threshold %d=%ld\n",
i, chip->temp_thresh_map[i], i - 1,
chip->temp_thresh_map[i - 1]);
return -EINVAL;
}
}
return 0;
}
/* Read the hardware default TEMP_DAC stage threshold temperatures */
static int qpnp_tm_gen2_rev2_sync_thresholds(struct qpnp_tm_chip *chip)
{
int ret, i;
u8 reg = 0;
for (i = 0; i < STAGE_COUNT; i++) {
ret = qpnp_tm_read(chip, QPNP_TM_REG_TEMP_DAC_STG1 + i, &reg);
if (ret < 0)
return ret;
chip->temp_thresh_map[i] = TEMP_DAC_REG_TO_TEMP(reg);
}
return 0;
}
/* Configure TEMP_LITE registers based on DT thermal_zone trips */
static int qpnp_tm_lite_configure_trip_temps_cb(struct thermal_trip *trip, void *data)
{
struct qpnp_tm_chip *chip = data;
int ret;
mutex_lock(&chip->lock);
trip->priv = THERMAL_INT_TO_TRIP_PRIV(chip->ntrips);
ret = qpnp_tm_lite_set_temp_thresh(chip, chip->ntrips, trip->temperature);
chip->ntrips++;
mutex_unlock(&chip->lock);
return ret;
}
static int qpnp_tm_lite_configure_trip_temps(struct qpnp_tm_chip *chip)
{
int ret;
ret = thermal_zone_for_each_trip(chip->tz_dev, qpnp_tm_lite_configure_trip_temps_cb, chip);
if (ret < 0)
return ret;
/* Verify that trips are strictly increasing. */
if (chip->temp_thresh_map[2] <= chip->temp_thresh_map[0]) {
dev_err(chip->dev, "Threshold 2=%ld <= threshold 0=%ld\n",
chip->temp_thresh_map[2], chip->temp_thresh_map[0]);
return -EINVAL;
}
return 0;
}
/* Read the hardware default TEMP_LITE stage threshold temperatures */
static int qpnp_tm_lite_sync_thresholds(struct qpnp_tm_chip *chip)
{
int ret, thresh;
u8 reg = 0;
/*
* Store the warning trip temp in temp_thresh_map[0] and the shutdown trip
* temp in temp_thresh_map[2]. The second trip point is purely in software
* to facilitate a controlled shutdown after the warning threshold is
* crossed but before the automatic hardware shutdown threshold is
* crossed. Thus, there is no register to read for the second trip
* point.
*/
ret = qpnp_tm_read(chip, QPNP_TM_REG_LITE_TEMP_CFG1, &reg);
if (ret < 0)
return ret;
thresh = FIELD_GET(LITE_TEMP_CFG_THRESHOLD_MASK, reg);
chip->temp_thresh_map[0] = temp_lite_warning_map[thresh];
ret = qpnp_tm_read(chip, QPNP_TM_REG_LITE_TEMP_CFG2, &reg);
if (ret < 0)
return ret;
thresh = FIELD_GET(LITE_TEMP_CFG_THRESHOLD_MASK, reg);
chip->temp_thresh_map[2] = temp_lite_shutdown_map[thresh];
return 0;
}
static const struct spmi_temp_alarm_data spmi_temp_alarm_data = {
.ops = &qpnp_tm_sensor_ops,
.temp_map = &temp_map_gen1,
.sync_thresholds = qpnp_tm_sync_thresholds,
.configure_trip_temps = qpnp_tm_configure_trip_temp,
.get_temp_stage = qpnp_tm_gen1_get_temp_stage,
};
static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_data = {
.ops = &qpnp_tm_sensor_ops,
.temp_map = &temp_map_gen1,
.sync_thresholds = qpnp_tm_sync_thresholds,
.configure_trip_temps = qpnp_tm_configure_trip_temp,
.get_temp_stage = qpnp_tm_gen2_get_temp_stage,
};
static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_rev1_data = {
.ops = &qpnp_tm_sensor_ops,
.temp_map = &temp_map_gen2_v1,
.sync_thresholds = qpnp_tm_sync_thresholds,
.configure_trip_temps = qpnp_tm_configure_trip_temp,
.get_temp_stage = qpnp_tm_gen2_get_temp_stage,
};
static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_rev2_data = {
.ops = &qpnp_tm_gen2_rev2_sensor_ops,
.sync_thresholds = qpnp_tm_gen2_rev2_sync_thresholds,
.configure_trip_temps = qpnp_tm_gen2_rev2_configure_trip_temps,
.get_temp_stage = qpnp_tm_gen2_get_temp_stage,
};
static const struct spmi_temp_alarm_data spmi_temp_alarm_lite_data = {
.ops = &qpnp_tm_lite_sensor_ops,
.sync_thresholds = qpnp_tm_lite_sync_thresholds,
.configure_trip_temps = qpnp_tm_lite_configure_trip_temps,
.get_temp_stage = qpnp_tm_lite_get_temp_stage,
};
/*
* This function initializes the internal temp value based on only the
* current thermal stage and threshold.
*/
static int qpnp_tm_threshold_init(struct qpnp_tm_chip *chip)
{
int ret;
ret = chip->data->sync_thresholds(chip);
if (ret < 0)
return ret;
ret = chip->data->get_temp_stage(chip);
if (ret < 0)
return ret;
chip->stage = ret;
chip->temp = DEFAULT_TEMP;
if (chip->stage)
chip->temp = qpnp_tm_decode_temp(chip, chip->stage);
return ret;
}
/* This function initializes threshold control and disables shutdown override. */
static int qpnp_tm_init(struct qpnp_tm_chip *chip)
{
int ret;
u8 reg;
ret = chip->data->configure_trip_temps(chip);
if (ret < 0)
return ret;
/* Enable the thermal alarm PMIC module in always-on mode. */
reg = ALARM_CTRL_FORCE_ENABLE;
@ -341,8 +752,6 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip)
chip->initialized = true;
out:
mutex_unlock(&chip->lock);
return ret;
}
@ -350,8 +759,8 @@ static int qpnp_tm_probe(struct platform_device *pdev)
{
struct qpnp_tm_chip *chip;
struct device_node *node;
u8 type, subtype, dig_major;
u32 res;
u8 type, subtype, dig_major, dig_minor;
u32 res, dig_revision;
int ret, irq;
node = pdev->dev.of_node;
@ -402,18 +811,53 @@ static int qpnp_tm_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, ret,
"could not read dig_major\n");
ret = qpnp_tm_read(chip, QPNP_TM_REG_DIG_MINOR, &dig_minor);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"could not read dig_minor\n");
if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1
&& subtype != QPNP_TM_SUBTYPE_GEN2)) {
&& subtype != QPNP_TM_SUBTYPE_GEN2
&& subtype != QPNP_TM_SUBTYPE_LITE)) {
dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n",
type, subtype);
return -ENODEV;
}
chip->subtype = subtype;
if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 1)
chip->temp_map = &temp_map_gen2_v1;
if (subtype == QPNP_TM_SUBTYPE_GEN1)
chip->data = &spmi_temp_alarm_data;
else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major == 0)
chip->data = &spmi_temp_alarm_gen2_data;
else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major == 1)
chip->data = &spmi_temp_alarm_gen2_rev1_data;
else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 2)
chip->data = &spmi_temp_alarm_gen2_rev2_data;
else if (subtype == QPNP_TM_SUBTYPE_LITE)
chip->data = &spmi_temp_alarm_lite_data;
else
chip->temp_map = &temp_map_gen1;
return -ENODEV;
if (chip->subtype == QPNP_TM_SUBTYPE_GEN2) {
dig_revision = (dig_major << 8) | dig_minor;
/*
* Check if stage 2 automatic partial shutdown must remain
* enabled to avoid potential repeated faults upon reaching
* over-temperature stage 3.
*/
switch (dig_revision) {
case 0x0001:
case 0x0002:
case 0x0100:
case 0x0101:
chip->require_stage2_shutdown = true;
break;
}
}
ret = qpnp_tm_threshold_init(chip);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret, "threshold init failed\n");
/*
* Register the sensor before initializing the hardware to be able to
@ -421,7 +865,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
* before the hardware initialization is completed.
*/
chip->tz_dev = devm_thermal_of_zone_register(
&pdev->dev, 0, chip, &qpnp_tm_sensor_ops);
&pdev->dev, 0, chip, chip->data->ops);
if (IS_ERR(chip->tz_dev))
return dev_err_probe(&pdev->dev, PTR_ERR(chip->tz_dev),
"failed to register sensor\n");

View File

@ -277,7 +277,7 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, int *temp)
return rcar_thermal_get_current_temp(priv, temp);
}
static struct thermal_zone_device_ops rcar_thermal_zone_ops = {
static const struct thermal_zone_device_ops rcar_thermal_zone_ops = {
.get_temp = rcar_thermal_get_temp,
};

View File

@ -9,6 +9,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@ -69,16 +70,18 @@ struct chip_tsadc_table {
* struct rockchip_tsadc_chip - hold the private data of tsadc chip
* @chn_offset: the channel offset of the first channel
* @chn_num: the channel number of tsadc chip
* @tshut_temp: the hardware-controlled shutdown temperature value
* @trim_slope: used to convert the trim code to a temperature in millicelsius
* @tshut_temp: the hardware-controlled shutdown temperature value, with no trim
* @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
* @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
* @initialize: SoC special initialize tsadc controller method
* @irq_ack: clear the interrupt
* @control: enable/disable method for the tsadc controller
* @get_temp: get the temperature
* @get_temp: get the raw temperature, unadjusted by trim
* @set_alarm_temp: set the high temperature interrupt
* @set_tshut_temp: set the hardware-controlled shutdown temperature
* @set_tshut_mode: set the hardware-controlled shutdown mode
* @get_trim_code: convert a hardware temperature code to one adjusted for by trim
* @table: the chip-specific conversion table
*/
struct rockchip_tsadc_chip {
@ -86,6 +89,9 @@ struct rockchip_tsadc_chip {
int chn_offset;
int chn_num;
/* Used to convert trim code to trim temp */
int trim_slope;
/* The hardware-controlled tshut property */
int tshut_temp;
enum tshut_mode tshut_mode;
@ -105,6 +111,8 @@ struct rockchip_tsadc_chip {
int (*set_tshut_temp)(const struct chip_tsadc_table *table,
int chn, void __iomem *reg, int temp);
void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m);
int (*get_trim_code)(const struct chip_tsadc_table *table,
int code, int trim_base, int trim_base_frac);
/* Per-table methods */
struct chip_tsadc_table table;
@ -114,12 +122,16 @@ struct rockchip_tsadc_chip {
* struct rockchip_thermal_sensor - hold the information of thermal sensor
* @thermal: pointer to the platform/configuration data
* @tzd: pointer to a thermal zone
* @of_node: pointer to the device_node representing this sensor, if any
* @id: identifier of the thermal sensor
* @trim_temp: per-sensor trim temperature value
*/
struct rockchip_thermal_sensor {
struct rockchip_thermal_data *thermal;
struct thermal_zone_device *tzd;
struct device_node *of_node;
int id;
int trim_temp;
};
/**
@ -132,7 +144,11 @@ struct rockchip_thermal_sensor {
* @pclk: the advanced peripherals bus clock
* @grf: the general register file will be used to do static set by software
* @regs: the base address of tsadc controller
* @tshut_temp: the hardware-controlled shutdown temperature value
* @trim_base: major component of sensor trim value, in Celsius
* @trim_base_frac: minor component of sensor trim value, in Decicelsius
* @trim: fallback thermal trim value for each channel
* @tshut_temp: the hardware-controlled shutdown temperature value, with no trim
* @trim_temp: the fallback trim temperature for the whole sensor
* @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
* @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
*/
@ -149,7 +165,12 @@ struct rockchip_thermal_data {
struct regmap *grf;
void __iomem *regs;
int trim_base;
int trim_base_frac;
int trim;
int tshut_temp;
int trim_temp;
enum tshut_mode tshut_mode;
enum tshut_polarity tshut_polarity;
};
@ -249,6 +270,9 @@ struct rockchip_thermal_data {
#define GRF_CON_TSADC_CH_INV (0x10001 << 1)
#define RK_MAX_TEMP (180000)
/**
* struct tsadc_table - code to temperature conversion table
* @code: the value of adc channel
@ -1045,7 +1069,7 @@ static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs,
writel_relaxed(val, regs + TSADCV2_INT_EN);
}
static void rk_tsadcv3_tshut_mode(int chn, void __iomem *regs,
static void rk_tsadcv4_tshut_mode(int chn, void __iomem *regs,
enum tshut_mode mode)
{
u32 val_gpio, val_cru;
@ -1061,6 +1085,15 @@ static void rk_tsadcv3_tshut_mode(int chn, void __iomem *regs,
writel_relaxed(val_cru, regs + TSADCV3_HSHUT_CRU_INT_EN);
}
static int rk_tsadcv2_get_trim_code(const struct chip_tsadc_table *table,
int code, int trim_base, int trim_base_frac)
{
int temp = trim_base * 1000 + trim_base_frac * 100;
u32 base_code = rk_tsadcv2_temp_to_code(table, temp);
return code - base_code;
}
static const struct rockchip_tsadc_chip px30_tsadc_data = {
/* cpu, gpu */
.chn_offset = 0,
@ -1284,6 +1317,30 @@ static const struct rockchip_tsadc_chip rk3568_tsadc_data = {
},
};
static const struct rockchip_tsadc_chip rk3576_tsadc_data = {
/* top, big_core, little_core, ddr, npu, gpu */
.chn_offset = 0,
.chn_num = 6, /* six channels for tsadc */
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
.initialize = rk_tsadcv8_initialize,
.irq_ack = rk_tsadcv4_irq_ack,
.control = rk_tsadcv4_control,
.get_temp = rk_tsadcv4_get_temp,
.set_alarm_temp = rk_tsadcv3_alarm_temp,
.set_tshut_temp = rk_tsadcv3_tshut_temp,
.set_tshut_mode = rk_tsadcv4_tshut_mode,
.get_trim_code = rk_tsadcv2_get_trim_code,
.trim_slope = 923,
.table = {
.id = rk3588_code_table,
.length = ARRAY_SIZE(rk3588_code_table),
.data_mask = TSADCV4_DATA_MASK,
.mode = ADC_INCREMENT,
},
};
static const struct rockchip_tsadc_chip rk3588_tsadc_data = {
/* top, big_core0, big_core1, little_core, center, gpu, npu */
.chn_offset = 0,
@ -1297,7 +1354,7 @@ static const struct rockchip_tsadc_chip rk3588_tsadc_data = {
.get_temp = rk_tsadcv4_get_temp,
.set_alarm_temp = rk_tsadcv3_alarm_temp,
.set_tshut_temp = rk_tsadcv3_tshut_temp,
.set_tshut_mode = rk_tsadcv3_tshut_mode,
.set_tshut_mode = rk_tsadcv4_tshut_mode,
.table = {
.id = rk3588_code_table,
.length = ARRAY_SIZE(rk3588_code_table),
@ -1342,6 +1399,10 @@ static const struct of_device_id of_rockchip_thermal_match[] = {
.compatible = "rockchip,rk3568-tsadc",
.data = (void *)&rk3568_tsadc_data,
},
{
.compatible = "rockchip,rk3576-tsadc",
.data = (void *)&rk3576_tsadc_data,
},
{
.compatible = "rockchip,rk3588-tsadc",
.data = (void *)&rk3588_tsadc_data,
@ -1387,7 +1448,7 @@ static int rockchip_thermal_set_trips(struct thermal_zone_device *tz, int low, i
__func__, sensor->id, low, high);
return tsadc->set_alarm_temp(&tsadc->table,
sensor->id, thermal->regs, high);
sensor->id, thermal->regs, high + sensor->trim_temp);
}
static int rockchip_thermal_get_temp(struct thermal_zone_device *tz, int *out_temp)
@ -1399,6 +1460,8 @@ static int rockchip_thermal_get_temp(struct thermal_zone_device *tz, int *out_te
retval = tsadc->get_temp(&tsadc->table,
sensor->id, thermal->regs, out_temp);
*out_temp -= sensor->trim_temp;
return retval;
}
@ -1407,6 +1470,104 @@ static const struct thermal_zone_device_ops rockchip_of_thermal_ops = {
.set_trips = rockchip_thermal_set_trips,
};
/**
* rockchip_get_efuse_value - read an OTP cell from a device node
* @np: pointer to the device node with the nvmem-cells property
* @cell_name: name of cell that should be read
* @value: pointer to where the read value will be placed
*
* Return: Negative errno on failure, during which *value will not be touched,
* or 0 on success.
*/
static int rockchip_get_efuse_value(struct device_node *np, const char *cell_name,
int *value)
{
struct nvmem_cell *cell;
int ret = 0;
size_t len;
u8 *buf;
int i;
cell = of_nvmem_cell_get(np, cell_name);
if (IS_ERR(cell))
return PTR_ERR(cell);
buf = nvmem_cell_read(cell, &len);
nvmem_cell_put(cell);
if (IS_ERR(buf))
return PTR_ERR(buf);
if (len > sizeof(*value)) {
ret = -ERANGE;
goto exit;
}
/* Copy with implicit endian conversion */
*value = 0;
for (i = 0; i < len; i++)
*value |= (int) buf[i] << (8 * i);
exit:
kfree(buf);
return ret;
}
static int rockchip_get_trim_configuration(struct device *dev, struct device_node *np,
struct rockchip_thermal_data *thermal)
{
const struct rockchip_tsadc_chip *tsadc = thermal->chip;
int trim_base = 0, trim_base_frac = 0, trim = 0;
int trim_code;
int ret;
thermal->trim_base = 0;
thermal->trim_base_frac = 0;
thermal->trim = 0;
if (!tsadc->get_trim_code)
return 0;
ret = rockchip_get_efuse_value(np, "trim_base", &trim_base);
if (ret < 0) {
if (ret == -ENOENT) {
trim_base = 30;
dev_dbg(dev, "trim_base is absent, defaulting to 30\n");
} else {
dev_err(dev, "failed reading nvmem value of trim_base: %pe\n",
ERR_PTR(ret));
return ret;
}
}
ret = rockchip_get_efuse_value(np, "trim_base_frac", &trim_base_frac);
if (ret < 0) {
if (ret == -ENOENT) {
dev_dbg(dev, "trim_base_frac is absent, defaulting to 0\n");
} else {
dev_err(dev, "failed reading nvmem value of trim_base_frac: %pe\n",
ERR_PTR(ret));
return ret;
}
}
thermal->trim_base = trim_base;
thermal->trim_base_frac = trim_base_frac;
/*
* If the tsadc node contains the trim property, then it is used in the
* absence of per-channel trim values
*/
if (!rockchip_get_efuse_value(np, "trim", &trim))
thermal->trim = trim;
if (trim) {
trim_code = tsadc->get_trim_code(&tsadc->table, trim,
trim_base, trim_base_frac);
thermal->trim_temp = thermal->chip->trim_slope * trim_code;
}
return 0;
}
static int rockchip_configure_from_dt(struct device *dev,
struct device_node *np,
struct rockchip_thermal_data *thermal)
@ -1467,6 +1628,8 @@ static int rockchip_configure_from_dt(struct device *dev,
if (IS_ERR(thermal->grf))
dev_warn(dev, "Missing rockchip,grf property\n");
rockchip_get_trim_configuration(dev, np, thermal);
return 0;
}
@ -1477,23 +1640,50 @@ rockchip_thermal_register_sensor(struct platform_device *pdev,
int id)
{
const struct rockchip_tsadc_chip *tsadc = thermal->chip;
struct device *dev = &pdev->dev;
int trim = thermal->trim;
int trim_code, tshut_temp;
int trim_temp = 0;
int error;
if (thermal->trim_temp)
trim_temp = thermal->trim_temp;
if (tsadc->get_trim_code && sensor->of_node) {
error = rockchip_get_efuse_value(sensor->of_node, "trim", &trim);
if (error < 0 && error != -ENOENT) {
dev_err(dev, "failed reading trim of sensor %d: %pe\n",
id, ERR_PTR(error));
return error;
}
if (trim) {
trim_code = tsadc->get_trim_code(&tsadc->table, trim,
thermal->trim_base,
thermal->trim_base_frac);
trim_temp = thermal->chip->trim_slope * trim_code;
}
}
sensor->trim_temp = trim_temp;
dev_dbg(dev, "trim of sensor %d is %d\n", id, sensor->trim_temp);
tshut_temp = min(thermal->tshut_temp + sensor->trim_temp, RK_MAX_TEMP);
tsadc->set_tshut_mode(id, thermal->regs, thermal->tshut_mode);
error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs,
thermal->tshut_temp);
error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs, tshut_temp);
if (error)
dev_err(&pdev->dev, "%s: invalid tshut=%d, error=%d\n",
__func__, thermal->tshut_temp, error);
dev_err(dev, "%s: invalid tshut=%d, error=%d\n",
__func__, tshut_temp, error);
sensor->thermal = thermal;
sensor->id = id;
sensor->tzd = devm_thermal_of_zone_register(&pdev->dev, id, sensor,
sensor->tzd = devm_thermal_of_zone_register(dev, id, sensor,
&rockchip_of_thermal_ops);
if (IS_ERR(sensor->tzd)) {
error = PTR_ERR(sensor->tzd);
dev_err(&pdev->dev, "failed to register sensor %d: %d\n",
dev_err(dev, "failed to register sensor %d: %d\n",
id, error);
return error;
}
@ -1516,9 +1706,11 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct rockchip_thermal_data *thermal;
struct device_node *child;
int irq;
int i;
int error;
u32 chn;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@ -1569,6 +1761,18 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
thermal->chip->initialize(thermal->grf, thermal->regs,
thermal->tshut_polarity);
for_each_available_child_of_node(np, child) {
if (!of_property_read_u32(child, "reg", &chn)) {
if (chn < thermal->chip->chn_num)
thermal->sensors[chn].of_node = child;
else
dev_warn(&pdev->dev,
"sensor address (%d) too large, ignoring its trim\n",
chn);
}
}
for (i = 0; i < thermal->chip->chn_num; i++) {
error = rockchip_thermal_register_sensor(pdev, thermal,
&thermal->sensors[i],
@ -1638,8 +1842,11 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev)
static int __maybe_unused rockchip_thermal_resume(struct device *dev)
{
struct rockchip_thermal_data *thermal = dev_get_drvdata(dev);
int i;
const struct rockchip_tsadc_chip *tsadc = thermal->chip;
struct rockchip_thermal_sensor *sensor;
int tshut_temp;
int error;
int i;
error = clk_enable(thermal->clk);
if (error)
@ -1653,21 +1860,23 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev)
rockchip_thermal_reset_controller(thermal->reset);
thermal->chip->initialize(thermal->grf, thermal->regs,
thermal->tshut_polarity);
tsadc->initialize(thermal->grf, thermal->regs, thermal->tshut_polarity);
for (i = 0; i < thermal->chip->chn_num; i++) {
int id = thermal->sensors[i].id;
sensor = &thermal->sensors[i];
thermal->chip->set_tshut_mode(id, thermal->regs,
tshut_temp = min(thermal->tshut_temp + sensor->trim_temp,
RK_MAX_TEMP);
tsadc->set_tshut_mode(sensor->id, thermal->regs,
thermal->tshut_mode);
error = thermal->chip->set_tshut_temp(&thermal->chip->table,
id, thermal->regs,
thermal->tshut_temp);
error = tsadc->set_tshut_temp(&thermal->chip->table,
sensor->id, thermal->regs,
tshut_temp);
if (error)
dev_err(dev, "%s: invalid tshut=%d, error=%d\n",
__func__, thermal->tshut_temp, error);
__func__, tshut_temp, error);
}
thermal->chip->control(thermal->regs, true);

View File

@ -41,7 +41,7 @@ static inline int thermal_get_temp(struct thermal_zone_device *thermal,
return 0;
}
static struct thermal_zone_device_ops ops = {
static const struct thermal_zone_device_ops ops = {
.get_temp = thermal_get_temp,
};

View File

@ -132,7 +132,7 @@ static int st_thermal_get_temp(struct thermal_zone_device *th, int *temperature)
return 0;
}
static struct thermal_zone_device_ops st_tz_ops = {
static const struct thermal_zone_device_ops st_tz_ops = {
.get_temp = st_thermal_get_temp,
};

View File

@ -1206,7 +1206,7 @@ static const struct irq_domain_ops soctherm_oc_domain_ops = {
/**
* soctherm_oc_int_init() - Initial enabling of the over
* current interrupts
* @np: The devicetree node for soctherm
* @fwnode: The devicetree node for soctherm
* @num_irqs: The number of new interrupt requests
*
* Sets the over current interrupt request chip data
@ -1215,7 +1215,7 @@ static const struct irq_domain_ops soctherm_oc_domain_ops = {
* -ENOMEM (out of memory), or irq_base if the function failed to
* allocate the irqs
*/
static int soctherm_oc_int_init(struct device_node *np, int num_irqs)
static int soctherm_oc_int_init(struct fwnode_handle *fwnode, int num_irqs)
{
if (!num_irqs) {
pr_info("%s(): OC interrupts are not enabled\n", __func__);
@ -1234,10 +1234,8 @@ static int soctherm_oc_int_init(struct device_node *np, int num_irqs)
soc_irq_cdata.irq_chip.irq_set_type = soctherm_oc_irq_set_type;
soc_irq_cdata.irq_chip.irq_set_wake = NULL;
soc_irq_cdata.domain = irq_domain_create_linear(of_fwnode_handle(np), num_irqs,
&soctherm_oc_domain_ops,
&soc_irq_cdata);
soc_irq_cdata.domain = irq_domain_create_linear(fwnode, num_irqs, &soctherm_oc_domain_ops,
&soc_irq_cdata);
if (!soc_irq_cdata.domain) {
pr_err("%s: Failed to create IRQ domain\n", __func__);
return -ENOMEM;
@ -1968,10 +1966,9 @@ static void tegra_soctherm_throttle(struct device *dev)
static int soctherm_interrupts_init(struct platform_device *pdev,
struct tegra_soctherm *tegra)
{
struct device_node *np = pdev->dev.of_node;
int ret;
ret = soctherm_oc_int_init(np, TEGRA_SOC_OC_IRQ_MAX);
ret = soctherm_oc_int_init(dev_fwnode(&pdev->dev), TEGRA_SOC_OC_IRQ_MAX);
if (ret < 0) {
dev_err(&pdev->dev, "soctherm_oc_int_init failed\n");
return ret;

View File

@ -381,7 +381,7 @@ static int tt_zone_get_temp(struct thermal_zone_device *tz, int *temp)
return 0;
}
static struct thermal_zone_device_ops tt_zone_ops = {
static const struct thermal_zone_device_ops tt_zone_ops = {
.get_temp = tt_zone_get_temp,
};

View File

@ -40,10 +40,13 @@ temp_show(struct device *dev, struct device_attribute *attr, char *buf)
ret = thermal_zone_get_temp(tz, &temperature);
if (ret)
return ret;
if (!ret)
return sprintf(buf, "%d\n", temperature);
return sprintf(buf, "%d\n", temperature);
if (ret == -EAGAIN)
return -ENODATA;
return ret;
}
static ssize_t