mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
- Use the newly introduced 'hot' and 'critical' ops for the acpi
thermal driver (Daniel Lezcano) - Remove the notify ops as it is no longer used (Daniel Lezcano) - Remove the 'forced passive' option and the unused bind/unbind functions (Daniel Lezcano) - Remove the THERMAL_TRIPS_NONE and the code cleanup around this macro (Daniel Lezcano) - Rework the delays to make them pre-computed instead of computing them again and again at each polling interval (Daniel Lezcano) - Remove the pointless 'thermal_zone_device_reset' function (Daniel Lezcano) - Use the critical and hot ops to prevent an unexpected system shutdown on int340x (Kai-Heng Feng) - Make the cooling device state private to the thermal subsystem (Daniel Lezcano) - Prevent to use not-power-aware actor devices with the power allocator governor (Lukasz Luba) - Remove 'zx' and 'tango' support along with the corresponding platforms (Arnd Bergman) - Fix several issues on the Omap thermal driver (Tony Lindgren) - Add support for adc-tm5 PMIC thermal monitor for Qcom platforms. Please note those changes rely on an immutable branch: iio-thermal-5.11-rc1/ib-iio-thermal-5.11-rc1 from the iio tree (Dmitry Baryshkov) - Fix an initialization loop in the adc-tm5 (Colin Ian King) - Fix a return error check in the cpufreq cooling device (Viresh Kumar) -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEGn3N4YVz0WNVyHskqDIjiipP6E8FAmAvkKMACgkQqDIjiipP 6E9yFggAmXy8t2j1mRvn/KLU+teTGIoSFkZ8mBnY2Sgip97IRZRhCwAZbUKOW0eI bvpAzBjacxdZHT7OxxvGzCOq/zlAh4UoStI8bMpzdUWPdkAj4ippArLYGvagLym8 WEQysWnrr8V1RCZbQuBNjyLwjf0fcXkzIBU1mbZXA8T8Y6Yn646TdtsrVT4Idg1j MOg7PAHBcTSY/wOReZKJ5TB1yvo2tNOuGOqUVbrIAHlRkiNTVHirVUq6aZGtTTKp 7ukcu8EI/o7XKBdQ5d9MZaHdwkcyAIJj4jdjmjkUJpa8VYQFPjayNyN3I+Py9lH2 jtWVYHQxZbY166IZP2yeXFjPzd6elw== =Jmz4 -----END PGP SIGNATURE----- Merge tag 'thermal-v5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux Pull thermal updates from Daniel Lezcano: - Use the newly introduced 'hot' and 'critical' ops for the acpi thermal driver (Daniel Lezcano) - Remove the notify ops as it is no longer used (Daniel Lezcano) - Remove the 'forced passive' option and the unused bind/unbind functions (Daniel Lezcano) - Remove the THERMAL_TRIPS_NONE and the code cleanup around this macro (Daniel Lezcano) - Rework the delays to make them pre-computed instead of computing them again and again at each polling interval (Daniel Lezcano) - Remove the pointless 'thermal_zone_device_reset' function (Daniel Lezcano) - Use the critical and hot ops to prevent an unexpected system shutdown on int340x (Kai-Heng Feng) - Make the cooling device state private to the thermal subsystem (Daniel Lezcano) - Prevent to use not-power-aware actor devices with the power allocator governor (Lukasz Luba) - Remove 'zx' and 'tango' support along with the corresponding platforms (Arnd Bergman) - Fix several issues on the Omap thermal driver (Tony Lindgren) - Add support for adc-tm5 PMIC thermal monitor for Qcom platforms (Dmitry Baryshkov) - Fix an initialization loop in the adc-tm5 (Colin Ian King) - Fix a return error check in the cpufreq cooling device (Viresh Kumar) * tag 'thermal-v5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux: (26 commits) thermal: cpufreq_cooling: freq_qos_update_request() returns < 0 on error thermal: qcom: Fix comparison with uninitialized variable channels_available thermal: qcom: add support for adc-tm5 PMIC thermal monitor dt-bindings: thermal: qcom: add adc-thermal monitor bindings thermal: ti-soc-thermal: Use non-inverted define for omap4 thermal: ti-soc-thermal: Simplify polling with iopoll thermal: ti-soc-thermal: Fix stuck sensor with continuous mode for 4430 thermal: ti-soc-thermal: Skip pointless register access for dra7 thermal/drivers/zx: Remove zx driver thermal/drivers/tango: Remove tango driver thermal: power allocator: fail binding for non-power actor devices thermal/core: Make cooling device state change private thermal: intel: pch: Fix unexpected shutdown at critical temperature thermal: int340x: Fix unexpected shutdown at critical temperature thermal/core: Remove pointless thermal_zone_device_reset() function thermal/core: Remove ms based delay fields thermal/core: Use precomputed jiffies for the polling thermal/core: Precompute the delays from msecs to jiffies thermal/core: Remove unused macro THERMAL_TRIPS_NONE thermal/core: Remove THERMAL_TRIPS_NONE test ...
This commit is contained in:
commit
5d26c176d5
153
Documentation/devicetree/bindings/thermal/qcom-spmi-adc-tm5.yaml
Normal file
153
Documentation/devicetree/bindings/thermal/qcom-spmi-adc-tm5.yaml
Normal file
@ -0,0 +1,153 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/thermal/qcom-spmi-adc-tm5.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm's SPMI PMIC ADC Thermal Monitoring
|
||||
maintainers:
|
||||
- Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,spmi-adc-tm5
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
"#thermal-sensor-cells":
|
||||
const: 1
|
||||
description:
|
||||
Number of cells required to uniquely identify the thermal sensors. Since
|
||||
we have multiple sensors this is set to 1
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
qcom,avg-samples:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Number of samples to be used for measurement.
|
||||
enum:
|
||||
- 1
|
||||
- 2
|
||||
- 4
|
||||
- 8
|
||||
- 16
|
||||
default: 1
|
||||
|
||||
qcom,decimation:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: This parameter is used to decrease ADC sampling rate.
|
||||
Quicker measurements can be made by reducing decimation ratio.
|
||||
enum:
|
||||
- 250
|
||||
- 420
|
||||
- 840
|
||||
default: 840
|
||||
|
||||
patternProperties:
|
||||
"^([-a-z0-9]*)@[0-7]$":
|
||||
type: object
|
||||
description:
|
||||
Represent one thermal sensor.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Specify the sensor channel. There are 8 channels in PMIC5's ADC TM
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
|
||||
io-channels:
|
||||
description:
|
||||
From common IIO binding. Used to pipe PMIC ADC channel to thermal monitor
|
||||
|
||||
qcom,ratiometric:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Channel calibration type.
|
||||
If this property is specified VADC will use the VDD reference
|
||||
(1.875V) and GND for channel calibration. If property is not found,
|
||||
channel will be calibrated with 0V and 1.25V reference channels,
|
||||
also known as absolute calibration.
|
||||
|
||||
qcom,hw-settle-time-us:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Time between AMUX getting configured and the ADC starting conversion.
|
||||
enum: [15, 100, 200, 300, 400, 500, 600, 700, 1000, 2000, 4000, 8000, 16000, 32000, 64000, 128000]
|
||||
|
||||
qcom,pre-scaling:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
description: Used for scaling the channel input signal before the
|
||||
signal is fed to VADC. The configuration for this node is to know the
|
||||
pre-determined ratio and use it for post scaling. It is a pair of
|
||||
integers, denoting the numerator and denominator of the fraction by
|
||||
which input signal is multiplied. For example, <1 3> indicates the
|
||||
signal is scaled down to 1/3 of its value before ADC measurement. If
|
||||
property is not found default value depending on chip will be used.
|
||||
items:
|
||||
- const: 1
|
||||
- enum: [ 1, 3, 4, 6, 20, 8, 10 ]
|
||||
|
||||
required:
|
||||
- reg
|
||||
- io-channels
|
||||
|
||||
additionalProperties:
|
||||
false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
- "#thermal-sensor-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spmi_bus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
pm8150b_adc: adc@3100 {
|
||||
reg = <0x3100>;
|
||||
compatible = "qcom,spmi-adc5";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#io-channel-cells = <1>;
|
||||
|
||||
/* Other propreties are omitted */
|
||||
conn-therm@4f {
|
||||
reg = <ADC5_AMUX_THM3_100K_PU>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
};
|
||||
|
||||
pm8150b_adc_tm: adc-tm@3500 {
|
||||
compatible = "qcom,spmi-adc-tm5";
|
||||
reg = <0x3500>;
|
||||
interrupts = <0x2 0x35 0x0 IRQ_TYPE_EDGE_RISING>;
|
||||
#thermal-sensor-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
conn-therm@0 {
|
||||
reg = <0>;
|
||||
io-channels = <&pm8150b_adc ADC5_AMUX_THM3_100K_PU>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time-us = <200>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -1,17 +0,0 @@
|
||||
* Tango Thermal
|
||||
|
||||
The SMP8758 SoC includes 3 instances of this temperature sensor
|
||||
(in the CPU, video decoder, and PCIe controller).
|
||||
|
||||
Required properties:
|
||||
- #thermal-sensor-cells: Should be 0 (see Documentation/devicetree/bindings/thermal/thermal-sensor.yaml)
|
||||
- compatible: "sigma,smp8758-thermal"
|
||||
- reg: Address range of the thermal registers
|
||||
|
||||
Example:
|
||||
|
||||
cpu_temp: thermal@920100 {
|
||||
#thermal-sensor-cells = <0>;
|
||||
compatible = "sigma,smp8758-thermal";
|
||||
reg = <0x920100 12>;
|
||||
};
|
@ -1,116 +0,0 @@
|
||||
* ZTE zx2967 family Thermal
|
||||
|
||||
Required Properties:
|
||||
- compatible: should be one of the following.
|
||||
* zte,zx296718-thermal
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- clocks : Pairs of phandle and specifier referencing the controller's clocks.
|
||||
- clock-names: "topcrm" for the topcrm clock.
|
||||
"apb" for the apb clock.
|
||||
- #thermal-sensor-cells: must be 0.
|
||||
|
||||
Please note: slope coefficient defined in thermal-zones section need to be
|
||||
multiplied by 1000.
|
||||
|
||||
Example for tempsensor:
|
||||
|
||||
tempsensor: tempsensor@148a000 {
|
||||
compatible = "zte,zx296718-thermal";
|
||||
reg = <0x0148a000 0x20>;
|
||||
clocks = <&topcrm TEMPSENSOR_GATE>, <&audiocrm AUDIO_TS_PCLK>;
|
||||
clock-names = "topcrm", "apb";
|
||||
#thermal-sensor-cells = <0>;
|
||||
};
|
||||
|
||||
Example for cooling device:
|
||||
|
||||
cooling_dev: cooling_dev {
|
||||
cluster0_cooling_dev: cluster0-cooling-dev {
|
||||
#cooling-cells = <2>;
|
||||
cpumask = <0xf>;
|
||||
capacitance = <1500>;
|
||||
};
|
||||
|
||||
cluster1_cooling_dev: cluster1-cooling-dev {
|
||||
#cooling-cells = <2>;
|
||||
cpumask = <0x30>;
|
||||
capacitance = <2000>;
|
||||
};
|
||||
};
|
||||
|
||||
Example for thermal zones:
|
||||
|
||||
thermal-zones {
|
||||
zx296718_thermal: zx296718_thermal {
|
||||
polling-delay-passive = <500>;
|
||||
polling-delay = <1000>;
|
||||
sustainable-power = <6500>;
|
||||
|
||||
thermal-sensors = <&tempsensor 0>;
|
||||
/*
|
||||
* slope need to be multiplied by 1000.
|
||||
*/
|
||||
coefficients = <1951 (-922)>;
|
||||
|
||||
trips {
|
||||
trip0: switch_on_temperature {
|
||||
temperature = <90000>;
|
||||
hysteresis = <2000>;
|
||||
type = "passive";
|
||||
};
|
||||
|
||||
trip1: desired_temperature {
|
||||
temperature = <100000>;
|
||||
hysteresis = <2000>;
|
||||
type = "passive";
|
||||
};
|
||||
|
||||
crit: critical_temperature {
|
||||
temperature = <110000>;
|
||||
hysteresis = <2000>;
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
map0 {
|
||||
trip = <&trip0>;
|
||||
cooling-device = <&gpu 2 5>;
|
||||
};
|
||||
|
||||
map1 {
|
||||
trip = <&trip0>;
|
||||
cooling-device = <&cluster0_cooling_dev 1 2>;
|
||||
};
|
||||
|
||||
map2 {
|
||||
trip = <&trip1>;
|
||||
cooling-device = <&cluster0_cooling_dev 1 2>;
|
||||
};
|
||||
|
||||
map3 {
|
||||
trip = <&crit>;
|
||||
cooling-device = <&cluster0_cooling_dev 1 2>;
|
||||
};
|
||||
|
||||
map4 {
|
||||
trip = <&trip0>;
|
||||
cooling-device = <&cluster1_cooling_dev 1 2>;
|
||||
contribution = <9000>;
|
||||
};
|
||||
|
||||
map5 {
|
||||
trip = <&trip1>;
|
||||
cooling-device = <&cluster1_cooling_dev 1 2>;
|
||||
contribution = <4096>;
|
||||
};
|
||||
|
||||
map6 {
|
||||
trip = <&crit>;
|
||||
cooling-device = <&cluster1_cooling_dev 1 2>;
|
||||
contribution = <4096>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -520,19 +520,6 @@ available_policies
|
||||
|
||||
RW, Optional
|
||||
|
||||
passive
|
||||
Attribute is only present for zones in which the passive cooling
|
||||
policy is not supported by native thermal driver. Default is zero
|
||||
and can be set to a temperature (in millidegrees) to enable a
|
||||
passive trip point for the zone. Activation is done by polling with
|
||||
an interval of 1 second.
|
||||
|
||||
Unit: millidegrees Celsius
|
||||
|
||||
Valid values: 0 (disabled) or greater than 1000
|
||||
|
||||
RW, Optional
|
||||
|
||||
emul_temp
|
||||
Interface to set the emulated temperature method in thermal zone
|
||||
(sensor). After setting this temperature, the thermal zone may pass
|
||||
|
@ -670,27 +670,24 @@ static int thermal_get_trend(struct thermal_zone_device *thermal,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int thermal_notify(struct thermal_zone_device *thermal, int trip,
|
||||
enum thermal_trip_type trip_type)
|
||||
static void acpi_thermal_zone_device_hot(struct thermal_zone_device *thermal)
|
||||
{
|
||||
u8 type = 0;
|
||||
struct acpi_thermal *tz = thermal->devdata;
|
||||
|
||||
if (trip_type == THERMAL_TRIP_CRITICAL)
|
||||
type = ACPI_THERMAL_NOTIFY_CRITICAL;
|
||||
else if (trip_type == THERMAL_TRIP_HOT)
|
||||
type = ACPI_THERMAL_NOTIFY_HOT;
|
||||
else
|
||||
return 0;
|
||||
acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
|
||||
dev_name(&tz->device->dev),
|
||||
ACPI_THERMAL_NOTIFY_HOT, 1);
|
||||
}
|
||||
|
||||
static void acpi_thermal_zone_device_critical(struct thermal_zone_device *thermal)
|
||||
{
|
||||
struct acpi_thermal *tz = thermal->devdata;
|
||||
|
||||
acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
|
||||
dev_name(&tz->device->dev), type, 1);
|
||||
dev_name(&tz->device->dev),
|
||||
ACPI_THERMAL_NOTIFY_CRITICAL, 1);
|
||||
|
||||
if (trip_type == THERMAL_TRIP_CRITICAL && nocrt)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
thermal_zone_device_critical(thermal);
|
||||
}
|
||||
|
||||
static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
|
||||
@ -760,25 +757,6 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < tz->devices.count; i++) {
|
||||
handle = tz->devices.handles[i];
|
||||
status = acpi_bus_get_device(handle, &dev);
|
||||
if (ACPI_SUCCESS(status) && (dev == device)) {
|
||||
if (bind)
|
||||
result = thermal_zone_bind_cooling_device
|
||||
(thermal, THERMAL_TRIPS_NONE,
|
||||
cdev, THERMAL_NO_LIMIT,
|
||||
THERMAL_NO_LIMIT,
|
||||
THERMAL_WEIGHT_DEFAULT);
|
||||
else
|
||||
result = thermal_zone_unbind_cooling_device
|
||||
(thermal, THERMAL_TRIPS_NONE,
|
||||
cdev);
|
||||
if (result)
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
failed:
|
||||
return result;
|
||||
}
|
||||
@ -805,7 +783,8 @@ static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
|
||||
.get_trip_temp = thermal_get_trip_temp,
|
||||
.get_crit_temp = thermal_get_crit_temp,
|
||||
.get_trend = thermal_get_trend,
|
||||
.notify = thermal_notify,
|
||||
.hot = acpi_thermal_zone_device_hot,
|
||||
.critical = acpi_thermal_zone_device_critical,
|
||||
};
|
||||
|
||||
static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
|
||||
|
@ -458,7 +458,6 @@ static int pwm_fan_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
ctx->cdev = cdev;
|
||||
thermal_cdev_update(cdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -368,6 +368,28 @@ static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static s32 qcom_vadc_map_temp_voltage(const struct vadc_map_pt *pts,
|
||||
u32 tablesize, int input)
|
||||
{
|
||||
u32 i = 0;
|
||||
|
||||
/*
|
||||
* Table must be sorted, find the interval of 'y' which contains value
|
||||
* 'input' and map it to proper 'x' value
|
||||
*/
|
||||
while (i < tablesize && pts[i].y < input)
|
||||
i++;
|
||||
|
||||
if (i == 0)
|
||||
return pts[0].x;
|
||||
if (i == tablesize)
|
||||
return pts[tablesize - 1].x;
|
||||
|
||||
/* interpolate linearly */
|
||||
return fixp_linear_interpolate(pts[i - 1].y, pts[i - 1].x,
|
||||
pts[i].y, pts[i].x, input);
|
||||
}
|
||||
|
||||
static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
|
||||
u16 adc_code,
|
||||
bool absolute,
|
||||
@ -463,6 +485,21 @@ static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* convert voltage to ADC code, using 1.875V reference */
|
||||
static u16 qcom_vadc_scale_voltage_code(s32 voltage,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const u32 full_scale_code_volt,
|
||||
unsigned int factor)
|
||||
{
|
||||
s64 volt = voltage;
|
||||
s64 adc_vdd_ref_mv = 1875; /* reference voltage */
|
||||
|
||||
volt *= prescale->num * factor * full_scale_code_volt;
|
||||
volt = div64_s64(volt, (s64)prescale->den * adc_vdd_ref_mv * 1000);
|
||||
|
||||
return volt;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
@ -627,6 +664,19 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_vadc_scale);
|
||||
|
||||
u16 qcom_adc_tm5_temp_volt_scale(unsigned int prescale_ratio,
|
||||
u32 full_scale_code_volt, int temp)
|
||||
{
|
||||
const struct vadc_prescale_ratio *prescale = &adc5_prescale_ratios[prescale_ratio];
|
||||
s32 voltage;
|
||||
|
||||
voltage = qcom_vadc_map_temp_voltage(adcmap_100k_104ef_104fb_1875_vref,
|
||||
ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
|
||||
temp);
|
||||
return qcom_vadc_scale_voltage_code(voltage, prescale, full_scale_code_volt, 1000);
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_adc_tm5_temp_volt_scale);
|
||||
|
||||
int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
|
||||
unsigned int prescale_ratio,
|
||||
const struct adc5_data *data,
|
||||
|
@ -336,7 +336,8 @@ static void acerhdf_check_param(struct thermal_zone_device *thermal)
|
||||
pr_notice("interval changed to: %d\n", interval);
|
||||
|
||||
if (thermal)
|
||||
thermal->polling_delay = interval*1000;
|
||||
thermal->polling_delay_jiffies =
|
||||
round_jiffies(msecs_to_jiffies(interval * 1000));
|
||||
|
||||
prev_interval = interval;
|
||||
}
|
||||
|
@ -450,15 +450,6 @@ depends on (ARCH_STI || ARCH_STM32) && OF
|
||||
source "drivers/thermal/st/Kconfig"
|
||||
endmenu
|
||||
|
||||
config TANGO_THERMAL
|
||||
tristate "Tango thermal management"
|
||||
depends on ARCH_TANGO || COMPILE_TEST
|
||||
help
|
||||
Enable the Tango thermal driver, which supports the primitive
|
||||
temperature sensor embedded in Tango chips since the SMP8758.
|
||||
This sensor only generates a 1-bit signal to indicate whether
|
||||
the die temperature exceeds a programmable threshold.
|
||||
|
||||
source "drivers/thermal/tegra/Kconfig"
|
||||
|
||||
config GENERIC_ADC_THERMAL
|
||||
@ -476,14 +467,6 @@ depends on (ARCH_QCOM && OF) || COMPILE_TEST
|
||||
source "drivers/thermal/qcom/Kconfig"
|
||||
endmenu
|
||||
|
||||
config ZX2967_THERMAL
|
||||
tristate "Thermal sensors on zx2967 SoC"
|
||||
depends on ARCH_ZX || COMPILE_TEST
|
||||
help
|
||||
Enable the zx2967 thermal sensors driver, which supports
|
||||
the primitive temperature sensor embedded in zx2967 SoCs.
|
||||
This sensor generates the real time die temperature.
|
||||
|
||||
config UNIPHIER_THERMAL
|
||||
tristate "Socionext UniPhier thermal driver"
|
||||
depends on ARCH_UNIPHIER || COMPILE_TEST
|
||||
|
@ -42,7 +42,6 @@ obj-y += samsung/
|
||||
obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
|
||||
obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
|
||||
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
|
||||
obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
|
||||
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
|
||||
obj-$(CONFIG_IMX_SC_THERMAL) += imx_sc_thermal.o
|
||||
obj-$(CONFIG_IMX8MM_THERMAL) += imx8mm_thermal.o
|
||||
@ -57,7 +56,6 @@ obj-y += tegra/
|
||||
obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
|
||||
obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
|
||||
obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
|
||||
obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o
|
||||
obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
|
||||
obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o
|
||||
obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o
|
||||
|
@ -485,7 +485,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
frequency = get_state_freq(cpufreq_cdev, state);
|
||||
|
||||
ret = freq_qos_update_request(&cpufreq_cdev->qos_req, frequency);
|
||||
if (ret > 0) {
|
||||
if (ret >= 0) {
|
||||
cpufreq_cdev->cpufreq_state = state;
|
||||
cpus = cpufreq_cdev->policy->cpus;
|
||||
max_capacity = arch_scale_cpu_capacity(cpumask_first(cpus));
|
||||
|
@ -95,7 +95,7 @@ static void da9062_thermal_poll_on(struct work_struct *work)
|
||||
thermal_zone_device_update(thermal->zone,
|
||||
THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
delay = msecs_to_jiffies(thermal->zone->passive_delay);
|
||||
delay = thermal->zone->passive_delay_jiffies;
|
||||
queue_delayed_work(system_freezable_wq, &thermal->work, delay);
|
||||
return;
|
||||
}
|
||||
@ -245,7 +245,7 @@ static int da9062_thermal_probe(struct platform_device *pdev)
|
||||
|
||||
dev_dbg(&pdev->dev,
|
||||
"TJUNC temperature polling period set at %d ms\n",
|
||||
thermal->zone->passive_delay);
|
||||
jiffies_to_msecs(thermal->zone->passive_delay_jiffies));
|
||||
|
||||
ret = platform_get_irq_byname(pdev, "THERMAL");
|
||||
if (ret < 0) {
|
||||
|
@ -258,7 +258,7 @@ static u32 pid_controller(struct thermal_zone_device *tz,
|
||||
* power being applied, slowing down the controller)
|
||||
*/
|
||||
d = mul_frac(tz->tzp->k_d, err - params->prev_err);
|
||||
d = div_frac(d, tz->passive_delay);
|
||||
d = div_frac(d, jiffies_to_msecs(tz->passive_delay_jiffies));
|
||||
params->prev_err = err;
|
||||
|
||||
power_range = p + i + d;
|
||||
@ -589,6 +589,34 @@ static void allow_maximum_power(struct thermal_zone_device *tz)
|
||||
mutex_unlock(&tz->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* check_power_actors() - Check all cooling devices and warn when they are
|
||||
* not power actors
|
||||
* @tz: thermal zone to operate on
|
||||
*
|
||||
* Check all cooling devices in the @tz and warn every time they are missing
|
||||
* power actor API. The warning should help to investigate the issue, which
|
||||
* could be e.g. lack of Energy Model for a given device.
|
||||
*
|
||||
* Return: 0 on success, -EINVAL if any cooling device does not implement
|
||||
* the power actor API.
|
||||
*/
|
||||
static int check_power_actors(struct thermal_zone_device *tz)
|
||||
{
|
||||
struct thermal_instance *instance;
|
||||
int ret = 0;
|
||||
|
||||
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
|
||||
if (!cdev_is_power_actor(instance->cdev)) {
|
||||
dev_warn(&tz->device, "power_allocator: %s is not a power actor\n",
|
||||
instance->cdev->type);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* power_allocator_bind() - bind the power_allocator governor to a thermal zone
|
||||
* @tz: thermal zone to bind it to
|
||||
@ -596,7 +624,8 @@ static void allow_maximum_power(struct thermal_zone_device *tz)
|
||||
* Initialize the PID controller parameters and bind it to the thermal
|
||||
* zone.
|
||||
*
|
||||
* Return: 0 on success, or -ENOMEM if we ran out of memory.
|
||||
* Return: 0 on success, or -ENOMEM if we ran out of memory, or -EINVAL
|
||||
* when there are unsupported cooling devices in the @tz.
|
||||
*/
|
||||
static int power_allocator_bind(struct thermal_zone_device *tz)
|
||||
{
|
||||
@ -604,6 +633,10 @@ static int power_allocator_bind(struct thermal_zone_device *tz)
|
||||
struct power_allocator_params *params;
|
||||
int control_temp;
|
||||
|
||||
ret = check_power_actors(tz);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
params = kzalloc(sizeof(*params), GFP_KERNEL);
|
||||
if (!params)
|
||||
return -ENOMEM;
|
||||
|
@ -109,7 +109,7 @@ static void update_passive_instance(struct thermal_zone_device *tz,
|
||||
* If value is +1, activate a passive instance.
|
||||
* If value is -1, deactivate a passive instance.
|
||||
*/
|
||||
if (type == THERMAL_TRIP_PASSIVE || type == THERMAL_TRIPS_NONE)
|
||||
if (type == THERMAL_TRIP_PASSIVE)
|
||||
tz->passive += value;
|
||||
}
|
||||
|
||||
@ -122,13 +122,8 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
|
||||
bool throttle = false;
|
||||
int old_target;
|
||||
|
||||
if (trip == THERMAL_TRIPS_NONE) {
|
||||
trip_temp = tz->forced_passive;
|
||||
trip_type = THERMAL_TRIPS_NONE;
|
||||
} else {
|
||||
tz->ops->get_trip_temp(tz, trip, &trip_temp);
|
||||
tz->ops->get_trip_type(tz, trip, &trip_type);
|
||||
}
|
||||
tz->ops->get_trip_temp(tz, trip, &trip_temp);
|
||||
tz->ops->get_trip_type(tz, trip, &trip_type);
|
||||
|
||||
trend = get_tz_trend(tz, trip);
|
||||
|
||||
@ -189,9 +184,6 @@ static int step_wise_throttle(struct thermal_zone_device *tz, int trip)
|
||||
|
||||
thermal_zone_trip_update(tz, trip);
|
||||
|
||||
if (tz->forced_passive)
|
||||
thermal_zone_trip_update(tz, THERMAL_TRIPS_NONE);
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
list_for_each_entry(instance, &tz->thermal_instances, tz_node)
|
||||
|
@ -146,12 +146,18 @@ static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void int340x_thermal_critical(struct thermal_zone_device *zone)
|
||||
{
|
||||
dev_dbg(&zone->device, "%s: critical temperature reached\n", zone->type);
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
|
||||
.get_temp = int340x_thermal_get_zone_temp,
|
||||
.get_trip_temp = int340x_thermal_get_trip_temp,
|
||||
.get_trip_type = int340x_thermal_get_trip_type,
|
||||
.set_trip_temp = int340x_thermal_set_trip_temp,
|
||||
.get_trip_hyst = int340x_thermal_get_trip_hyst,
|
||||
.critical = int340x_thermal_critical,
|
||||
};
|
||||
|
||||
static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
|
||||
|
@ -326,10 +326,16 @@ static int pch_get_trip_temp(struct thermal_zone_device *tzd, int trip, int *tem
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pch_critical(struct thermal_zone_device *tzd)
|
||||
{
|
||||
dev_dbg(&tzd->device, "%s: critical temperature reached\n", tzd->type);
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops tzd_ops = {
|
||||
.get_temp = pch_thermal_get_temp,
|
||||
.get_trip_type = pch_get_trip_type,
|
||||
.get_trip_temp = pch_get_trip_temp,
|
||||
.critical = pch_critical,
|
||||
};
|
||||
|
||||
enum board_ids {
|
||||
|
@ -100,7 +100,6 @@ static int khadas_mcu_fan_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
ctx->cdev = cdev;
|
||||
thermal_cdev_update(cdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -10,6 +10,17 @@ config QCOM_TSENS
|
||||
Also able to set threshold temperature for both hot and cold and update
|
||||
when a threshold is reached.
|
||||
|
||||
config QCOM_SPMI_ADC_TM5
|
||||
tristate "Qualcomm SPMI PMIC Thermal Monitor ADC5"
|
||||
depends on OF && SPMI && IIO
|
||||
select REGMAP_SPMI
|
||||
select QCOM_VADC_COMMON
|
||||
help
|
||||
This enables the thermal driver for the ADC 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
|
||||
|
@ -3,4 +3,5 @@ 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_TEMP_ALARM) += qcom-spmi-temp-alarm.o
|
||||
|
623
drivers/thermal/qcom/qcom-spmi-adc-tm5.c
Normal file
623
drivers/thermal/qcom/qcom-spmi-adc-tm5.c
Normal file
@ -0,0 +1,623 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020 Linaro Limited
|
||||
*
|
||||
* Based on original driver:
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/iio/adc/qcom-vadc-common.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
/*
|
||||
* Thermal monitoring block consists of 8 (ADC_TM5_NUM_CHANNELS) channels. Each
|
||||
* channel is programmed to use one of ADC channels for voltage comparison.
|
||||
* Voltages are programmed using ADC codes, so we have to convert temp to
|
||||
* voltage and then to ADC code value.
|
||||
*
|
||||
* Configuration of TM channels must match configuration of corresponding ADC
|
||||
* channels.
|
||||
*/
|
||||
|
||||
#define ADC5_MAX_CHANNEL 0xc0
|
||||
#define ADC_TM5_NUM_CHANNELS 8
|
||||
|
||||
#define ADC_TM5_STATUS_LOW 0x0a
|
||||
|
||||
#define ADC_TM5_STATUS_HIGH 0x0b
|
||||
|
||||
#define ADC_TM5_NUM_BTM 0x0f
|
||||
|
||||
#define ADC_TM5_ADC_DIG_PARAM 0x42
|
||||
|
||||
#define ADC_TM5_FAST_AVG_CTL (ADC_TM5_ADC_DIG_PARAM + 1)
|
||||
#define ADC_TM5_FAST_AVG_EN BIT(7)
|
||||
|
||||
#define ADC_TM5_MEAS_INTERVAL_CTL (ADC_TM5_ADC_DIG_PARAM + 2)
|
||||
#define ADC_TM5_TIMER1 3 /* 3.9ms */
|
||||
|
||||
#define ADC_TM5_MEAS_INTERVAL_CTL2 (ADC_TM5_ADC_DIG_PARAM + 3)
|
||||
#define ADC_TM5_MEAS_INTERVAL_CTL2_MASK 0xf0
|
||||
#define ADC_TM5_TIMER2 10 /* 1 second */
|
||||
#define ADC_TM5_MEAS_INTERVAL_CTL3_MASK 0xf
|
||||
#define ADC_TM5_TIMER3 4 /* 4 second */
|
||||
|
||||
#define ADC_TM_EN_CTL1 0x46
|
||||
#define ADC_TM_EN BIT(7)
|
||||
#define ADC_TM_CONV_REQ 0x47
|
||||
#define ADC_TM_CONV_REQ_EN BIT(7)
|
||||
|
||||
#define ADC_TM5_M_CHAN_BASE 0x60
|
||||
|
||||
#define ADC_TM5_M_ADC_CH_SEL_CTL(n) (ADC_TM5_M_CHAN_BASE + ((n) * 8) + 0)
|
||||
#define ADC_TM5_M_LOW_THR0(n) (ADC_TM5_M_CHAN_BASE + ((n) * 8) + 1)
|
||||
#define ADC_TM5_M_LOW_THR1(n) (ADC_TM5_M_CHAN_BASE + ((n) * 8) + 2)
|
||||
#define ADC_TM5_M_HIGH_THR0(n) (ADC_TM5_M_CHAN_BASE + ((n) * 8) + 3)
|
||||
#define ADC_TM5_M_HIGH_THR1(n) (ADC_TM5_M_CHAN_BASE + ((n) * 8) + 4)
|
||||
#define ADC_TM5_M_MEAS_INTERVAL_CTL(n) (ADC_TM5_M_CHAN_BASE + ((n) * 8) + 5)
|
||||
#define ADC_TM5_M_CTL(n) (ADC_TM5_M_CHAN_BASE + ((n) * 8) + 6)
|
||||
#define ADC_TM5_M_CTL_HW_SETTLE_DELAY_MASK 0xf
|
||||
#define ADC_TM5_M_CTL_CAL_SEL_MASK 0x30
|
||||
#define ADC_TM5_M_CTL_CAL_VAL 0x40
|
||||
#define ADC_TM5_M_EN(n) (ADC_TM5_M_CHAN_BASE + ((n) * 8) + 7)
|
||||
#define ADC_TM5_M_MEAS_EN BIT(7)
|
||||
#define ADC_TM5_M_HIGH_THR_INT_EN BIT(1)
|
||||
#define ADC_TM5_M_LOW_THR_INT_EN BIT(0)
|
||||
|
||||
enum adc5_timer_select {
|
||||
ADC5_TIMER_SEL_1 = 0,
|
||||
ADC5_TIMER_SEL_2,
|
||||
ADC5_TIMER_SEL_3,
|
||||
ADC5_TIMER_SEL_NONE,
|
||||
};
|
||||
|
||||
struct adc_tm5_data {
|
||||
const u32 full_scale_code_volt;
|
||||
unsigned int *decimation;
|
||||
unsigned int *hw_settle;
|
||||
};
|
||||
|
||||
enum adc_tm5_cal_method {
|
||||
ADC_TM5_NO_CAL = 0,
|
||||
ADC_TM5_RATIOMETRIC_CAL,
|
||||
ADC_TM5_ABSOLUTE_CAL
|
||||
};
|
||||
|
||||
struct adc_tm5_chip;
|
||||
|
||||
/**
|
||||
* struct adc_tm5_channel - ADC Thermal Monitoring channel data.
|
||||
* @channel: channel number.
|
||||
* @adc_channel: corresponding ADC channel number.
|
||||
* @cal_method: calibration method.
|
||||
* @prescale: channel scaling performed on the input signal.
|
||||
* @hw_settle_time: the time between AMUX being configured and the
|
||||
* start of conversion.
|
||||
* @iio: IIO channel instance used by this channel.
|
||||
* @chip: ADC TM chip instance.
|
||||
* @tzd: thermal zone device used by this channel.
|
||||
*/
|
||||
struct adc_tm5_channel {
|
||||
unsigned int channel;
|
||||
unsigned int adc_channel;
|
||||
enum adc_tm5_cal_method cal_method;
|
||||
unsigned int prescale;
|
||||
unsigned int hw_settle_time;
|
||||
struct iio_channel *iio;
|
||||
struct adc_tm5_chip *chip;
|
||||
struct thermal_zone_device *tzd;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adc_tm5_chip - ADC Thermal Monitoring properties
|
||||
* @regmap: SPMI ADC5 Thermal Monitoring peripheral register map field.
|
||||
* @dev: SPMI ADC5 device.
|
||||
* @data: software configuration data.
|
||||
* @channels: array of ADC TM channel data.
|
||||
* @nchannels: amount of channels defined/allocated
|
||||
* @decimation: sampling rate supported for the channel.
|
||||
* @avg_samples: ability to provide single result from the ADC
|
||||
* that is an average of multiple measurements.
|
||||
* @base: base address of TM registers.
|
||||
*/
|
||||
struct adc_tm5_chip {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
const struct adc_tm5_data *data;
|
||||
struct adc_tm5_channel *channels;
|
||||
unsigned int nchannels;
|
||||
unsigned int decimation;
|
||||
unsigned int avg_samples;
|
||||
u16 base;
|
||||
};
|
||||
|
||||
static const struct adc_tm5_data adc_tm5_data_pmic = {
|
||||
.full_scale_code_volt = 0x70e4,
|
||||
.decimation = (unsigned int []) { 250, 420, 840 },
|
||||
.hw_settle = (unsigned int []) { 15, 100, 200, 300, 400, 500, 600, 700,
|
||||
1000, 2000, 4000, 8000, 16000, 32000,
|
||||
64000, 128000 },
|
||||
};
|
||||
|
||||
static int adc_tm5_read(struct adc_tm5_chip *adc_tm, u16 offset, u8 *data, int len)
|
||||
{
|
||||
return regmap_bulk_read(adc_tm->regmap, adc_tm->base + offset, data, len);
|
||||
}
|
||||
|
||||
static int adc_tm5_write(struct adc_tm5_chip *adc_tm, u16 offset, u8 *data, int len)
|
||||
{
|
||||
return regmap_bulk_write(adc_tm->regmap, adc_tm->base + offset, data, len);
|
||||
}
|
||||
|
||||
static int adc_tm5_reg_update(struct adc_tm5_chip *adc_tm, u16 offset, u8 mask, u8 val)
|
||||
{
|
||||
return regmap_write_bits(adc_tm->regmap, adc_tm->base + offset, mask, val);
|
||||
}
|
||||
|
||||
static irqreturn_t adc_tm5_isr(int irq, void *data)
|
||||
{
|
||||
struct adc_tm5_chip *chip = data;
|
||||
u8 status_low, status_high, ctl;
|
||||
int ret, i;
|
||||
|
||||
ret = adc_tm5_read(chip, ADC_TM5_STATUS_LOW, &status_low, sizeof(status_low));
|
||||
if (unlikely(ret)) {
|
||||
dev_err(chip->dev, "read status low failed: %d\n", ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
ret = adc_tm5_read(chip, ADC_TM5_STATUS_HIGH, &status_high, sizeof(status_high));
|
||||
if (unlikely(ret)) {
|
||||
dev_err(chip->dev, "read status high failed: %d\n", ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
for (i = 0; i < chip->nchannels; i++) {
|
||||
bool upper_set = false, lower_set = false;
|
||||
unsigned int ch = chip->channels[i].channel;
|
||||
|
||||
/* No TZD, we warned at the boot time */
|
||||
if (!chip->channels[i].tzd)
|
||||
continue;
|
||||
|
||||
ret = adc_tm5_read(chip, ADC_TM5_M_EN(ch), &ctl, sizeof(ctl));
|
||||
if (unlikely(ret)) {
|
||||
dev_err(chip->dev, "ctl read failed: %d, channel %d\n", ret, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(ctl & ADC_TM5_M_MEAS_EN))
|
||||
continue;
|
||||
|
||||
lower_set = (status_low & BIT(ch)) &&
|
||||
(ctl & ADC_TM5_M_LOW_THR_INT_EN);
|
||||
|
||||
upper_set = (status_high & BIT(ch)) &&
|
||||
(ctl & ADC_TM5_M_HIGH_THR_INT_EN);
|
||||
|
||||
if (upper_set || lower_set)
|
||||
thermal_zone_device_update(chip->channels[i].tzd,
|
||||
THERMAL_EVENT_UNSPECIFIED);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int adc_tm5_get_temp(void *data, int *temp)
|
||||
{
|
||||
struct adc_tm5_channel *channel = data;
|
||||
int ret;
|
||||
|
||||
if (!channel || !channel->iio)
|
||||
return -EINVAL;
|
||||
|
||||
ret = iio_read_channel_processed(channel->iio, temp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret != IIO_VAL_INT)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_tm5_disable_channel(struct adc_tm5_channel *channel)
|
||||
{
|
||||
struct adc_tm5_chip *chip = channel->chip;
|
||||
unsigned int reg = ADC_TM5_M_EN(channel->channel);
|
||||
|
||||
return adc_tm5_reg_update(chip, reg,
|
||||
ADC_TM5_M_MEAS_EN |
|
||||
ADC_TM5_M_HIGH_THR_INT_EN |
|
||||
ADC_TM5_M_LOW_THR_INT_EN,
|
||||
0);
|
||||
}
|
||||
|
||||
static int adc_tm5_enable(struct adc_tm5_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
u8 data;
|
||||
|
||||
data = ADC_TM_EN;
|
||||
ret = adc_tm5_write(chip, ADC_TM_EN_CTL1, &data, sizeof(data));
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "adc-tm enable failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data = ADC_TM_CONV_REQ_EN;
|
||||
ret = adc_tm5_write(chip, ADC_TM_CONV_REQ, &data, sizeof(data));
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "adc-tm request conversion failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_tm5_configure(struct adc_tm5_channel *channel, int low, int high)
|
||||
{
|
||||
struct adc_tm5_chip *chip = channel->chip;
|
||||
u8 buf[8];
|
||||
u16 reg = ADC_TM5_M_ADC_CH_SEL_CTL(channel->channel);
|
||||
int ret;
|
||||
|
||||
ret = adc_tm5_read(chip, reg, buf, sizeof(buf));
|
||||
if (ret) {
|
||||
dev_err(chip->dev, "channel %d params read failed: %d\n", channel->channel, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
buf[0] = channel->adc_channel;
|
||||
|
||||
/* High temperature corresponds to low voltage threshold */
|
||||
if (high != INT_MAX) {
|
||||
u16 adc_code = qcom_adc_tm5_temp_volt_scale(channel->prescale,
|
||||
chip->data->full_scale_code_volt, high);
|
||||
|
||||
buf[1] = adc_code & 0xff;
|
||||
buf[2] = adc_code >> 8;
|
||||
buf[7] |= ADC_TM5_M_LOW_THR_INT_EN;
|
||||
} else {
|
||||
buf[7] &= ~ADC_TM5_M_LOW_THR_INT_EN;
|
||||
}
|
||||
|
||||
/* Low temperature corresponds to high voltage threshold */
|
||||
if (low != -INT_MAX) {
|
||||
u16 adc_code = qcom_adc_tm5_temp_volt_scale(channel->prescale,
|
||||
chip->data->full_scale_code_volt, low);
|
||||
|
||||
buf[3] = adc_code & 0xff;
|
||||
buf[4] = adc_code >> 8;
|
||||
buf[7] |= ADC_TM5_M_HIGH_THR_INT_EN;
|
||||
} else {
|
||||
buf[7] &= ~ADC_TM5_M_HIGH_THR_INT_EN;
|
||||
}
|
||||
|
||||
buf[5] = ADC5_TIMER_SEL_2;
|
||||
|
||||
/* Set calibration select, hw_settle delay */
|
||||
buf[6] &= ~ADC_TM5_M_CTL_HW_SETTLE_DELAY_MASK;
|
||||
buf[6] |= FIELD_PREP(ADC_TM5_M_CTL_HW_SETTLE_DELAY_MASK, channel->hw_settle_time);
|
||||
buf[6] &= ~ADC_TM5_M_CTL_CAL_SEL_MASK;
|
||||
buf[6] |= FIELD_PREP(ADC_TM5_M_CTL_CAL_SEL_MASK, channel->cal_method);
|
||||
|
||||
buf[7] |= ADC_TM5_M_MEAS_EN;
|
||||
|
||||
ret = adc_tm5_write(chip, reg, buf, sizeof(buf));
|
||||
if (ret) {
|
||||
dev_err(chip->dev, "channel %d params write failed: %d\n", channel->channel, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return adc_tm5_enable(chip);
|
||||
}
|
||||
|
||||
static int adc_tm5_set_trips(void *data, int low, int high)
|
||||
{
|
||||
struct adc_tm5_channel *channel = data;
|
||||
struct adc_tm5_chip *chip;
|
||||
int ret;
|
||||
|
||||
if (!channel)
|
||||
return -EINVAL;
|
||||
|
||||
chip = channel->chip;
|
||||
dev_dbg(chip->dev, "%d:low(mdegC):%d, high(mdegC):%d\n",
|
||||
channel->channel, low, high);
|
||||
|
||||
if (high == INT_MAX && low <= -INT_MAX)
|
||||
ret = adc_tm5_disable_channel(channel);
|
||||
else
|
||||
ret = adc_tm5_configure(channel, low, high);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct thermal_zone_of_device_ops adc_tm5_ops = {
|
||||
.get_temp = adc_tm5_get_temp,
|
||||
.set_trips = adc_tm5_set_trips,
|
||||
};
|
||||
|
||||
static int adc_tm5_register_tzd(struct adc_tm5_chip *adc_tm)
|
||||
{
|
||||
unsigned int i;
|
||||
struct thermal_zone_device *tzd;
|
||||
|
||||
for (i = 0; i < adc_tm->nchannels; i++) {
|
||||
adc_tm->channels[i].chip = adc_tm;
|
||||
|
||||
tzd = devm_thermal_zone_of_sensor_register(adc_tm->dev,
|
||||
adc_tm->channels[i].channel,
|
||||
&adc_tm->channels[i],
|
||||
&adc_tm5_ops);
|
||||
if (IS_ERR(tzd)) {
|
||||
dev_err(adc_tm->dev, "Error registering TZ zone for channel %d: %ld\n",
|
||||
adc_tm->channels[i].channel, PTR_ERR(tzd));
|
||||
return PTR_ERR(tzd);
|
||||
}
|
||||
adc_tm->channels[i].tzd = tzd;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_tm5_init(struct adc_tm5_chip *chip)
|
||||
{
|
||||
u8 buf[4], channels_available;
|
||||
int ret;
|
||||
unsigned int i;
|
||||
|
||||
ret = adc_tm5_read(chip, ADC_TM5_NUM_BTM,
|
||||
&channels_available, sizeof(channels_available));
|
||||
if (ret) {
|
||||
dev_err(chip->dev, "read failed for BTM channels\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < chip->nchannels; i++) {
|
||||
if (chip->channels[i].channel >= channels_available) {
|
||||
dev_err(chip->dev, "Invalid channel %d\n", chip->channels[i].channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
buf[0] = chip->decimation;
|
||||
buf[1] = chip->avg_samples | ADC_TM5_FAST_AVG_EN;
|
||||
buf[2] = ADC_TM5_TIMER1;
|
||||
buf[3] = FIELD_PREP(ADC_TM5_MEAS_INTERVAL_CTL2_MASK, ADC_TM5_TIMER2) |
|
||||
FIELD_PREP(ADC_TM5_MEAS_INTERVAL_CTL3_MASK, ADC_TM5_TIMER3);
|
||||
|
||||
ret = adc_tm5_write(chip, ADC_TM5_ADC_DIG_PARAM, buf, sizeof(buf));
|
||||
if (ret) {
|
||||
dev_err(chip->dev, "block write failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc_tm5_get_dt_channel_data(struct adc_tm5_chip *adc_tm,
|
||||
struct adc_tm5_channel *channel,
|
||||
struct device_node *node)
|
||||
{
|
||||
const char *name = node->name;
|
||||
u32 chan, value, varr[2];
|
||||
int ret;
|
||||
struct device *dev = adc_tm->dev;
|
||||
struct of_phandle_args args;
|
||||
|
||||
ret = of_property_read_u32(node, "reg", &chan);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: invalid channel number %d\n", name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (chan >= ADC_TM5_NUM_CHANNELS) {
|
||||
dev_err(dev, "%s: channel number too big: %d\n", name, chan);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
channel->channel = chan;
|
||||
|
||||
/*
|
||||
* We are tied to PMIC's ADC controller, which always use single
|
||||
* argument for channel number. So don't bother parsing
|
||||
* #io-channel-cells, just enforce cell_count = 1.
|
||||
*/
|
||||
ret = of_parse_phandle_with_fixed_args(node, "io-channels", 1, 0, &args);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s: error parsing ADC channel number %d: %d\n", name, chan, ret);
|
||||
return ret;
|
||||
}
|
||||
of_node_put(args.np);
|
||||
|
||||
if (args.args_count != 1 || args.args[0] >= ADC5_MAX_CHANNEL) {
|
||||
dev_err(dev, "%s: invalid ADC channel number %d\n", name, chan);
|
||||
return ret;
|
||||
}
|
||||
channel->adc_channel = args.args[0];
|
||||
|
||||
channel->iio = devm_of_iio_channel_get_by_name(adc_tm->dev, node, NULL);
|
||||
if (IS_ERR(channel->iio)) {
|
||||
ret = PTR_ERR(channel->iio);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "%s: error getting channel: %d\n", name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2);
|
||||
if (!ret) {
|
||||
ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s: invalid pre-scaling <%d %d>\n",
|
||||
name, varr[0], varr[1]);
|
||||
return ret;
|
||||
}
|
||||
channel->prescale = ret;
|
||||
} else {
|
||||
/* 1:1 prescale is index 0 */
|
||||
channel->prescale = 0;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,hw-settle-time-us", &value);
|
||||
if (!ret) {
|
||||
ret = qcom_adc5_hw_settle_time_from_dt(value, adc_tm->data->hw_settle);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s invalid hw-settle-time-us %d us\n",
|
||||
name, value);
|
||||
return ret;
|
||||
}
|
||||
channel->hw_settle_time = ret;
|
||||
} else {
|
||||
channel->hw_settle_time = VADC_DEF_HW_SETTLE_TIME;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(node, "qcom,ratiometric"))
|
||||
channel->cal_method = ADC_TM5_RATIOMETRIC_CAL;
|
||||
else
|
||||
channel->cal_method = ADC_TM5_ABSOLUTE_CAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_tm5_get_dt_data(struct adc_tm5_chip *adc_tm, struct device_node *node)
|
||||
{
|
||||
struct adc_tm5_channel *channels;
|
||||
struct device_node *child;
|
||||
u32 value;
|
||||
int ret;
|
||||
struct device *dev = adc_tm->dev;
|
||||
|
||||
adc_tm->nchannels = of_get_available_child_count(node);
|
||||
if (!adc_tm->nchannels)
|
||||
return -EINVAL;
|
||||
|
||||
adc_tm->channels = devm_kcalloc(dev, adc_tm->nchannels,
|
||||
sizeof(*adc_tm->channels), GFP_KERNEL);
|
||||
if (!adc_tm->channels)
|
||||
return -ENOMEM;
|
||||
|
||||
channels = adc_tm->channels;
|
||||
|
||||
adc_tm->data = of_device_get_match_data(dev);
|
||||
if (!adc_tm->data)
|
||||
adc_tm->data = &adc_tm5_data_pmic;
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,decimation", &value);
|
||||
if (!ret) {
|
||||
ret = qcom_adc5_decimation_from_dt(value, adc_tm->data->decimation);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "invalid decimation %d\n", value);
|
||||
return ret;
|
||||
}
|
||||
adc_tm->decimation = ret;
|
||||
} else {
|
||||
adc_tm->decimation = ADC5_DECIMATION_DEFAULT;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,avg-samples", &value);
|
||||
if (!ret) {
|
||||
ret = qcom_adc5_avg_samples_from_dt(value);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "invalid avg-samples %d\n", value);
|
||||
return ret;
|
||||
}
|
||||
adc_tm->avg_samples = ret;
|
||||
} else {
|
||||
adc_tm->avg_samples = VADC_DEF_AVG_SAMPLES;
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(node, child) {
|
||||
ret = adc_tm5_get_dt_channel_data(adc_tm, channels, child);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
channels++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_tm5_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct adc_tm5_chip *adc_tm;
|
||||
struct regmap *regmap;
|
||||
int ret, irq;
|
||||
u32 reg;
|
||||
|
||||
regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!regmap)
|
||||
return -ENODEV;
|
||||
|
||||
ret = of_property_read_u32(node, "reg", ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adc_tm = devm_kzalloc(&pdev->dev, sizeof(*adc_tm), GFP_KERNEL);
|
||||
if (!adc_tm)
|
||||
return -ENOMEM;
|
||||
|
||||
adc_tm->regmap = regmap;
|
||||
adc_tm->dev = dev;
|
||||
adc_tm->base = reg;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "get_irq failed: %d\n", irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = adc_tm5_get_dt_data(adc_tm, node);
|
||||
if (ret) {
|
||||
dev_err(dev, "get dt data failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = adc_tm5_init(adc_tm);
|
||||
if (ret) {
|
||||
dev_err(dev, "adc-tm init failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = adc_tm5_register_tzd(adc_tm);
|
||||
if (ret) {
|
||||
dev_err(dev, "tzd register failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_request_threaded_irq(dev, irq, NULL, adc_tm5_isr,
|
||||
IRQF_ONESHOT, "pm-adc-tm5", adc_tm);
|
||||
}
|
||||
|
||||
static const struct of_device_id adc_tm5_match_table[] = {
|
||||
{
|
||||
.compatible = "qcom,spmi-adc-tm5",
|
||||
.data = &adc_tm5_data_pmic,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adc_tm5_match_table);
|
||||
|
||||
static struct platform_driver adc_tm5_driver = {
|
||||
.driver = {
|
||||
.name = "qcom-spmi-adc-tm5",
|
||||
.of_match_table = adc_tm5_match_table,
|
||||
},
|
||||
.probe = adc_tm5_probe,
|
||||
};
|
||||
module_platform_driver(adc_tm5_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SPMI PMIC Thermal Monitor ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1,126 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/*
|
||||
* According to a data sheet draft, "this temperature sensor uses a bandgap
|
||||
* type of circuit to compare a voltage which has a negative temperature
|
||||
* coefficient with a voltage that is proportional to absolute temperature.
|
||||
* A resistor bank allows 41 different temperature thresholds to be selected
|
||||
* and the logic output will then indicate whether the actual die temperature
|
||||
* lies above or below the selected threshold."
|
||||
*/
|
||||
|
||||
#define TEMPSI_CMD 0
|
||||
#define TEMPSI_RES 4
|
||||
#define TEMPSI_CFG 8
|
||||
|
||||
#define CMD_OFF 0
|
||||
#define CMD_ON 1
|
||||
#define CMD_READ 2
|
||||
|
||||
#define IDX_MIN 15
|
||||
#define IDX_MAX 40
|
||||
|
||||
struct tango_thermal_priv {
|
||||
void __iomem *base;
|
||||
int thresh_idx;
|
||||
};
|
||||
|
||||
static bool temp_above_thresh(void __iomem *base, int thresh_idx)
|
||||
{
|
||||
writel(CMD_READ | thresh_idx << 8, base + TEMPSI_CMD);
|
||||
usleep_range(10, 20);
|
||||
writel(CMD_READ | thresh_idx << 8, base + TEMPSI_CMD);
|
||||
|
||||
return readl(base + TEMPSI_RES);
|
||||
}
|
||||
|
||||
static int tango_get_temp(void *arg, int *res)
|
||||
{
|
||||
struct tango_thermal_priv *priv = arg;
|
||||
int idx = priv->thresh_idx;
|
||||
|
||||
if (temp_above_thresh(priv->base, idx)) {
|
||||
/* Search upward by incrementing thresh_idx */
|
||||
while (idx < IDX_MAX && temp_above_thresh(priv->base, ++idx))
|
||||
cpu_relax();
|
||||
idx = idx - 1; /* always return lower bound */
|
||||
} else {
|
||||
/* Search downward by decrementing thresh_idx */
|
||||
while (idx > IDX_MIN && !temp_above_thresh(priv->base, --idx))
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
*res = (idx * 9 / 2 - 38) * 1000; /* millidegrees Celsius */
|
||||
priv->thresh_idx = idx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_of_device_ops ops = {
|
||||
.get_temp = tango_get_temp,
|
||||
};
|
||||
|
||||
static void tango_thermal_init(struct tango_thermal_priv *priv)
|
||||
{
|
||||
writel(0, priv->base + TEMPSI_CFG);
|
||||
writel(CMD_ON, priv->base + TEMPSI_CMD);
|
||||
}
|
||||
|
||||
static int tango_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct tango_thermal_priv *priv;
|
||||
struct thermal_zone_device *tzdev;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
priv->thresh_idx = IDX_MIN;
|
||||
tango_thermal_init(priv);
|
||||
|
||||
tzdev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv, &ops);
|
||||
return PTR_ERR_OR_ZERO(tzdev);
|
||||
}
|
||||
|
||||
static int __maybe_unused tango_thermal_resume(struct device *dev)
|
||||
{
|
||||
tango_thermal_init(dev_get_drvdata(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tango_thermal_pm, NULL, tango_thermal_resume);
|
||||
|
||||
static const struct of_device_id tango_sensor_ids[] = {
|
||||
{
|
||||
.compatible = "sigma,smp8758-thermal",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tango_sensor_ids);
|
||||
|
||||
static struct platform_driver tango_thermal_driver = {
|
||||
.probe = tango_thermal_probe,
|
||||
.driver = {
|
||||
.name = "tango-thermal",
|
||||
.of_match_table = tango_sensor_ids,
|
||||
.pm = &tango_thermal_pm,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(tango_thermal_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Sigma Designs");
|
||||
MODULE_DESCRIPTION("Tango temperature sensor");
|
@ -289,16 +289,11 @@ static int __init thermal_register_governors(void)
|
||||
* - Critical trip point will cause a system shutdown.
|
||||
*/
|
||||
static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
|
||||
int delay)
|
||||
unsigned long delay)
|
||||
{
|
||||
if (delay > 1000)
|
||||
if (delay)
|
||||
mod_delayed_work(system_freezable_power_efficient_wq,
|
||||
&tz->poll_queue,
|
||||
round_jiffies(msecs_to_jiffies(delay)));
|
||||
else if (delay)
|
||||
mod_delayed_work(system_freezable_power_efficient_wq,
|
||||
&tz->poll_queue,
|
||||
msecs_to_jiffies(delay));
|
||||
&tz->poll_queue, delay);
|
||||
else
|
||||
cancel_delayed_work(&tz->poll_queue);
|
||||
}
|
||||
@ -317,9 +312,9 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz)
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (!stop && tz->passive)
|
||||
thermal_zone_device_set_polling(tz, tz->passive_delay);
|
||||
else if (!stop && tz->polling_delay)
|
||||
thermal_zone_device_set_polling(tz, tz->polling_delay);
|
||||
thermal_zone_device_set_polling(tz, tz->passive_delay_jiffies);
|
||||
else if (!stop && tz->polling_delay_jiffies)
|
||||
thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies);
|
||||
else
|
||||
thermal_zone_device_set_polling(tz, 0);
|
||||
|
||||
@ -412,9 +407,6 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
|
||||
|
||||
trace_thermal_zone_trip(tz, trip, trip_type);
|
||||
|
||||
if (tz->ops->notify)
|
||||
tz->ops->notify(tz, trip, trip_type);
|
||||
|
||||
if (trip_type == THERMAL_TRIP_HOT && tz->ops->hot)
|
||||
tz->ops->hot(tz);
|
||||
else if (trip_type == THERMAL_TRIP_CRITICAL)
|
||||
@ -486,12 +478,6 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz)
|
||||
pos->initialized = false;
|
||||
}
|
||||
|
||||
static void thermal_zone_device_reset(struct thermal_zone_device *tz)
|
||||
{
|
||||
tz->passive = 0;
|
||||
thermal_zone_device_init(tz);
|
||||
}
|
||||
|
||||
static int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
@ -601,26 +587,6 @@ static void thermal_zone_device_check(struct work_struct *work)
|
||||
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
}
|
||||
|
||||
void thermal_zone_device_rebind_exception(struct thermal_zone_device *tz,
|
||||
const char *cdev_type, size_t size)
|
||||
{
|
||||
struct thermal_cooling_device *cdev = NULL;
|
||||
|
||||
mutex_lock(&thermal_list_lock);
|
||||
list_for_each_entry(cdev, &thermal_cdev_list, node) {
|
||||
/* skip non matching cdevs */
|
||||
if (strncmp(cdev_type, cdev->type, size))
|
||||
continue;
|
||||
|
||||
/* re binding the exception matching the type pattern */
|
||||
thermal_zone_bind_cooling_device(tz, THERMAL_TRIPS_NONE, cdev,
|
||||
THERMAL_NO_LIMIT,
|
||||
THERMAL_NO_LIMIT,
|
||||
THERMAL_WEIGHT_DEFAULT);
|
||||
}
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
}
|
||||
|
||||
int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *),
|
||||
void *data)
|
||||
{
|
||||
@ -688,23 +654,6 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id)
|
||||
return match;
|
||||
}
|
||||
|
||||
void thermal_zone_device_unbind_exception(struct thermal_zone_device *tz,
|
||||
const char *cdev_type, size_t size)
|
||||
{
|
||||
struct thermal_cooling_device *cdev = NULL;
|
||||
|
||||
mutex_lock(&thermal_list_lock);
|
||||
list_for_each_entry(cdev, &thermal_cdev_list, node) {
|
||||
/* skip non matching cdevs */
|
||||
if (strncmp(cdev_type, cdev->type, size))
|
||||
continue;
|
||||
/* unbinding the exception matching the type pattern */
|
||||
thermal_zone_unbind_cooling_device(tz, THERMAL_TRIPS_NONE,
|
||||
cdev);
|
||||
}
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Device management section: cooling devices, zones devices, and binding
|
||||
*
|
||||
@ -750,7 +699,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
|
||||
unsigned long max_state;
|
||||
int result, ret;
|
||||
|
||||
if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
|
||||
if (trip >= tz->trips || trip < 0)
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry(pos1, &thermal_tz_list, node) {
|
||||
@ -1352,8 +1301,9 @@ thermal_zone_device_register(const char *type, int trips, int mask,
|
||||
tz->device.class = &thermal_class;
|
||||
tz->devdata = devdata;
|
||||
tz->trips = trips;
|
||||
tz->passive_delay = passive_delay;
|
||||
tz->polling_delay = polling_delay;
|
||||
|
||||
thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay);
|
||||
thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay);
|
||||
|
||||
/* sys I/F */
|
||||
/* Add nodes that are always present via .groups */
|
||||
@ -1407,7 +1357,7 @@ thermal_zone_device_register(const char *type, int trips, int mask,
|
||||
|
||||
INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check);
|
||||
|
||||
thermal_zone_device_reset(tz);
|
||||
thermal_zone_device_init(tz);
|
||||
/* Update the new thermal zone and mark it as already updated. */
|
||||
if (atomic_cmpxchg(&tz->need_update, 1, 0))
|
||||
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
@ -65,6 +65,8 @@ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
|
||||
cdev->ops->power2state;
|
||||
}
|
||||
|
||||
void thermal_cdev_update(struct thermal_cooling_device *);
|
||||
|
||||
/**
|
||||
* struct thermal_trip - representation of a point in temperature domain
|
||||
* @np: pointer to struct device_node that this trip point was created from
|
||||
@ -118,15 +120,12 @@ struct thermal_instance {
|
||||
|
||||
int thermal_register_governor(struct thermal_governor *);
|
||||
void thermal_unregister_governor(struct thermal_governor *);
|
||||
void thermal_zone_device_rebind_exception(struct thermal_zone_device *,
|
||||
const char *, size_t);
|
||||
void thermal_zone_device_unbind_exception(struct thermal_zone_device *,
|
||||
const char *, size_t);
|
||||
int thermal_zone_device_set_policy(struct thermal_zone_device *, char *);
|
||||
int thermal_build_list_of_policies(char *buf);
|
||||
|
||||
/* Helpers */
|
||||
void thermal_zone_set_trips(struct thermal_zone_device *tz);
|
||||
void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms);
|
||||
|
||||
/* sysfs I/F */
|
||||
int thermal_zone_create_device_groups(struct thermal_zone_device *, int);
|
||||
|
@ -175,6 +175,13 @@ exit:
|
||||
mutex_unlock(&tz->lock);
|
||||
}
|
||||
|
||||
void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms)
|
||||
{
|
||||
*delay_jiffies = msecs_to_jiffies(delay_ms);
|
||||
if (delay_ms > 1000)
|
||||
*delay_jiffies = round_jiffies(*delay_jiffies);
|
||||
}
|
||||
|
||||
static void thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
int target)
|
||||
{
|
||||
|
@ -216,49 +216,6 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
|
||||
return ret ? ret : sprintf(buf, "%d\n", temperature);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
passive_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
int state;
|
||||
|
||||
if (sscanf(buf, "%d\n", &state) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* sanity check: values below 1000 millicelcius don't make sense
|
||||
* and can cause the system to go into a thermal heart attack
|
||||
*/
|
||||
if (state && state < 1000)
|
||||
return -EINVAL;
|
||||
|
||||
if (state && !tz->forced_passive) {
|
||||
if (!tz->passive_delay)
|
||||
tz->passive_delay = 1000;
|
||||
thermal_zone_device_rebind_exception(tz, "Processor",
|
||||
sizeof("Processor"));
|
||||
} else if (!state && tz->forced_passive) {
|
||||
tz->passive_delay = 0;
|
||||
thermal_zone_device_unbind_exception(tz, "Processor",
|
||||
sizeof("Processor"));
|
||||
}
|
||||
|
||||
tz->forced_passive = state;
|
||||
|
||||
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
passive_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", tz->forced_passive);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
policy_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
@ -403,7 +360,6 @@ static DEVICE_ATTR_RW(sustainable_power);
|
||||
|
||||
/* These thermal zone device attributes are created based on conditions */
|
||||
static DEVICE_ATTR_RW(mode);
|
||||
static DEVICE_ATTR_RW(passive);
|
||||
|
||||
/* These attributes are unconditionally added to a thermal zone */
|
||||
static struct attribute *thermal_zone_dev_attrs[] = {
|
||||
@ -438,45 +394,9 @@ static const struct attribute_group thermal_zone_mode_attribute_group = {
|
||||
.attrs = thermal_zone_mode_attrs,
|
||||
};
|
||||
|
||||
/* We expose passive only if passive trips are present */
|
||||
static struct attribute *thermal_zone_passive_attrs[] = {
|
||||
&dev_attr_passive.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static umode_t thermal_zone_passive_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
int attrno)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct thermal_zone_device *tz;
|
||||
enum thermal_trip_type trip_type;
|
||||
int count, passive = 0;
|
||||
|
||||
tz = container_of(dev, struct thermal_zone_device, device);
|
||||
|
||||
for (count = 0; count < tz->trips && !passive; count++) {
|
||||
tz->ops->get_trip_type(tz, count, &trip_type);
|
||||
|
||||
if (trip_type == THERMAL_TRIP_PASSIVE)
|
||||
passive = 1;
|
||||
}
|
||||
|
||||
if (!passive)
|
||||
return attr->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct attribute_group thermal_zone_passive_attribute_group = {
|
||||
.attrs = thermal_zone_passive_attrs,
|
||||
.is_visible = thermal_zone_passive_is_visible,
|
||||
};
|
||||
|
||||
static const struct attribute_group *thermal_zone_attribute_groups[] = {
|
||||
&thermal_zone_attribute_group,
|
||||
&thermal_zone_mode_attribute_group,
|
||||
&thermal_zone_passive_attribute_group,
|
||||
/* This is not NULL terminated as we create the group dynamically */
|
||||
};
|
||||
|
||||
@ -955,10 +875,7 @@ trip_point_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
instance =
|
||||
container_of(attr, struct thermal_instance, attr);
|
||||
|
||||
if (instance->trip == THERMAL_TRIPS_NONE)
|
||||
return sprintf(buf, "-1\n");
|
||||
else
|
||||
return sprintf(buf, "%d\n", instance->trip);
|
||||
return sprintf(buf, "%d\n", instance->trip);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
|
@ -24,7 +24,7 @@ omap4430_mpu_temp_sensor_registers = {
|
||||
.bgap_dtemp_mask = OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK,
|
||||
|
||||
.bgap_mode_ctrl = OMAP4430_TEMP_SENSOR_CTRL_OFFSET,
|
||||
.mode_ctrl_mask = OMAP4430_SINGLE_MODE_MASK,
|
||||
.mode_ctrl_mask = OMAP4430_CONTINUOUS_MODE_MASK,
|
||||
|
||||
.bgap_efuse = OMAP4430_FUSE_OPP_BGAP,
|
||||
};
|
||||
@ -58,7 +58,8 @@ omap4430_adc_to_temp[OMAP4430_ADC_END_VALUE - OMAP4430_ADC_START_VALUE + 1] = {
|
||||
const struct ti_bandgap_data omap4430_data = {
|
||||
.features = TI_BANDGAP_FEATURE_MODE_CONFIG |
|
||||
TI_BANDGAP_FEATURE_CLK_CTRL |
|
||||
TI_BANDGAP_FEATURE_POWER_SWITCH,
|
||||
TI_BANDGAP_FEATURE_POWER_SWITCH |
|
||||
TI_BANDGAP_FEATURE_CONT_MODE_ONLY,
|
||||
.fclock_name = "bandgap_fclk",
|
||||
.div_ck_name = "bandgap_fclk",
|
||||
.conv_table = omap4430_adc_to_temp,
|
||||
@ -96,7 +97,7 @@ omap4460_mpu_temp_sensor_registers = {
|
||||
.mask_cold_mask = OMAP4460_MASK_COLD_MASK,
|
||||
|
||||
.bgap_mode_ctrl = OMAP4460_BGAP_CTRL_OFFSET,
|
||||
.mode_ctrl_mask = OMAP4460_SINGLE_MODE_MASK,
|
||||
.mode_ctrl_mask = OMAP4460_CONTINUOUS_MODE_MASK,
|
||||
|
||||
.bgap_counter = OMAP4460_BGAP_COUNTER_OFFSET,
|
||||
.counter_mask = OMAP4460_COUNTER_MASK,
|
||||
|
@ -40,7 +40,7 @@
|
||||
/* OMAP4430.TEMP_SENSOR bits */
|
||||
#define OMAP4430_BGAP_TEMPSOFF_MASK BIT(12)
|
||||
#define OMAP4430_BGAP_TSHUT_MASK BIT(11)
|
||||
#define OMAP4430_SINGLE_MODE_MASK BIT(10)
|
||||
#define OMAP4430_CONTINUOUS_MODE_MASK BIT(10)
|
||||
#define OMAP4430_BGAP_TEMP_SENSOR_SOC_MASK BIT(9)
|
||||
#define OMAP4430_BGAP_TEMP_SENSOR_EOCZ_MASK BIT(8)
|
||||
#define OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK (0xff << 0)
|
||||
@ -113,7 +113,7 @@
|
||||
#define OMAP4460_BGAP_TEMP_SENSOR_DTEMP_MASK (0x3ff << 0)
|
||||
|
||||
/* OMAP4460.BANDGAP_CTRL bits */
|
||||
#define OMAP4460_SINGLE_MODE_MASK BIT(31)
|
||||
#define OMAP4460_CONTINUOUS_MODE_MASK BIT(31)
|
||||
#define OMAP4460_MASK_HOT_MASK BIT(1)
|
||||
#define OMAP4460_MASK_COLD_MASK BIT(0)
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@ -602,36 +603,41 @@ void *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id)
|
||||
static int
|
||||
ti_bandgap_force_single_read(struct ti_bandgap *bgp, int id)
|
||||
{
|
||||
u32 counter = 1000;
|
||||
struct temp_sensor_registers *tsr;
|
||||
struct temp_sensor_registers *tsr = bgp->conf->sensors[id].registers;
|
||||
void __iomem *temp_sensor_ctrl = bgp->base + tsr->temp_sensor_ctrl;
|
||||
int error;
|
||||
u32 val;
|
||||
|
||||
/* Select single conversion mode */
|
||||
if (TI_BANDGAP_HAS(bgp, MODE_CONFIG))
|
||||
RMW_BITS(bgp, id, bgap_mode_ctrl, mode_ctrl_mask, 0);
|
||||
|
||||
/* Start of Conversion = 1 */
|
||||
RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 1);
|
||||
|
||||
/* Wait for EOCZ going up */
|
||||
tsr = bgp->conf->sensors[id].registers;
|
||||
|
||||
while (--counter) {
|
||||
if (ti_bandgap_readl(bgp, tsr->temp_sensor_ctrl) &
|
||||
tsr->bgap_eocz_mask)
|
||||
break;
|
||||
/* Select continuous or single conversion mode */
|
||||
if (TI_BANDGAP_HAS(bgp, MODE_CONFIG)) {
|
||||
if (TI_BANDGAP_HAS(bgp, CONT_MODE_ONLY))
|
||||
RMW_BITS(bgp, id, bgap_mode_ctrl, mode_ctrl_mask, 1);
|
||||
else
|
||||
RMW_BITS(bgp, id, bgap_mode_ctrl, mode_ctrl_mask, 0);
|
||||
}
|
||||
|
||||
/* Start of Conversion = 0 */
|
||||
RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 0);
|
||||
/* Set Start of Conversion if available */
|
||||
if (tsr->bgap_soc_mask) {
|
||||
RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 1);
|
||||
|
||||
/* Wait for EOCZ going down */
|
||||
counter = 1000;
|
||||
while (--counter) {
|
||||
if (!(ti_bandgap_readl(bgp, tsr->temp_sensor_ctrl) &
|
||||
tsr->bgap_eocz_mask))
|
||||
break;
|
||||
/* Wait for EOCZ going up */
|
||||
error = readl_poll_timeout_atomic(temp_sensor_ctrl, val,
|
||||
val & tsr->bgap_eocz_mask,
|
||||
1, 1000);
|
||||
if (error)
|
||||
dev_warn(bgp->dev, "eocz timed out waiting high\n");
|
||||
|
||||
/* Clear Start of Conversion if available */
|
||||
RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 0);
|
||||
}
|
||||
|
||||
/* Wait for EOCZ going down, always needed even if no bgap_soc_mask */
|
||||
error = readl_poll_timeout_atomic(temp_sensor_ctrl, val,
|
||||
!(val & tsr->bgap_eocz_mask),
|
||||
1, 1500);
|
||||
if (error)
|
||||
dev_warn(bgp->dev, "eocz timed out waiting low\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -280,6 +280,7 @@ struct ti_temp_sensor {
|
||||
* has Errata 814
|
||||
* TI_BANDGAP_FEATURE_UNRELIABLE - used when the sensor readings are too
|
||||
* inaccurate.
|
||||
* TI_BANDGAP_FEATURE_CONT_MODE_ONLY - used when single mode hangs the sensor
|
||||
* TI_BANDGAP_HAS(b, f) - macro to check if a bandgap device is capable of a
|
||||
* specific feature (above) or not. Return non-zero, if yes.
|
||||
*/
|
||||
@ -295,6 +296,7 @@ struct ti_temp_sensor {
|
||||
#define TI_BANDGAP_FEATURE_HISTORY_BUFFER BIT(9)
|
||||
#define TI_BANDGAP_FEATURE_ERRATA_814 BIT(10)
|
||||
#define TI_BANDGAP_FEATURE_UNRELIABLE BIT(11)
|
||||
#define TI_BANDGAP_FEATURE_CONT_MODE_ONLY BIT(12)
|
||||
#define TI_BANDGAP_HAS(b, f) \
|
||||
((b)->conf->features & TI_BANDGAP_FEATURE_ ## f)
|
||||
|
||||
|
@ -166,6 +166,7 @@ int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
|
||||
char *domain)
|
||||
{
|
||||
struct ti_thermal_data *data;
|
||||
int interval;
|
||||
|
||||
data = ti_bandgap_get_sensor_data(bgp, id);
|
||||
|
||||
@ -183,9 +184,10 @@ int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
|
||||
return PTR_ERR(data->ti_thermal);
|
||||
}
|
||||
|
||||
interval = jiffies_to_msecs(data->ti_thermal->polling_delay_jiffies);
|
||||
|
||||
ti_bandgap_set_sensor_data(bgp, id, data);
|
||||
ti_bandgap_write_update_interval(bgp, data->sensor_id,
|
||||
data->ti_thermal->polling_delay);
|
||||
ti_bandgap_write_update_interval(bgp, data->sensor_id, interval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,256 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* ZTE's zx2967 family thermal sensor driver
|
||||
*
|
||||
* Copyright (C) 2017 ZTE Ltd.
|
||||
*
|
||||
* Author: Baoyou Xie <baoyou.xie@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
/* Power Mode: 0->low 1->high */
|
||||
#define ZX2967_THERMAL_POWER_MODE 0
|
||||
#define ZX2967_POWER_MODE_LOW 0
|
||||
#define ZX2967_POWER_MODE_HIGH 1
|
||||
|
||||
/* DCF Control Register */
|
||||
#define ZX2967_THERMAL_DCF 0x4
|
||||
#define ZX2967_DCF_EN BIT(1)
|
||||
#define ZX2967_DCF_FREEZE BIT(0)
|
||||
|
||||
/* Selection Register */
|
||||
#define ZX2967_THERMAL_SEL 0x8
|
||||
|
||||
/* Control Register */
|
||||
#define ZX2967_THERMAL_CTRL 0x10
|
||||
|
||||
#define ZX2967_THERMAL_READY BIT(12)
|
||||
#define ZX2967_THERMAL_TEMP_MASK GENMASK(11, 0)
|
||||
#define ZX2967_THERMAL_ID_MASK 0x18
|
||||
#define ZX2967_THERMAL_ID 0x10
|
||||
|
||||
#define ZX2967_GET_TEMP_TIMEOUT_US (100 * 1024)
|
||||
|
||||
/**
|
||||
* struct zx2967_thermal_priv - zx2967 thermal sensor private structure
|
||||
* @tzd: struct thermal_zone_device where the sensor is registered
|
||||
* @lock: prevents read sensor in parallel
|
||||
* @clk_topcrm: topcrm clk structure
|
||||
* @clk_apb: apb clk structure
|
||||
* @regs: pointer to base address of the thermal sensor
|
||||
* @dev: struct device pointer
|
||||
*/
|
||||
|
||||
struct zx2967_thermal_priv {
|
||||
struct thermal_zone_device *tzd;
|
||||
struct mutex lock;
|
||||
struct clk *clk_topcrm;
|
||||
struct clk *clk_apb;
|
||||
void __iomem *regs;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static int zx2967_thermal_get_temp(void *data, int *temp)
|
||||
{
|
||||
void __iomem *regs;
|
||||
struct zx2967_thermal_priv *priv = data;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!priv->tzd)
|
||||
return -EAGAIN;
|
||||
|
||||
regs = priv->regs;
|
||||
mutex_lock(&priv->lock);
|
||||
writel_relaxed(ZX2967_POWER_MODE_LOW,
|
||||
regs + ZX2967_THERMAL_POWER_MODE);
|
||||
writel_relaxed(ZX2967_DCF_EN, regs + ZX2967_THERMAL_DCF);
|
||||
|
||||
val = readl_relaxed(regs + ZX2967_THERMAL_SEL);
|
||||
val &= ~ZX2967_THERMAL_ID_MASK;
|
||||
val |= ZX2967_THERMAL_ID;
|
||||
writel_relaxed(val, regs + ZX2967_THERMAL_SEL);
|
||||
|
||||
/*
|
||||
* Must wait for a while, surely it's a bit odd.
|
||||
* otherwise temperature value we got has a few deviation, even if
|
||||
* the THERMAL_READY bit is set.
|
||||
*/
|
||||
usleep_range(100, 300);
|
||||
ret = readx_poll_timeout(readl, regs + ZX2967_THERMAL_CTRL,
|
||||
val, val & ZX2967_THERMAL_READY, 300,
|
||||
ZX2967_GET_TEMP_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "Thermal sensor data timeout\n");
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
writel_relaxed(ZX2967_DCF_FREEZE | ZX2967_DCF_EN,
|
||||
regs + ZX2967_THERMAL_DCF);
|
||||
val = readl_relaxed(regs + ZX2967_THERMAL_CTRL)
|
||||
& ZX2967_THERMAL_TEMP_MASK;
|
||||
writel_relaxed(ZX2967_POWER_MODE_HIGH,
|
||||
regs + ZX2967_THERMAL_POWER_MODE);
|
||||
|
||||
/*
|
||||
* Calculate temperature
|
||||
* In dts, slope is multiplied by 1000.
|
||||
*/
|
||||
*temp = DIV_ROUND_CLOSEST(((s32)val + priv->tzd->tzp->offset) * 1000,
|
||||
priv->tzd->tzp->slope);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&priv->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_of_device_ops zx2967_of_thermal_ops = {
|
||||
.get_temp = zx2967_thermal_get_temp,
|
||||
};
|
||||
|
||||
static int zx2967_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct zx2967_thermal_priv *priv;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(priv->regs))
|
||||
return PTR_ERR(priv->regs);
|
||||
|
||||
priv->clk_topcrm = devm_clk_get(&pdev->dev, "topcrm");
|
||||
if (IS_ERR(priv->clk_topcrm)) {
|
||||
ret = PTR_ERR(priv->clk_topcrm);
|
||||
dev_err(&pdev->dev, "failed to get topcrm clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->clk_topcrm);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable topcrm clock: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->clk_apb = devm_clk_get(&pdev->dev, "apb");
|
||||
if (IS_ERR(priv->clk_apb)) {
|
||||
ret = PTR_ERR(priv->clk_apb);
|
||||
dev_err(&pdev->dev, "failed to get apb clock: %d\n", ret);
|
||||
goto disable_clk_topcrm;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->clk_apb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable apb clock: %d\n",
|
||||
ret);
|
||||
goto disable_clk_topcrm;
|
||||
}
|
||||
|
||||
mutex_init(&priv->lock);
|
||||
priv->tzd = thermal_zone_of_sensor_register(&pdev->dev,
|
||||
0, priv, &zx2967_of_thermal_ops);
|
||||
|
||||
if (IS_ERR(priv->tzd)) {
|
||||
ret = PTR_ERR(priv->tzd);
|
||||
dev_err(&pdev->dev, "failed to register sensor: %d\n", ret);
|
||||
goto disable_clk_all;
|
||||
}
|
||||
|
||||
if (priv->tzd->tzp->slope == 0) {
|
||||
thermal_zone_of_sensor_unregister(&pdev->dev, priv->tzd);
|
||||
dev_err(&pdev->dev, "coefficients of sensor is invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto disable_clk_all;
|
||||
}
|
||||
|
||||
priv->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk_all:
|
||||
clk_disable_unprepare(priv->clk_apb);
|
||||
disable_clk_topcrm:
|
||||
clk_disable_unprepare(priv->clk_topcrm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zx2967_thermal_exit(struct platform_device *pdev)
|
||||
{
|
||||
struct zx2967_thermal_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
thermal_zone_of_sensor_unregister(&pdev->dev, priv->tzd);
|
||||
clk_disable_unprepare(priv->clk_topcrm);
|
||||
clk_disable_unprepare(priv->clk_apb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id zx2967_thermal_id_table[] = {
|
||||
{ .compatible = "zte,zx296718-thermal" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, zx2967_thermal_id_table);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int zx2967_thermal_suspend(struct device *dev)
|
||||
{
|
||||
struct zx2967_thermal_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (priv && priv->clk_topcrm)
|
||||
clk_disable_unprepare(priv->clk_topcrm);
|
||||
|
||||
if (priv && priv->clk_apb)
|
||||
clk_disable_unprepare(priv->clk_apb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zx2967_thermal_resume(struct device *dev)
|
||||
{
|
||||
struct zx2967_thermal_priv *priv = dev_get_drvdata(dev);
|
||||
int error;
|
||||
|
||||
error = clk_prepare_enable(priv->clk_topcrm);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = clk_prepare_enable(priv->clk_apb);
|
||||
if (error) {
|
||||
clk_disable_unprepare(priv->clk_topcrm);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(zx2967_thermal_pm_ops,
|
||||
zx2967_thermal_suspend, zx2967_thermal_resume);
|
||||
|
||||
static struct platform_driver zx2967_thermal_driver = {
|
||||
.probe = zx2967_thermal_probe,
|
||||
.remove = zx2967_thermal_exit,
|
||||
.driver = {
|
||||
.name = "zx2967_thermal",
|
||||
.of_match_table = zx2967_thermal_id_table,
|
||||
.pm = &zx2967_thermal_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(zx2967_thermal_driver);
|
||||
|
||||
MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
|
||||
MODULE_DESCRIPTION("ZTE zx2967 thermal driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -158,6 +158,9 @@ int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
|
||||
u16 qcom_adc_tm5_temp_volt_scale(unsigned int prescale_ratio,
|
||||
u32 full_scale_code_volt, int temp);
|
||||
|
||||
int qcom_adc5_prescaling_from_dt(u32 num, u32 den);
|
||||
|
||||
int qcom_adc5_hw_settle_time_from_dt(u32 value, const unsigned int *hw_settle);
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/workqueue.h>
|
||||
#include <uapi/linux/thermal.h>
|
||||
|
||||
#define THERMAL_TRIPS_NONE -1
|
||||
#define THERMAL_MAX_TRIPS 12
|
||||
|
||||
/* invalid cooling state */
|
||||
@ -77,8 +76,6 @@ struct thermal_zone_device_ops {
|
||||
int (*set_emul_temp) (struct thermal_zone_device *, int);
|
||||
int (*get_trend) (struct thermal_zone_device *, int,
|
||||
enum thermal_trend *);
|
||||
int (*notify) (struct thermal_zone_device *, int,
|
||||
enum thermal_trip_type);
|
||||
void (*hot)(struct thermal_zone_device *);
|
||||
void (*critical)(struct thermal_zone_device *);
|
||||
};
|
||||
@ -118,9 +115,9 @@ struct thermal_cooling_device {
|
||||
* @devdata: private pointer for device private data
|
||||
* @trips: number of trip points the thermal zone supports
|
||||
* @trips_disabled; bitmap for disabled trips
|
||||
* @passive_delay: number of milliseconds to wait between polls when
|
||||
* @passive_delay_jiffies: number of jiffies to wait between polls when
|
||||
* performing passive cooling.
|
||||
* @polling_delay: number of milliseconds to wait between polls when
|
||||
* @polling_delay_jiffies: number of jiffies to wait between polls when
|
||||
* checking whether trip points have been crossed (0 for
|
||||
* interrupt driven systems)
|
||||
* @temperature: current temperature. This is only for core code,
|
||||
@ -133,9 +130,6 @@ struct thermal_cooling_device {
|
||||
trip point.
|
||||
* @prev_high_trip: the above current temperature if you've crossed a
|
||||
passive trip point.
|
||||
* @forced_passive: If > 0, temperature at which to switch on all ACPI
|
||||
* processor cooling devices. Currently only used by the
|
||||
* step-wise governor.
|
||||
* @need_update: if equals 1, thermal_zone_device_update needs to be invoked.
|
||||
* @ops: operations this &thermal_zone_device supports
|
||||
* @tzp: thermal zone parameters
|
||||
@ -161,15 +155,14 @@ struct thermal_zone_device {
|
||||
void *devdata;
|
||||
int trips;
|
||||
unsigned long trips_disabled; /* bitmap for disabled trips */
|
||||
int passive_delay;
|
||||
int polling_delay;
|
||||
unsigned long passive_delay_jiffies;
|
||||
unsigned long polling_delay_jiffies;
|
||||
int temperature;
|
||||
int last_temperature;
|
||||
int emul_temperature;
|
||||
int passive;
|
||||
int prev_low_trip;
|
||||
int prev_high_trip;
|
||||
unsigned int forced_passive;
|
||||
atomic_t need_update;
|
||||
struct thermal_zone_device_ops *ops;
|
||||
struct thermal_zone_params *tzp;
|
||||
@ -397,7 +390,6 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
|
||||
int thermal_zone_get_slope(struct thermal_zone_device *tz);
|
||||
int thermal_zone_get_offset(struct thermal_zone_device *tz);
|
||||
|
||||
void thermal_cdev_update(struct thermal_cooling_device *);
|
||||
void thermal_notify_framework(struct thermal_zone_device *, int);
|
||||
int thermal_zone_device_enable(struct thermal_zone_device *tz);
|
||||
int thermal_zone_device_disable(struct thermal_zone_device *tz);
|
||||
@ -444,8 +436,6 @@ static inline int thermal_zone_get_offset(
|
||||
struct thermal_zone_device *tz)
|
||||
{ return -ENODEV; }
|
||||
|
||||
static inline void thermal_cdev_update(struct thermal_cooling_device *cdev)
|
||||
{ }
|
||||
static inline void thermal_notify_framework(struct thermal_zone_device *tz,
|
||||
int trip)
|
||||
{ }
|
||||
|
Loading…
Reference in New Issue
Block a user