mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 12:43:29 +02:00
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:
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
|
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
|
emul_temp
|
||||||
Interface to set the emulated temperature method in thermal zone
|
Interface to set the emulated temperature method in thermal zone
|
||||||
(sensor). After setting this temperature, the thermal zone may pass
|
(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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void acpi_thermal_zone_device_hot(struct thermal_zone_device *thermal)
|
||||||
static int thermal_notify(struct thermal_zone_device *thermal, int trip,
|
|
||||||
enum thermal_trip_type trip_type)
|
|
||||||
{
|
{
|
||||||
u8 type = 0;
|
|
||||||
struct acpi_thermal *tz = thermal->devdata;
|
struct acpi_thermal *tz = thermal->devdata;
|
||||||
|
|
||||||
if (trip_type == THERMAL_TRIP_CRITICAL)
|
acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
|
||||||
type = ACPI_THERMAL_NOTIFY_CRITICAL;
|
dev_name(&tz->device->dev),
|
||||||
else if (trip_type == THERMAL_TRIP_HOT)
|
ACPI_THERMAL_NOTIFY_HOT, 1);
|
||||||
type = ACPI_THERMAL_NOTIFY_HOT;
|
}
|
||||||
else
|
|
||||||
return 0;
|
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,
|
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)
|
thermal_zone_device_critical(thermal);
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *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:
|
failed:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -805,7 +783,8 @@ static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
|
|||||||
.get_trip_temp = thermal_get_trip_temp,
|
.get_trip_temp = thermal_get_trip_temp,
|
||||||
.get_crit_temp = thermal_get_crit_temp,
|
.get_crit_temp = thermal_get_crit_temp,
|
||||||
.get_trend = thermal_get_trend,
|
.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)
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
ctx->cdev = cdev;
|
ctx->cdev = cdev;
|
||||||
thermal_cdev_update(cdev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -368,6 +368,28 @@ static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
|
|||||||
return 0;
|
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,
|
static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
|
||||||
u16 adc_code,
|
u16 adc_code,
|
||||||
bool absolute,
|
bool absolute,
|
||||||
@@ -463,6 +485,21 @@ static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
|
|||||||
return 0;
|
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,
|
static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
|
||||||
const struct vadc_prescale_ratio *prescale,
|
const struct vadc_prescale_ratio *prescale,
|
||||||
const struct adc5_data *data,
|
const struct adc5_data *data,
|
||||||
@@ -627,6 +664,19 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(qcom_vadc_scale);
|
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,
|
int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
|
||||||
unsigned int prescale_ratio,
|
unsigned int prescale_ratio,
|
||||||
const struct adc5_data *data,
|
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);
|
pr_notice("interval changed to: %d\n", interval);
|
||||||
|
|
||||||
if (thermal)
|
if (thermal)
|
||||||
thermal->polling_delay = interval*1000;
|
thermal->polling_delay_jiffies =
|
||||||
|
round_jiffies(msecs_to_jiffies(interval * 1000));
|
||||||
|
|
||||||
prev_interval = interval;
|
prev_interval = interval;
|
||||||
}
|
}
|
||||||
|
@@ -450,15 +450,6 @@ depends on (ARCH_STI || ARCH_STM32) && OF
|
|||||||
source "drivers/thermal/st/Kconfig"
|
source "drivers/thermal/st/Kconfig"
|
||||||
endmenu
|
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"
|
source "drivers/thermal/tegra/Kconfig"
|
||||||
|
|
||||||
config GENERIC_ADC_THERMAL
|
config GENERIC_ADC_THERMAL
|
||||||
@@ -476,14 +467,6 @@ depends on (ARCH_QCOM && OF) || COMPILE_TEST
|
|||||||
source "drivers/thermal/qcom/Kconfig"
|
source "drivers/thermal/qcom/Kconfig"
|
||||||
endmenu
|
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
|
config UNIPHIER_THERMAL
|
||||||
tristate "Socionext UniPhier thermal driver"
|
tristate "Socionext UniPhier thermal driver"
|
||||||
depends on ARCH_UNIPHIER || COMPILE_TEST
|
depends on ARCH_UNIPHIER || COMPILE_TEST
|
||||||
|
@@ -42,7 +42,6 @@ obj-y += samsung/
|
|||||||
obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
|
obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
|
||||||
obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
|
obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
|
||||||
obj-$(CONFIG_ARMADA_THERMAL) += armada_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_THERMAL) += imx_thermal.o
|
||||||
obj-$(CONFIG_IMX_SC_THERMAL) += imx_sc_thermal.o
|
obj-$(CONFIG_IMX_SC_THERMAL) += imx_sc_thermal.o
|
||||||
obj-$(CONFIG_IMX8MM_THERMAL) += imx8mm_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_HISI_THERMAL) += hisi_thermal.o
|
||||||
obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
|
obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
|
||||||
obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.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_UNIPHIER_THERMAL) += uniphier_thermal.o
|
||||||
obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o
|
obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o
|
||||||
obj-$(CONFIG_SPRD_THERMAL) += sprd_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);
|
frequency = get_state_freq(cpufreq_cdev, state);
|
||||||
|
|
||||||
ret = freq_qos_update_request(&cpufreq_cdev->qos_req, frequency);
|
ret = freq_qos_update_request(&cpufreq_cdev->qos_req, frequency);
|
||||||
if (ret > 0) {
|
if (ret >= 0) {
|
||||||
cpufreq_cdev->cpufreq_state = state;
|
cpufreq_cdev->cpufreq_state = state;
|
||||||
cpus = cpufreq_cdev->policy->cpus;
|
cpus = cpufreq_cdev->policy->cpus;
|
||||||
max_capacity = arch_scale_cpu_capacity(cpumask_first(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_zone_device_update(thermal->zone,
|
||||||
THERMAL_EVENT_UNSPECIFIED);
|
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);
|
queue_delayed_work(system_freezable_wq, &thermal->work, delay);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -245,7 +245,7 @@ static int da9062_thermal_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
dev_dbg(&pdev->dev,
|
dev_dbg(&pdev->dev,
|
||||||
"TJUNC temperature polling period set at %d ms\n",
|
"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");
|
ret = platform_get_irq_byname(pdev, "THERMAL");
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@@ -258,7 +258,7 @@ static u32 pid_controller(struct thermal_zone_device *tz,
|
|||||||
* power being applied, slowing down the controller)
|
* power being applied, slowing down the controller)
|
||||||
*/
|
*/
|
||||||
d = mul_frac(tz->tzp->k_d, err - params->prev_err);
|
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;
|
params->prev_err = err;
|
||||||
|
|
||||||
power_range = p + i + d;
|
power_range = p + i + d;
|
||||||
@@ -589,6 +589,34 @@ static void allow_maximum_power(struct thermal_zone_device *tz)
|
|||||||
mutex_unlock(&tz->lock);
|
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
|
* power_allocator_bind() - bind the power_allocator governor to a thermal zone
|
||||||
* @tz: thermal zone to bind it to
|
* @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
|
* Initialize the PID controller parameters and bind it to the thermal
|
||||||
* zone.
|
* 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)
|
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;
|
struct power_allocator_params *params;
|
||||||
int control_temp;
|
int control_temp;
|
||||||
|
|
||||||
|
ret = check_power_actors(tz);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
params = kzalloc(sizeof(*params), GFP_KERNEL);
|
params = kzalloc(sizeof(*params), GFP_KERNEL);
|
||||||
if (!params)
|
if (!params)
|
||||||
return -ENOMEM;
|
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, activate a passive instance.
|
||||||
* If value is -1, deactivate 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;
|
tz->passive += value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,13 +122,8 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
|
|||||||
bool throttle = false;
|
bool throttle = false;
|
||||||
int old_target;
|
int old_target;
|
||||||
|
|
||||||
if (trip == THERMAL_TRIPS_NONE) {
|
tz->ops->get_trip_temp(tz, trip, &trip_temp);
|
||||||
trip_temp = tz->forced_passive;
|
tz->ops->get_trip_type(tz, trip, &trip_type);
|
||||||
trip_type = THERMAL_TRIPS_NONE;
|
|
||||||
} else {
|
|
||||||
tz->ops->get_trip_temp(tz, trip, &trip_temp);
|
|
||||||
tz->ops->get_trip_type(tz, trip, &trip_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
trend = get_tz_trend(tz, trip);
|
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);
|
thermal_zone_trip_update(tz, trip);
|
||||||
|
|
||||||
if (tz->forced_passive)
|
|
||||||
thermal_zone_trip_update(tz, THERMAL_TRIPS_NONE);
|
|
||||||
|
|
||||||
mutex_lock(&tz->lock);
|
mutex_lock(&tz->lock);
|
||||||
|
|
||||||
list_for_each_entry(instance, &tz->thermal_instances, tz_node)
|
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;
|
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 = {
|
static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
|
||||||
.get_temp = int340x_thermal_get_zone_temp,
|
.get_temp = int340x_thermal_get_zone_temp,
|
||||||
.get_trip_temp = int340x_thermal_get_trip_temp,
|
.get_trip_temp = int340x_thermal_get_trip_temp,
|
||||||
.get_trip_type = int340x_thermal_get_trip_type,
|
.get_trip_type = int340x_thermal_get_trip_type,
|
||||||
.set_trip_temp = int340x_thermal_set_trip_temp,
|
.set_trip_temp = int340x_thermal_set_trip_temp,
|
||||||
.get_trip_hyst = int340x_thermal_get_trip_hyst,
|
.get_trip_hyst = int340x_thermal_get_trip_hyst,
|
||||||
|
.critical = int340x_thermal_critical,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
|
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;
|
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 = {
|
static struct thermal_zone_device_ops tzd_ops = {
|
||||||
.get_temp = pch_thermal_get_temp,
|
.get_temp = pch_thermal_get_temp,
|
||||||
.get_trip_type = pch_get_trip_type,
|
.get_trip_type = pch_get_trip_type,
|
||||||
.get_trip_temp = pch_get_trip_temp,
|
.get_trip_temp = pch_get_trip_temp,
|
||||||
|
.critical = pch_critical,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum board_ids {
|
enum board_ids {
|
||||||
|
@@ -100,7 +100,6 @@ static int khadas_mcu_fan_probe(struct platform_device *pdev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
ctx->cdev = cdev;
|
ctx->cdev = cdev;
|
||||||
thermal_cdev_update(cdev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,17 @@ config QCOM_TSENS
|
|||||||
Also able to set threshold temperature for both hot and cold and update
|
Also able to set threshold temperature for both hot and cold and update
|
||||||
when a threshold is reached.
|
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
|
config QCOM_SPMI_TEMP_ALARM
|
||||||
tristate "Qualcomm SPMI PMIC Temperature Alarm"
|
tristate "Qualcomm SPMI PMIC Temperature Alarm"
|
||||||
depends on OF && SPMI && IIO
|
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 \
|
qcom_tsens-y += tsens.o tsens-v2.o tsens-v1.o tsens-v0_1.o \
|
||||||
tsens-8960.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
|
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.
|
* - Critical trip point will cause a system shutdown.
|
||||||
*/
|
*/
|
||||||
static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
|
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,
|
mod_delayed_work(system_freezable_power_efficient_wq,
|
||||||
&tz->poll_queue,
|
&tz->poll_queue, delay);
|
||||||
round_jiffies(msecs_to_jiffies(delay)));
|
|
||||||
else if (delay)
|
|
||||||
mod_delayed_work(system_freezable_power_efficient_wq,
|
|
||||||
&tz->poll_queue,
|
|
||||||
msecs_to_jiffies(delay));
|
|
||||||
else
|
else
|
||||||
cancel_delayed_work(&tz->poll_queue);
|
cancel_delayed_work(&tz->poll_queue);
|
||||||
}
|
}
|
||||||
@@ -317,9 +312,9 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz)
|
|||||||
mutex_lock(&tz->lock);
|
mutex_lock(&tz->lock);
|
||||||
|
|
||||||
if (!stop && tz->passive)
|
if (!stop && tz->passive)
|
||||||
thermal_zone_device_set_polling(tz, tz->passive_delay);
|
thermal_zone_device_set_polling(tz, tz->passive_delay_jiffies);
|
||||||
else if (!stop && tz->polling_delay)
|
else if (!stop && tz->polling_delay_jiffies)
|
||||||
thermal_zone_device_set_polling(tz, tz->polling_delay);
|
thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies);
|
||||||
else
|
else
|
||||||
thermal_zone_device_set_polling(tz, 0);
|
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);
|
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)
|
if (trip_type == THERMAL_TRIP_HOT && tz->ops->hot)
|
||||||
tz->ops->hot(tz);
|
tz->ops->hot(tz);
|
||||||
else if (trip_type == THERMAL_TRIP_CRITICAL)
|
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;
|
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,
|
static int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
|
||||||
enum thermal_device_mode mode)
|
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);
|
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 *),
|
int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *),
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
@@ -688,23 +654,6 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id)
|
|||||||
return match;
|
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
|
* 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;
|
unsigned long max_state;
|
||||||
int result, ret;
|
int result, ret;
|
||||||
|
|
||||||
if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
|
if (trip >= tz->trips || trip < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
list_for_each_entry(pos1, &thermal_tz_list, node) {
|
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->device.class = &thermal_class;
|
||||||
tz->devdata = devdata;
|
tz->devdata = devdata;
|
||||||
tz->trips = trips;
|
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 */
|
/* sys I/F */
|
||||||
/* Add nodes that are always present via .groups */
|
/* 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);
|
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. */
|
/* Update the new thermal zone and mark it as already updated. */
|
||||||
if (atomic_cmpxchg(&tz->need_update, 1, 0))
|
if (atomic_cmpxchg(&tz->need_update, 1, 0))
|
||||||
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
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;
|
cdev->ops->power2state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void thermal_cdev_update(struct thermal_cooling_device *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct thermal_trip - representation of a point in temperature domain
|
* struct thermal_trip - representation of a point in temperature domain
|
||||||
* @np: pointer to struct device_node that this trip point was created from
|
* @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 *);
|
int thermal_register_governor(struct thermal_governor *);
|
||||||
void thermal_unregister_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_zone_device_set_policy(struct thermal_zone_device *, char *);
|
||||||
int thermal_build_list_of_policies(char *buf);
|
int thermal_build_list_of_policies(char *buf);
|
||||||
|
|
||||||
/* Helpers */
|
/* Helpers */
|
||||||
void thermal_zone_set_trips(struct thermal_zone_device *tz);
|
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 */
|
/* sysfs I/F */
|
||||||
int thermal_zone_create_device_groups(struct thermal_zone_device *, int);
|
int thermal_zone_create_device_groups(struct thermal_zone_device *, int);
|
||||||
|
@@ -175,6 +175,13 @@ exit:
|
|||||||
mutex_unlock(&tz->lock);
|
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,
|
static void thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev,
|
||||||
int target)
|
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);
|
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
|
static ssize_t
|
||||||
policy_store(struct device *dev, struct device_attribute *attr,
|
policy_store(struct device *dev, struct device_attribute *attr,
|
||||||
const char *buf, size_t count)
|
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 */
|
/* These thermal zone device attributes are created based on conditions */
|
||||||
static DEVICE_ATTR_RW(mode);
|
static DEVICE_ATTR_RW(mode);
|
||||||
static DEVICE_ATTR_RW(passive);
|
|
||||||
|
|
||||||
/* These attributes are unconditionally added to a thermal zone */
|
/* These attributes are unconditionally added to a thermal zone */
|
||||||
static struct attribute *thermal_zone_dev_attrs[] = {
|
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,
|
.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[] = {
|
static const struct attribute_group *thermal_zone_attribute_groups[] = {
|
||||||
&thermal_zone_attribute_group,
|
&thermal_zone_attribute_group,
|
||||||
&thermal_zone_mode_attribute_group,
|
&thermal_zone_mode_attribute_group,
|
||||||
&thermal_zone_passive_attribute_group,
|
|
||||||
/* This is not NULL terminated as we create the group dynamically */
|
/* 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 =
|
instance =
|
||||||
container_of(attr, struct thermal_instance, attr);
|
container_of(attr, struct thermal_instance, attr);
|
||||||
|
|
||||||
if (instance->trip == THERMAL_TRIPS_NONE)
|
return sprintf(buf, "%d\n", instance->trip);
|
||||||
return sprintf(buf, "-1\n");
|
|
||||||
else
|
|
||||||
return sprintf(buf, "%d\n", instance->trip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
|
@@ -24,7 +24,7 @@ omap4430_mpu_temp_sensor_registers = {
|
|||||||
.bgap_dtemp_mask = OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK,
|
.bgap_dtemp_mask = OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK,
|
||||||
|
|
||||||
.bgap_mode_ctrl = OMAP4430_TEMP_SENSOR_CTRL_OFFSET,
|
.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,
|
.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 = {
|
const struct ti_bandgap_data omap4430_data = {
|
||||||
.features = TI_BANDGAP_FEATURE_MODE_CONFIG |
|
.features = TI_BANDGAP_FEATURE_MODE_CONFIG |
|
||||||
TI_BANDGAP_FEATURE_CLK_CTRL |
|
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",
|
.fclock_name = "bandgap_fclk",
|
||||||
.div_ck_name = "bandgap_fclk",
|
.div_ck_name = "bandgap_fclk",
|
||||||
.conv_table = omap4430_adc_to_temp,
|
.conv_table = omap4430_adc_to_temp,
|
||||||
@@ -96,7 +97,7 @@ omap4460_mpu_temp_sensor_registers = {
|
|||||||
.mask_cold_mask = OMAP4460_MASK_COLD_MASK,
|
.mask_cold_mask = OMAP4460_MASK_COLD_MASK,
|
||||||
|
|
||||||
.bgap_mode_ctrl = OMAP4460_BGAP_CTRL_OFFSET,
|
.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,
|
.bgap_counter = OMAP4460_BGAP_COUNTER_OFFSET,
|
||||||
.counter_mask = OMAP4460_COUNTER_MASK,
|
.counter_mask = OMAP4460_COUNTER_MASK,
|
||||||
|
@@ -40,7 +40,7 @@
|
|||||||
/* OMAP4430.TEMP_SENSOR bits */
|
/* OMAP4430.TEMP_SENSOR bits */
|
||||||
#define OMAP4430_BGAP_TEMPSOFF_MASK BIT(12)
|
#define OMAP4430_BGAP_TEMPSOFF_MASK BIT(12)
|
||||||
#define OMAP4430_BGAP_TSHUT_MASK BIT(11)
|
#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_SOC_MASK BIT(9)
|
||||||
#define OMAP4430_BGAP_TEMP_SENSOR_EOCZ_MASK BIT(8)
|
#define OMAP4430_BGAP_TEMP_SENSOR_EOCZ_MASK BIT(8)
|
||||||
#define OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK (0xff << 0)
|
#define OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK (0xff << 0)
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
#define OMAP4460_BGAP_TEMP_SENSOR_DTEMP_MASK (0x3ff << 0)
|
#define OMAP4460_BGAP_TEMP_SENSOR_DTEMP_MASK (0x3ff << 0)
|
||||||
|
|
||||||
/* OMAP4460.BANDGAP_CTRL bits */
|
/* 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_HOT_MASK BIT(1)
|
||||||
#define OMAP4460_MASK_COLD_MASK BIT(0)
|
#define OMAP4460_MASK_COLD_MASK BIT(0)
|
||||||
|
|
||||||
|
@@ -26,6 +26,7 @@
|
|||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
#include <linux/cpu_pm.h>
|
#include <linux/cpu_pm.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
@@ -602,36 +603,41 @@ void *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id)
|
|||||||
static int
|
static int
|
||||||
ti_bandgap_force_single_read(struct ti_bandgap *bgp, int id)
|
ti_bandgap_force_single_read(struct ti_bandgap *bgp, int id)
|
||||||
{
|
{
|
||||||
u32 counter = 1000;
|
struct temp_sensor_registers *tsr = bgp->conf->sensors[id].registers;
|
||||||
struct temp_sensor_registers *tsr;
|
void __iomem *temp_sensor_ctrl = bgp->base + tsr->temp_sensor_ctrl;
|
||||||
|
int error;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
/* Select single conversion mode */
|
/* Select continuous or single conversion mode */
|
||||||
if (TI_BANDGAP_HAS(bgp, MODE_CONFIG))
|
if (TI_BANDGAP_HAS(bgp, MODE_CONFIG)) {
|
||||||
RMW_BITS(bgp, id, bgap_mode_ctrl, mode_ctrl_mask, 0);
|
if (TI_BANDGAP_HAS(bgp, CONT_MODE_ONLY))
|
||||||
|
RMW_BITS(bgp, id, bgap_mode_ctrl, mode_ctrl_mask, 1);
|
||||||
/* Start of Conversion = 1 */
|
else
|
||||||
RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 1);
|
RMW_BITS(bgp, id, bgap_mode_ctrl, mode_ctrl_mask, 0);
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start of Conversion = 0 */
|
/* Set Start of Conversion if available */
|
||||||
RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 0);
|
if (tsr->bgap_soc_mask) {
|
||||||
|
RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 1);
|
||||||
|
|
||||||
/* Wait for EOCZ going down */
|
/* Wait for EOCZ going up */
|
||||||
counter = 1000;
|
error = readl_poll_timeout_atomic(temp_sensor_ctrl, val,
|
||||||
while (--counter) {
|
val & tsr->bgap_eocz_mask,
|
||||||
if (!(ti_bandgap_readl(bgp, tsr->temp_sensor_ctrl) &
|
1, 1000);
|
||||||
tsr->bgap_eocz_mask))
|
if (error)
|
||||||
break;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -280,6 +280,7 @@ struct ti_temp_sensor {
|
|||||||
* has Errata 814
|
* has Errata 814
|
||||||
* TI_BANDGAP_FEATURE_UNRELIABLE - used when the sensor readings are too
|
* TI_BANDGAP_FEATURE_UNRELIABLE - used when the sensor readings are too
|
||||||
* inaccurate.
|
* 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
|
* 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.
|
* 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_HISTORY_BUFFER BIT(9)
|
||||||
#define TI_BANDGAP_FEATURE_ERRATA_814 BIT(10)
|
#define TI_BANDGAP_FEATURE_ERRATA_814 BIT(10)
|
||||||
#define TI_BANDGAP_FEATURE_UNRELIABLE BIT(11)
|
#define TI_BANDGAP_FEATURE_UNRELIABLE BIT(11)
|
||||||
|
#define TI_BANDGAP_FEATURE_CONT_MODE_ONLY BIT(12)
|
||||||
#define TI_BANDGAP_HAS(b, f) \
|
#define TI_BANDGAP_HAS(b, f) \
|
||||||
((b)->conf->features & TI_BANDGAP_FEATURE_ ## 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)
|
char *domain)
|
||||||
{
|
{
|
||||||
struct ti_thermal_data *data;
|
struct ti_thermal_data *data;
|
||||||
|
int interval;
|
||||||
|
|
||||||
data = ti_bandgap_get_sensor_data(bgp, id);
|
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);
|
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_set_sensor_data(bgp, id, data);
|
||||||
ti_bandgap_write_update_interval(bgp, data->sensor_id,
|
ti_bandgap_write_update_interval(bgp, data->sensor_id, interval);
|
||||||
data->ti_thermal->polling_delay);
|
|
||||||
|
|
||||||
return 0;
|
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,
|
const struct adc5_data *data,
|
||||||
u16 adc_code, int *result_mdec);
|
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_prescaling_from_dt(u32 num, u32 den);
|
||||||
|
|
||||||
int qcom_adc5_hw_settle_time_from_dt(u32 value, const unsigned int *hw_settle);
|
int qcom_adc5_hw_settle_time_from_dt(u32 value, const unsigned int *hw_settle);
|
||||||
|
@@ -17,7 +17,6 @@
|
|||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <uapi/linux/thermal.h>
|
#include <uapi/linux/thermal.h>
|
||||||
|
|
||||||
#define THERMAL_TRIPS_NONE -1
|
|
||||||
#define THERMAL_MAX_TRIPS 12
|
#define THERMAL_MAX_TRIPS 12
|
||||||
|
|
||||||
/* invalid cooling state */
|
/* invalid cooling state */
|
||||||
@@ -77,8 +76,6 @@ struct thermal_zone_device_ops {
|
|||||||
int (*set_emul_temp) (struct thermal_zone_device *, int);
|
int (*set_emul_temp) (struct thermal_zone_device *, int);
|
||||||
int (*get_trend) (struct thermal_zone_device *, int,
|
int (*get_trend) (struct thermal_zone_device *, int,
|
||||||
enum thermal_trend *);
|
enum thermal_trend *);
|
||||||
int (*notify) (struct thermal_zone_device *, int,
|
|
||||||
enum thermal_trip_type);
|
|
||||||
void (*hot)(struct thermal_zone_device *);
|
void (*hot)(struct thermal_zone_device *);
|
||||||
void (*critical)(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
|
* @devdata: private pointer for device private data
|
||||||
* @trips: number of trip points the thermal zone supports
|
* @trips: number of trip points the thermal zone supports
|
||||||
* @trips_disabled; bitmap for disabled trips
|
* @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.
|
* 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
|
* checking whether trip points have been crossed (0 for
|
||||||
* interrupt driven systems)
|
* interrupt driven systems)
|
||||||
* @temperature: current temperature. This is only for core code,
|
* @temperature: current temperature. This is only for core code,
|
||||||
@@ -133,9 +130,6 @@ struct thermal_cooling_device {
|
|||||||
trip point.
|
trip point.
|
||||||
* @prev_high_trip: the above current temperature if you've crossed a
|
* @prev_high_trip: the above current temperature if you've crossed a
|
||||||
passive trip point.
|
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.
|
* @need_update: if equals 1, thermal_zone_device_update needs to be invoked.
|
||||||
* @ops: operations this &thermal_zone_device supports
|
* @ops: operations this &thermal_zone_device supports
|
||||||
* @tzp: thermal zone parameters
|
* @tzp: thermal zone parameters
|
||||||
@@ -161,15 +155,14 @@ struct thermal_zone_device {
|
|||||||
void *devdata;
|
void *devdata;
|
||||||
int trips;
|
int trips;
|
||||||
unsigned long trips_disabled; /* bitmap for disabled trips */
|
unsigned long trips_disabled; /* bitmap for disabled trips */
|
||||||
int passive_delay;
|
unsigned long passive_delay_jiffies;
|
||||||
int polling_delay;
|
unsigned long polling_delay_jiffies;
|
||||||
int temperature;
|
int temperature;
|
||||||
int last_temperature;
|
int last_temperature;
|
||||||
int emul_temperature;
|
int emul_temperature;
|
||||||
int passive;
|
int passive;
|
||||||
int prev_low_trip;
|
int prev_low_trip;
|
||||||
int prev_high_trip;
|
int prev_high_trip;
|
||||||
unsigned int forced_passive;
|
|
||||||
atomic_t need_update;
|
atomic_t need_update;
|
||||||
struct thermal_zone_device_ops *ops;
|
struct thermal_zone_device_ops *ops;
|
||||||
struct thermal_zone_params *tzp;
|
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_slope(struct thermal_zone_device *tz);
|
||||||
int thermal_zone_get_offset(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);
|
void thermal_notify_framework(struct thermal_zone_device *, int);
|
||||||
int thermal_zone_device_enable(struct thermal_zone_device *tz);
|
int thermal_zone_device_enable(struct thermal_zone_device *tz);
|
||||||
int thermal_zone_device_disable(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)
|
struct thermal_zone_device *tz)
|
||||||
{ return -ENODEV; }
|
{ return -ENODEV; }
|
||||||
|
|
||||||
static inline void thermal_cdev_update(struct thermal_cooling_device *cdev)
|
|
||||||
{ }
|
|
||||||
static inline void thermal_notify_framework(struct thermal_zone_device *tz,
|
static inline void thermal_notify_framework(struct thermal_zone_device *tz,
|
||||||
int trip)
|
int trip)
|
||||||
{ }
|
{ }
|
||||||
|
Reference in New Issue
Block a user