mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-02-20 11:45:48 +08:00
Merge "ARM: dts: msm: Add ADC_TM nodes for PM855, PM855B and PM855l"
This commit is contained in:
commit
2e670e7b5c
145
Documentation/devicetree/bindings/thermal/qcom-adc-tm.txt
Normal file
145
Documentation/devicetree/bindings/thermal/qcom-adc-tm.txt
Normal file
@ -0,0 +1,145 @@
|
||||
Qualcomm Technologies, Inc. PMIC thermal monitor ADC driver (ADC_TM)
|
||||
|
||||
PMIC thermal monitoring (TM) provides interface to thermal clients
|
||||
to set temperature thresholds and receive notification when the thresholds
|
||||
are crossed. A 15 bit ADC is used for measurements. The driver is part
|
||||
of the sysfs thermal framework that provides support to read the trip
|
||||
points, set threshold for the trip points and enable the trip points.
|
||||
|
||||
ADC_TM node
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: Should contain "qcom,adc-tm5" for PMIC5 ADC TM driver.
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: ADC_TM base address and length in the SPMI PMIC register map.
|
||||
|
||||
- #address-cells:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: Must be one. Child node 'reg' property should define ADC
|
||||
channel number.
|
||||
|
||||
- #size-cells:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: Must be zero.
|
||||
|
||||
- interrupts:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: End of conversion interrupt.
|
||||
|
||||
- interrupt-names:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: Should contain "thr-int-en" for PMIC5 ADC TM driver.
|
||||
|
||||
- qcom,decimation:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: This parameter is used to decrease ADC sampling rate.
|
||||
Quicker measurements can be made by reducing decimation ratio.
|
||||
For PMIC5 ADC, combined two step decimation values are 250, 420 and 840.
|
||||
If property is not found, default value of 840 will be used.
|
||||
|
||||
- qcom,avg-samples:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: Number of samples to be used for measurement.
|
||||
Averaging provides the option to obtain a single measurement
|
||||
from the ADC that is an average of multiple samples. The value
|
||||
selected is 2^(value).
|
||||
Valid values are: 1, 2, 4, 8, 16
|
||||
If property is not found, 1 sample will be used.
|
||||
|
||||
- #thermal-sensor-cells:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: Should be 1. See thermal.txt for a description.
|
||||
|
||||
- io-channels:
|
||||
Usage: Required
|
||||
Value type: <phandle u32>
|
||||
Definition: The phandle of the iio provider.
|
||||
|
||||
Channel node properties:
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: ADC channel number.
|
||||
See include/dt-bindings/iio/qcom,spmi-vadc.h
|
||||
|
||||
- qcom,pre-scaling:
|
||||
Usage: optional
|
||||
Value type: <u32 array>
|
||||
Definition: 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. Select one from
|
||||
the following options.
|
||||
<1 1>, <1 3>, <1 4>, <1 6>, <1 20>, <1 8>, <10 81>, <1 10>
|
||||
If property is not found default value depending on chip will be used.
|
||||
|
||||
- qcom,ratiometric:
|
||||
Usage: optional
|
||||
Value type: <empty>
|
||||
Definition: 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:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: Time between AMUX getting configured and the ADC starting
|
||||
conversion.
|
||||
For PMIC5, delay = 15us for value 0,
|
||||
100us * (value) for values 0 < value < 11, and
|
||||
2ms * (value - 10) otherwise.
|
||||
Valid values are: 15, 100, 200, 300, 400, 500, 600, 700, 1,
|
||||
2, 4, 8, 16, 32, 64, 128 ms
|
||||
If property is not found, channel will use 15us.
|
||||
|
||||
Example:
|
||||
|
||||
/* ADC_TM node */
|
||||
pmic_adc_tm: adc_tm@3500 {
|
||||
compatible = "qcom,adc-tm5";
|
||||
reg = <0x3500 0x100>;
|
||||
interrupts = <0x0 0x35 0x0 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "thr-int-en";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#thermal-sensor-cells = <1>;
|
||||
io-channels = <&pmic_vadc ADC_AMUX_THM2_PU2>;
|
||||
|
||||
/* Channel node */
|
||||
skin_msm_therm {
|
||||
reg = <ADC_AMUX_THM2_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
};
|
||||
|
||||
/* Adding thermal zone to register with of_thermal */
|
||||
&thermal_zones {
|
||||
wp-therm {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pmic_adc_tm ADC_AMUX_THM2_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -134,6 +134,17 @@
|
||||
qcom,pre-scaling = <1 1>;
|
||||
};
|
||||
};
|
||||
|
||||
pm855_adc_tm: adc_tm@3500 {
|
||||
compatible = "qcom,adc-tm5";
|
||||
reg = <0x3500 0x100>;
|
||||
interrupts = <0x0 0x35 0x0 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "thr-int-en";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#thermal-sensor-cells = <1>;
|
||||
io-channels = <&pm855_vadc ADC_XO_THERM_PU2>;
|
||||
};
|
||||
};
|
||||
|
||||
qcom,pm855@1 {
|
||||
|
@ -142,6 +142,17 @@
|
||||
};
|
||||
};
|
||||
|
||||
pm855b_adc_tm: adc_tm@3500 {
|
||||
compatible = "qcom,adc-tm5";
|
||||
reg = <0x3500 0x100>;
|
||||
interrupts = <0x2 0x35 0x0 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "thr-int-en";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#thermal-sensor-cells = <1>;
|
||||
io-channels = <&pm855b_vadc ADC_AMUX_THM2_PU2>;
|
||||
};
|
||||
|
||||
pm855b_charger: qcom,qpnp-smb5 {
|
||||
compatible = "qcom,qpnp-smb5";
|
||||
#address-cells = <1>;
|
||||
@ -472,6 +483,20 @@
|
||||
};
|
||||
|
||||
&thermal_zones {
|
||||
pm855b-wp-therm {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855b_adc_tm ADC_AMUX_THM2_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
pm855b_temp_alarm: pm855b_tz {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
|
@ -117,6 +117,19 @@
|
||||
"bcl-vbat-lvl2";
|
||||
#thermal-sensor-cells = <1>;
|
||||
};
|
||||
|
||||
pm855l_adc_tm: adc_tm@3500 {
|
||||
compatible = "qcom,adc-tm5";
|
||||
reg = <0x3500 0x100>;
|
||||
interrupts = <0x4 0x35 0x0 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "thr-int-en";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#thermal-sensor-cells = <1>;
|
||||
io-channels = <&pm855l_vadc ADC_AMUX_THM1_PU2>,
|
||||
<&pm855l_vadc ADC_AMUX_THM2_PU2>,
|
||||
<&pm855l_vadc ADC_AMUX_THM3_PU2>;
|
||||
};
|
||||
};
|
||||
|
||||
qcom,pm855l@5 {
|
||||
|
@ -410,3 +410,111 @@
|
||||
mhi,fw-name = "debug.mbn";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pm855b_adc_tm {
|
||||
wp_therm {
|
||||
reg = <ADC_AMUX_THM2_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
};
|
||||
|
||||
&pm855_adc_tm {
|
||||
xo_therm {
|
||||
reg = <ADC_XO_THERM_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
};
|
||||
|
||||
&pm855l_adc_tm {
|
||||
camera_flash_therm {
|
||||
reg = <ADC_AMUX_THM1_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
|
||||
skin_msm_therm {
|
||||
reg = <ADC_AMUX_THM2_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
|
||||
pa_therm2 {
|
||||
reg = <ADC_AMUX_THM3_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
};
|
||||
|
||||
&thermal_zones {
|
||||
wp-therm {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855b_adc_tm ADC_AMUX_THM2_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
xo-therm {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855_adc_tm ADC_XO_THERM_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
camera-flash-therm {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM1_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
skin-msm-therm {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM2_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
pa-therm2 {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM3_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -414,3 +414,111 @@
|
||||
mhi,fw-name = "debug.mbn";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pm855b_adc_tm {
|
||||
wp_therm {
|
||||
reg = <ADC_AMUX_THM2_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
};
|
||||
|
||||
&pm855_adc_tm {
|
||||
xo_therm {
|
||||
reg = <ADC_XO_THERM_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
};
|
||||
|
||||
&pm855l_adc_tm {
|
||||
camera_flash_therm {
|
||||
reg = <ADC_AMUX_THM1_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
|
||||
skin_msm_therm {
|
||||
reg = <ADC_AMUX_THM2_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
|
||||
pa_therm2 {
|
||||
reg = <ADC_AMUX_THM3_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
};
|
||||
|
||||
&thermal_zones {
|
||||
wp-therm {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855b_adc_tm ADC_AMUX_THM2_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
xo-therm {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855_adc_tm ADC_XO_THERM_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
camera-flash-therm {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM1_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
skin-msm-therm {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM2_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
pa-therm2 {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM3_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -397,3 +397,111 @@
|
||||
&wil6210 {
|
||||
status = "ok";
|
||||
};
|
||||
|
||||
&pm855b_adc_tm {
|
||||
wp_therm {
|
||||
reg = <ADC_AMUX_THM2_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
};
|
||||
|
||||
&pm855_adc_tm {
|
||||
xo_therm {
|
||||
reg = <ADC_XO_THERM_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
};
|
||||
|
||||
&pm855l_adc_tm {
|
||||
camera_flash_therm {
|
||||
reg = <ADC_AMUX_THM1_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
|
||||
skin_msm_therm {
|
||||
reg = <ADC_AMUX_THM2_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
|
||||
pa_therm2 {
|
||||
reg = <ADC_AMUX_THM3_PU2>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
};
|
||||
|
||||
&thermal_zones {
|
||||
wp-therm {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855b_adc_tm ADC_AMUX_THM2_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
xo-therm {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855_adc_tm ADC_XO_THERM_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
camera-flash-therm {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM1_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
skin-msm-therm {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM2_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
pa-therm2 {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
thermal-governor = "user_space";
|
||||
thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM3_PU2>;
|
||||
trips {
|
||||
active-config0 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <1000>;
|
||||
type = "passive";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -93,3 +93,13 @@ config QTI_BCL_SOC_DRIVER
|
||||
threshold and notify the thermal framework.
|
||||
|
||||
If you want this support, you should say Y here.
|
||||
|
||||
config QTI_ADC_TM
|
||||
tristate "Qualcomm Technologies Inc. Thermal Monitor ADC Driver"
|
||||
depends on SPMI && THERMAL
|
||||
depends on QCOM_SPMI_ADC5
|
||||
help
|
||||
This enables the thermal Sysfs driver for the ADC thermal monitoring
|
||||
device. It shows up in Sysfs 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.
|
||||
|
@ -8,3 +8,4 @@ obj-$(CONFIG_QTI_QMI_COOLING_DEVICE) += thermal_mitigation_device_service_v01.o
|
||||
obj-$(CONFIG_MSM_BCL_PERIPHERAL_CTL) += bcl_peripheral.o
|
||||
obj-$(CONFIG_QTI_BCL_PMIC5) += bcl_pmic5.o
|
||||
obj-$(CONFIG_QTI_BCL_SOC_DRIVER) += bcl_soc.o
|
||||
obj-$(CONFIG_QTI_ADC_TM) += adc-tm.o adc-tm-common.o adc-tm5.o
|
||||
|
133
drivers/thermal/qcom/adc-tm-common.c
Normal file
133
drivers/thermal/qcom/adc-tm-common.c
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include "adc-tm.h"
|
||||
|
||||
/*
|
||||
* Voltage to temperature table for NTCG104EF104 thermistor with
|
||||
* 1.875V reference and 100k pull-up.
|
||||
*/
|
||||
static const struct adc_tm_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
|
||||
{ 1831, -40000 },
|
||||
{ 1814, -35000 },
|
||||
{ 1791, -30000 },
|
||||
{ 1761, -25000 },
|
||||
{ 1723, -20000 },
|
||||
{ 1675, -15000 },
|
||||
{ 1616, -10000 },
|
||||
{ 1545, -5000 },
|
||||
{ 1463, 0 },
|
||||
{ 1370, 5000 },
|
||||
{ 1268, 10000 },
|
||||
{ 1160, 15000 },
|
||||
{ 1049, 20000 },
|
||||
{ 937, 25000 },
|
||||
{ 828, 30000 },
|
||||
{ 726, 35000 },
|
||||
{ 630, 40000 },
|
||||
{ 544, 45000 },
|
||||
{ 467, 50000 },
|
||||
{ 399, 55000 },
|
||||
{ 340, 60000 },
|
||||
{ 290, 65000 },
|
||||
{ 247, 70000 },
|
||||
{ 209, 75000 },
|
||||
{ 179, 80000 },
|
||||
{ 153, 85000 },
|
||||
{ 130, 90000 },
|
||||
{ 112, 95000 },
|
||||
{ 96, 100000 },
|
||||
{ 82, 105000 },
|
||||
{ 71, 110000 },
|
||||
{ 62, 115000 },
|
||||
{ 53, 120000 },
|
||||
{ 46, 125000 },
|
||||
};
|
||||
|
||||
static void adc_tm_map_temp_voltage(const struct adc_tm_map_pt *pts,
|
||||
size_t tablesize, int input, int64_t *output)
|
||||
{
|
||||
bool descending = true;
|
||||
unsigned int i = 0;
|
||||
|
||||
/* Check if table is descending or ascending */
|
||||
if (tablesize > 1) {
|
||||
if (pts[0].y < pts[1].y)
|
||||
descending = 0;
|
||||
}
|
||||
|
||||
while (i < tablesize) {
|
||||
if (descending && (pts[i].y < input)) {
|
||||
/*
|
||||
* Table entry is less than measured value.
|
||||
* Table is descending, stop.
|
||||
*/
|
||||
break;
|
||||
} else if (!descending && (pts[i].y > input)) {
|
||||
/*
|
||||
* Table entry is greater than measured value.
|
||||
* Table is ascending, stop.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
*output = pts[0].x;
|
||||
} else if (i == tablesize) {
|
||||
*output = pts[tablesize-1].x;
|
||||
} else {
|
||||
/*
|
||||
* Result is between search_index and search_index-1.
|
||||
* Interpolate linearly.
|
||||
*/
|
||||
*output = (((int32_t) ((pts[i].x - pts[i-1].x) *
|
||||
(input - pts[i-1].y)) /
|
||||
(pts[i].y - pts[i-1].y)) +
|
||||
pts[i-1].x);
|
||||
}
|
||||
}
|
||||
|
||||
void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param,
|
||||
const struct adc_tm_data *data)
|
||||
{
|
||||
uint32_t adc_hc_vdd_ref_mv = 1875;
|
||||
|
||||
/* High temperature maps to lower threshold voltage */
|
||||
adc_tm_map_temp_voltage(
|
||||
adcmap_100k_104ef_104fb_1875_vref,
|
||||
ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
|
||||
param->high_thr_temp, ¶m->low_thr_voltage);
|
||||
|
||||
param->low_thr_voltage *= data->full_scale_code_volt;
|
||||
param->low_thr_voltage = div64_s64(param->low_thr_voltage,
|
||||
adc_hc_vdd_ref_mv);
|
||||
|
||||
/* Low temperature maps to higher threshold voltage */
|
||||
adc_tm_map_temp_voltage(
|
||||
adcmap_100k_104ef_104fb_1875_vref,
|
||||
ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
|
||||
param->low_thr_temp, ¶m->high_thr_voltage);
|
||||
|
||||
param->high_thr_voltage *= data->full_scale_code_volt;
|
||||
param->high_thr_voltage = div64_s64(param->high_thr_voltage,
|
||||
adc_hc_vdd_ref_mv);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(adc_tm_scale_therm_voltage_100k);
|
||||
|
||||
MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC ADC_TM common driver");
|
||||
MODULE_LICENSE("GPL v2");
|
334
drivers/thermal/qcom/adc-tm.c
Normal file
334
drivers/thermal/qcom/adc-tm.c
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include "adc-tm.h"
|
||||
|
||||
LIST_HEAD(adc_tm_device_list);
|
||||
|
||||
static int adc_tm_get_temp(void *data, int *temp)
|
||||
{
|
||||
struct adc_tm_sensor *s = data;
|
||||
struct adc_tm_chip *adc_tm = s->chip;
|
||||
|
||||
return adc_tm->ops->get_temp(s, temp);
|
||||
}
|
||||
|
||||
static int adc_tm_set_trip_temp(void *data, int low_temp, int high_temp)
|
||||
{
|
||||
struct adc_tm_sensor *s = data;
|
||||
struct adc_tm_chip *adc_tm = s->chip;
|
||||
|
||||
if (adc_tm->ops->set_trips)
|
||||
return adc_tm->ops->set_trips(s, low_temp, high_temp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_tm_register_interrupts(struct adc_tm_chip *adc_tm)
|
||||
{
|
||||
if (adc_tm->ops->interrupts_reg)
|
||||
return adc_tm->ops->interrupts_reg(adc_tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_tm_init(struct adc_tm_chip *adc_tm, uint32_t dt_chans)
|
||||
{
|
||||
if (adc_tm->ops->init)
|
||||
return adc_tm->ops->init(adc_tm, dt_chans);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_zone_of_device_ops adc_tm_ops = {
|
||||
.get_temp = adc_tm_get_temp,
|
||||
.set_trips = adc_tm_set_trip_temp,
|
||||
};
|
||||
|
||||
static int adc_tm_register_tzd(struct adc_tm_chip *adc_tm, int dt_chan_num)
|
||||
{
|
||||
unsigned int i;
|
||||
struct thermal_zone_device *tzd;
|
||||
|
||||
for (i = 0; i < dt_chan_num; i++) {
|
||||
adc_tm->sensor[i].chip = adc_tm;
|
||||
tzd = devm_thermal_zone_of_sensor_register(adc_tm->dev,
|
||||
adc_tm->sensor[i].adc_ch,
|
||||
&adc_tm->sensor[i],
|
||||
&adc_tm_ops);
|
||||
if (IS_ERR(tzd)) {
|
||||
pr_err("Error registering TZ zone:%d for dt_ch:%d\n",
|
||||
PTR_ERR(tzd), adc_tm->sensor[i].adc_ch);
|
||||
continue;
|
||||
}
|
||||
adc_tm->sensor[i].tzd = tzd;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_tm_avg_samples_from_dt(u32 value)
|
||||
{
|
||||
if (!is_power_of_2(value) || value > ADC_TM_AVG_SAMPLES_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
return __ffs64(value);
|
||||
}
|
||||
|
||||
static int adc_tm_hw_settle_time_from_dt(u32 value,
|
||||
const unsigned int *hw_settle)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ADC_TM_HW_SETTLE_SAMPLES_MAX; i++) {
|
||||
if (value == hw_settle[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adc_tm_decimation_from_dt(u32 value, const unsigned int *decimation)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ADC_TM_DECIMATION_SAMPLES_MAX; i++) {
|
||||
if (value == decimation[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct of_device_id adc_tm_match_table[] = {
|
||||
{
|
||||
.compatible = "qcom,adc-tm5",
|
||||
.data = &data_adc_tm5,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int adc_tm_get_dt_data(struct platform_device *pdev,
|
||||
struct adc_tm_chip *adc_tm,
|
||||
struct iio_channel *chan,
|
||||
uint32_t dt_chan_num)
|
||||
{
|
||||
struct device_node *child, *node = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *id;
|
||||
const struct adc_tm_data *data;
|
||||
int ret, idx = 0;
|
||||
|
||||
if (!node)
|
||||
return -EINVAL;
|
||||
|
||||
id = of_match_node(adc_tm_match_table, node);
|
||||
if (id)
|
||||
data = id->data;
|
||||
else
|
||||
data = &data_adc_tm5;
|
||||
adc_tm->data = data;
|
||||
adc_tm->ops = data->ops;
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,decimation",
|
||||
&adc_tm->prop.decimation);
|
||||
if (!ret) {
|
||||
ret = adc_tm_decimation_from_dt(adc_tm->prop.decimation,
|
||||
data->decimation);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Invalid decimation value\n");
|
||||
return ret;
|
||||
}
|
||||
adc_tm->prop.decimation = ret;
|
||||
} else {
|
||||
adc_tm->prop.decimation = ADC_TM_DECIMATION_DEFAULT;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,avg-samples",
|
||||
&adc_tm->prop.fast_avg_samples);
|
||||
if (!ret) {
|
||||
ret = adc_tm_avg_samples_from_dt(adc_tm->prop.fast_avg_samples);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Invalid fast average with%d\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
adc_tm->prop.fast_avg_samples = ADC_TM_DEF_AVG_SAMPLES;
|
||||
}
|
||||
|
||||
adc_tm->prop.timer1 = ADC_TM_TIMER1;
|
||||
adc_tm->prop.timer2 = ADC_TM_TIMER2;
|
||||
adc_tm->prop.timer3 = ADC_TM_TIMER3;
|
||||
|
||||
for_each_child_of_node(node, child) {
|
||||
int channel_num, i = 0;
|
||||
int calib_type = 0, ret, hw_settle_time = 0;
|
||||
struct iio_channel *chan_adc;
|
||||
|
||||
ret = of_property_read_u32(child, "reg", &channel_num);
|
||||
if (ret) {
|
||||
dev_err(dev, "Invalid channel num\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(child, "qcom,hw-settle-time",
|
||||
&hw_settle_time);
|
||||
if (!ret) {
|
||||
ret = adc_tm_hw_settle_time_from_dt(hw_settle_time,
|
||||
data->hw_settle);
|
||||
if (ret < 0) {
|
||||
pr_err("Invalid channel hw settle time property\n");
|
||||
return ret;
|
||||
}
|
||||
hw_settle_time = ret;
|
||||
} else {
|
||||
hw_settle_time = ADC_TM_DEF_HW_SETTLE_TIME;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(child, "qcom,ratiometric"))
|
||||
calib_type = ADC_RATIO_CAL;
|
||||
else
|
||||
calib_type = ADC_ABS_CAL;
|
||||
|
||||
/* Individual channel properties */
|
||||
adc_tm->sensor[idx].adc_ch = channel_num;
|
||||
adc_tm->sensor[idx].cal_sel = calib_type;
|
||||
/* Default to 1 second timer select */
|
||||
adc_tm->sensor[idx].timer_select = ADC_TIMER_SEL_2;
|
||||
adc_tm->sensor[idx].hw_settle_time = hw_settle_time;
|
||||
while (i < dt_chan_num) {
|
||||
chan_adc = &chan[i];
|
||||
if (chan_adc->channel->channel == channel_num)
|
||||
adc_tm->sensor[idx].adc = chan_adc;
|
||||
i++;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_tm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *child, *node = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct adc_tm_chip *adc_tm;
|
||||
struct regmap *regmap;
|
||||
struct iio_channel *channels;
|
||||
int ret, dt_chan_num = 0, indio_chan_count = 0;
|
||||
u32 reg;
|
||||
|
||||
if (!node)
|
||||
return -EINVAL;
|
||||
|
||||
for_each_child_of_node(node, child)
|
||||
dt_chan_num++;
|
||||
|
||||
if (!dt_chan_num) {
|
||||
dev_err(dev, "No channel listing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
channels = iio_channel_get_all(dev);
|
||||
if (IS_ERR(channels))
|
||||
return PTR_ERR(channels);
|
||||
|
||||
while (channels[indio_chan_count].indio_dev)
|
||||
indio_chan_count++;
|
||||
|
||||
if (indio_chan_count != dt_chan_num) {
|
||||
dev_err(dev, "VADC IIO channel missing in main node\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!regmap)
|
||||
return -ENODEV;
|
||||
|
||||
ret = of_property_read_u32(node, "reg", ®);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
adc_tm = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct adc_tm_chip) + (dt_chan_num *
|
||||
(sizeof(struct adc_tm_sensor))), GFP_KERNEL);
|
||||
if (!adc_tm)
|
||||
return -ENOMEM;
|
||||
|
||||
adc_tm->regmap = regmap;
|
||||
adc_tm->dev = dev;
|
||||
adc_tm->base = reg;
|
||||
adc_tm->dt_channels = dt_chan_num;
|
||||
|
||||
ret = adc_tm_get_dt_data(pdev, adc_tm, channels, dt_chan_num);
|
||||
if (ret) {
|
||||
dev_err(dev, "adc-tm get dt data failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = adc_tm_init(adc_tm, dt_chan_num);
|
||||
if (ret) {
|
||||
dev_err(dev, "adc-tm init failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = adc_tm_register_tzd(adc_tm, dt_chan_num);
|
||||
if (ret) {
|
||||
dev_err(dev, "adc-tm failed to register with of thermal\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = adc_tm_register_interrupts(adc_tm);
|
||||
if (ret) {
|
||||
pr_err("adc-tm register interrupts failed:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_add_tail(&adc_tm->list, &adc_tm_device_list);
|
||||
platform_set_drvdata(pdev, adc_tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_tm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct adc_tm_chip *adc_tm = platform_get_drvdata(pdev);
|
||||
|
||||
if (adc_tm->ops->shutdown)
|
||||
adc_tm->ops->shutdown(adc_tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver adc_tm_driver = {
|
||||
.driver = {
|
||||
.name = "qcom,adc-tm",
|
||||
.of_match_table = adc_tm_match_table,
|
||||
},
|
||||
.probe = adc_tm_probe,
|
||||
.remove = adc_tm_remove,
|
||||
};
|
||||
module_platform_driver(adc_tm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC ADC_TM driver");
|
||||
MODULE_LICENSE("GPL v2");
|
218
drivers/thermal/qcom/adc-tm.h
Normal file
218
drivers/thermal/qcom/adc-tm.h
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef __QCOM_ADC_TM_H__
|
||||
#define __QCOM_ADC_TM_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
|
||||
struct adc_tm_chip;
|
||||
|
||||
#define ADC_TM_DECIMATION_DEFAULT 840
|
||||
#define ADC_TM_DECIMATION_SAMPLES_MAX 3
|
||||
#define ADC_TM_DEF_AVG_SAMPLES 0 /* 1 sample */
|
||||
#define ADC_TM_DEF_HW_SETTLE_TIME 0 /* 15 us */
|
||||
#define ADC_TM_HW_SETTLE_SAMPLES_MAX 16
|
||||
#define ADC_TM_AVG_SAMPLES_MAX 16
|
||||
#define ADC_TM_TIMER1 3 /* 3.9ms */
|
||||
#define ADC_TM_TIMER2 10 /* 1 second */
|
||||
#define ADC_TM_TIMER3 4 /* 4 second */
|
||||
|
||||
enum adc_cal_method {
|
||||
ADC_NO_CAL = 0,
|
||||
ADC_RATIO_CAL = 1,
|
||||
ADC_ABS_CAL = 2,
|
||||
ADC_CAL_SEL_NONE,
|
||||
};
|
||||
|
||||
enum adc_cal_val {
|
||||
ADC_TIMER_CAL = 0,
|
||||
ADC_NEW_CAL,
|
||||
ADC_CAL_VAL_NONE,
|
||||
};
|
||||
|
||||
enum adc_timer_select {
|
||||
ADC_TIMER_SEL_1 = 0,
|
||||
ADC_TIMER_SEL_2,
|
||||
ADC_TIMER_SEL_3,
|
||||
ADC_TIMER_SEL_NONE,
|
||||
};
|
||||
|
||||
struct adc_tm_sensor {
|
||||
struct adc_tm_chip *chip;
|
||||
struct thermal_zone_device *tzd;
|
||||
enum adc_cal_val cal_val;
|
||||
enum adc_cal_method cal_sel;
|
||||
unsigned int hw_settle_time;
|
||||
unsigned int adc_ch;
|
||||
unsigned int btm_ch;
|
||||
unsigned int prescaling;
|
||||
unsigned int timer_select;
|
||||
struct iio_channel *adc;
|
||||
};
|
||||
|
||||
struct adc_tm_cmn_prop {
|
||||
unsigned int decimation;
|
||||
unsigned int fast_avg_samples;
|
||||
unsigned int timer1;
|
||||
unsigned int timer2;
|
||||
unsigned int timer3;
|
||||
};
|
||||
|
||||
struct adc_tm_ops {
|
||||
int (*get_temp)(struct adc_tm_sensor *, int *);
|
||||
int (*init)(struct adc_tm_chip *, uint32_t);
|
||||
int (*set_trips)(struct adc_tm_sensor *, int, int);
|
||||
int (*interrupts_reg)(struct adc_tm_chip *);
|
||||
int (*shutdown)(struct adc_tm_chip *);
|
||||
};
|
||||
|
||||
struct adc_tm_chip {
|
||||
struct device *dev;
|
||||
struct list_head list;
|
||||
struct regmap *regmap;
|
||||
u16 base;
|
||||
struct adc_tm_cmn_prop prop;
|
||||
spinlock_t adc_tm_lock;
|
||||
const struct adc_tm_ops *ops;
|
||||
const struct adc_tm_data *data;
|
||||
unsigned int dt_channels;
|
||||
struct adc_tm_sensor sensor[0];
|
||||
};
|
||||
|
||||
struct adc_tm_data {
|
||||
const struct adc_tm_ops *ops;
|
||||
const u32 full_scale_code_volt;
|
||||
unsigned int *decimation;
|
||||
unsigned int *hw_settle;
|
||||
};
|
||||
|
||||
extern const struct adc_tm_data data_adc_tm5;
|
||||
/**
|
||||
* Channel index for the corresponding index to adc_tm_channel_select
|
||||
*/
|
||||
enum adc_tm_channel_num {
|
||||
ADC_TM_CHAN0 = 0,
|
||||
ADC_TM_CHAN1,
|
||||
ADC_TM_CHAN2,
|
||||
ADC_TM_CHAN3,
|
||||
ADC_TM_CHAN4,
|
||||
ADC_TM_CHAN5,
|
||||
ADC_TM_CHAN6,
|
||||
ADC_TM_CHAN7,
|
||||
ADC_TM_CHAN_NONE
|
||||
};
|
||||
|
||||
/**
|
||||
* Channel selection registers for each of the configurable measurements
|
||||
* Channels allotment is set at device config for a channel.
|
||||
*/
|
||||
enum adc_tm_channel_sel {
|
||||
ADC_TM_M0_ADC_CH_SEL_CTL = 0x60,
|
||||
ADC_TM_M1_ADC_CH_SEL_CTL = 0x68,
|
||||
ADC_TM_M2_ADC_CH_SEL_CTL = 0x70,
|
||||
ADC_TM_M3_ADC_CH_SEL_CTL = 0x78,
|
||||
ADC_TM_M4_ADC_CH_SEL_CTL = 0x80,
|
||||
ADC_TM_M5_ADC_CH_SEL_CTL = 0x88,
|
||||
ADC_TM_M6_ADC_CH_SEL_CTL = 0x90,
|
||||
ADC_TM_M7_ADC_CH_SEL_CTL = 0x98,
|
||||
ADC_TM_CH_SELECT_NONE
|
||||
};
|
||||
|
||||
/**
|
||||
* enum adc_tm_fast_avg_ctl - Provides ability to obtain single result
|
||||
* from the ADC that is an average of multiple measurement
|
||||
* samples. Select number of samples for use in fast
|
||||
* average mode (i.e. 2 ^ value).
|
||||
* %ADC_FAST_AVG_SAMPLE_1: 0x0 = 1
|
||||
* %ADC_FAST_AVG_SAMPLE_2: 0x1 = 2
|
||||
* %ADC_FAST_AVG_SAMPLE_4: 0x2 = 4
|
||||
* %ADC_FAST_AVG_SAMPLE_8: 0x3 = 8
|
||||
* %ADC_FAST_AVG_SAMPLE_16: 0x4 = 16
|
||||
*/
|
||||
enum qpnp_adc_fast_avg_ctl {
|
||||
ADC_FAST_AVG_SAMPLE_1 = 0,
|
||||
ADC_FAST_AVG_SAMPLE_2,
|
||||
ADC_FAST_AVG_SAMPLE_4,
|
||||
ADC_FAST_AVG_SAMPLE_8,
|
||||
ADC_FAST_AVG_SAMPLE_16,
|
||||
ADC_FAST_AVG_SAMPLE_NONE,
|
||||
};
|
||||
|
||||
struct adc_tm_trip_reg_type {
|
||||
enum adc_tm_channel_sel btm_amux_ch;
|
||||
uint16_t low_thr_lsb_addr;
|
||||
uint16_t low_thr_msb_addr;
|
||||
uint16_t high_thr_lsb_addr;
|
||||
uint16_t high_thr_msb_addr;
|
||||
u8 multi_meas_en;
|
||||
u8 low_thr_int_chan_en;
|
||||
u8 high_thr_int_chan_en;
|
||||
u8 meas_interval_ctl;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adc_tm_config - Represent ADC Thermal Monitor configuration.
|
||||
* @channel: ADC channel for which thermal monitoring is requested.
|
||||
* @adc_code: The pre-calibrated digital output of a given ADC releative to the
|
||||
* ADC reference.
|
||||
* @high_thr_temp: Temperature at which high threshold notification is required.
|
||||
* @low_thr_temp: Temperature at which low threshold notification is required.
|
||||
* @low_thr_voltage : Low threshold voltage ADC code used for reverse
|
||||
* calibration.
|
||||
* @high_thr_voltage: High threshold voltage ADC code used for reverse
|
||||
* calibration.
|
||||
*/
|
||||
struct adc_tm_config {
|
||||
int channel;
|
||||
int adc_code;
|
||||
int high_thr_temp;
|
||||
int low_thr_temp;
|
||||
int64_t high_thr_voltage;
|
||||
int64_t low_thr_voltage;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adc_map_pt - Map the graph representation for ADC channel
|
||||
* @x: Represent the ADC digitized code.
|
||||
* @y: Represent the physical data which can be temperature, voltage,
|
||||
* resistance.
|
||||
*/
|
||||
struct adc_tm_map_pt {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adc_linear_graph - Represent ADC characteristics.
|
||||
* @dy: numerator slope to calculate the gain.
|
||||
* @dx: denominator slope to calculate the gain.
|
||||
* @gnd: A/D word of the ground reference used for the channel.
|
||||
*
|
||||
* Each ADC device has different offset and gain parameters which are
|
||||
* computed to calibrate the device.
|
||||
*/
|
||||
struct adc_tm_linear_graph {
|
||||
s32 dy;
|
||||
s32 dx;
|
||||
s32 gnd;
|
||||
};
|
||||
|
||||
void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param,
|
||||
const struct adc_tm_data *data);
|
||||
|
||||
#endif /* __QCOM_ADC_TM_H__ */
|
573
drivers/thermal/qcom/adc-tm5.c
Normal file
573
drivers/thermal/qcom/adc-tm5.c
Normal file
@ -0,0 +1,573 @@
|
||||
/*
|
||||
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spmi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include "adc-tm.h"
|
||||
#include "../thermal_core.h"
|
||||
|
||||
#define ADC_TM_STATUS2 0x09
|
||||
#define ADC_TM_STATUS_LOW 0x0a
|
||||
#define ADC_TM_STATUS_HIGH 0x0b
|
||||
#define ADC_TM_NUM_BTM 0x0f
|
||||
|
||||
#define ADC_TM_ADC_DIG_PARAM 0x42
|
||||
#define ADC_TM_FAST_AVG_CTL 0x43
|
||||
#define ADC_TM_FAST_AVG_EN BIT(7)
|
||||
|
||||
#define ADC_TM_MEAS_INTERVAL_CTL 0x44
|
||||
#define ADC_TM_MEAS_INTERVAL_CTL2 0x45
|
||||
|
||||
#define ADC_TM_MEAS_INTERVAL_CTL2_SHIFT 0x4
|
||||
#define ADC_TM_MEAS_INTERVAL_CTL2_MASK 0xf0
|
||||
#define ADC_TM_MEAS_INTERVAL_CTL3_MASK 0xf
|
||||
|
||||
#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_TM_Mn_ADC_CH_SEL_CTL(n) ((n * 8) + 0x60)
|
||||
#define ADC_TM_Mn_LOW_THR0(n) ((n * 8) + 0x61)
|
||||
#define ADC_TM_Mn_LOW_THR1(n) ((n * 8) + 0x62)
|
||||
#define ADC_TM_Mn_HIGH_THR0(n) ((n * 8) + 0x63)
|
||||
#define ADC_TM_Mn_HIGH_THR1(n) ((n * 8) + 0x64)
|
||||
#define ADC_TM_Mn_MEAS_INTERVAL_CTL(n) ((n * 8) + 0x65)
|
||||
#define ADC_TM_Mn_CTL(n) ((n * 8) + 0x66)
|
||||
#define ADC_TM_CTL_HW_SETTLE_DELAY_MASK 0xf
|
||||
#define ADC_TM_CTL_CAL_SEL 0x30
|
||||
#define ADC_TM_CTL_CAL_SEL_MASK_SHIFT 4
|
||||
#define ADC_TM_CTL_CAL_VAL 0x40
|
||||
|
||||
#define ADC_TM_Mn_EN(n) ((n * 8) + 0x67)
|
||||
#define ADC_TM_Mn_MEAS_EN BIT(7)
|
||||
#define ADC_TM_Mn_HIGH_THR_INT_EN BIT(1)
|
||||
#define ADC_TM_Mn_LOW_THR_INT_EN BIT(0)
|
||||
#define ADC_TM_LOWER_MASK(n) ((n) & 0x000000ff)
|
||||
#define ADC_TM_UPPER_MASK(n) (((n) & 0xffffff00) >> 8)
|
||||
|
||||
static struct adc_tm_trip_reg_type adc_tm_ch_data[] = {
|
||||
[ADC_TM_CHAN0] = {ADC_TM_M0_ADC_CH_SEL_CTL},
|
||||
[ADC_TM_CHAN1] = {ADC_TM_M1_ADC_CH_SEL_CTL},
|
||||
[ADC_TM_CHAN2] = {ADC_TM_M2_ADC_CH_SEL_CTL},
|
||||
[ADC_TM_CHAN3] = {ADC_TM_M3_ADC_CH_SEL_CTL},
|
||||
[ADC_TM_CHAN4] = {ADC_TM_M4_ADC_CH_SEL_CTL},
|
||||
[ADC_TM_CHAN5] = {ADC_TM_M5_ADC_CH_SEL_CTL},
|
||||
[ADC_TM_CHAN6] = {ADC_TM_M6_ADC_CH_SEL_CTL},
|
||||
[ADC_TM_CHAN7] = {ADC_TM_M7_ADC_CH_SEL_CTL},
|
||||
};
|
||||
|
||||
static int adc_tm5_get_temp(struct adc_tm_sensor *sensor, int *temp)
|
||||
{
|
||||
int ret, milli_celsius;
|
||||
|
||||
if (!sensor || !sensor->adc)
|
||||
return -EINVAL;
|
||||
|
||||
ret = iio_read_channel_processed(sensor->adc, &milli_celsius);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*temp = milli_celsius;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t adc_tm5_read_reg(struct adc_tm_chip *chip,
|
||||
int16_t reg, u8 *data, int len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(chip->regmap, (chip->base + reg), data, len);
|
||||
if (ret < 0)
|
||||
pr_err("adc-tm read reg %d failed with %d\n", reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int32_t adc_tm5_write_reg(struct adc_tm_chip *chip,
|
||||
int16_t reg, u8 *data, int len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_write(chip->regmap, (chip->base + reg), data, len);
|
||||
if (ret < 0)
|
||||
pr_err("adc-tm write reg %d failed with %d\n", reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int32_t adc_tm5_reg_update(struct adc_tm_chip *chip,
|
||||
uint16_t addr, u8 mask, bool state)
|
||||
{
|
||||
u8 reg_value = 0;
|
||||
int ret;
|
||||
|
||||
ret = adc_tm5_read_reg(chip, addr, ®_value, 1);
|
||||
if (ret < 0) {
|
||||
pr_err("read failed for addr:0x%x\n", addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg_value = reg_value & ~mask;
|
||||
if (state)
|
||||
reg_value |= mask;
|
||||
|
||||
pr_debug("state:%d, reg:0x%x with bits:0x%x and mask:0x%x\n",
|
||||
state, addr, reg_value, ~mask);
|
||||
ret = adc_tm5_write_reg(chip, addr, ®_value, 1);
|
||||
if (ret < 0) {
|
||||
pr_err("write failed for addr:%x\n", addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int32_t adc_tm5_get_btm_idx(struct adc_tm_chip *chip,
|
||||
uint32_t btm_chan, uint32_t *btm_chan_idx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ADC_TM_CHAN_NONE; i++) {
|
||||
if (adc_tm_ch_data[i].btm_amux_ch == btm_chan) {
|
||||
*btm_chan_idx = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int32_t adc_tm5_enable(struct adc_tm_chip *chip)
|
||||
{
|
||||
int rc = 0;
|
||||
u8 data = 0;
|
||||
|
||||
data = ADC_TM_EN;
|
||||
rc = adc_tm5_write_reg(chip, ADC_TM_EN_CTL1, &data, 1);
|
||||
if (rc < 0) {
|
||||
pr_err("adc-tm enable failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
data = ADC_TM_CONV_REQ_EN;
|
||||
rc = adc_tm5_write_reg(chip, ADC_TM_CONV_REQ, &data, 1);
|
||||
if (rc < 0) {
|
||||
pr_err("adc-tm request conversion failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int adc_tm5_configure(struct adc_tm_sensor *sensor,
|
||||
uint32_t btm_chan_idx)
|
||||
{
|
||||
struct adc_tm_chip *chip = sensor->chip;
|
||||
u8 buf[8], cal_sel;
|
||||
int ret = 0;
|
||||
|
||||
ret = adc_tm5_read_reg(chip,
|
||||
ADC_TM_Mn_ADC_CH_SEL_CTL(btm_chan_idx), buf, 8);
|
||||
if (ret < 0) {
|
||||
pr_err("adc-tm block read failed with %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Update ADC channel select */
|
||||
buf[0] = sensor->adc_ch;
|
||||
|
||||
/* Update timer select */
|
||||
buf[5] = sensor->timer_select;
|
||||
|
||||
/* Set calibration select, hw_settle delay */
|
||||
cal_sel |= (u8) (sensor->cal_sel << ADC_TM_CTL_CAL_SEL_MASK_SHIFT);
|
||||
buf[6] &= (u8) ~ADC_TM_CTL_HW_SETTLE_DELAY_MASK;
|
||||
buf[6] |= (u8) sensor->hw_settle_time;
|
||||
buf[6] &= (u8) ~ADC_TM_CTL_CAL_SEL;
|
||||
buf[6] |= (u8) cal_sel;
|
||||
|
||||
buf[7] |= ADC_TM_Mn_MEAS_EN;
|
||||
|
||||
ret = adc_tm5_write_reg(chip,
|
||||
ADC_TM_Mn_ADC_CH_SEL_CTL(btm_chan_idx), buf, 8);
|
||||
if (ret < 0) {
|
||||
pr_err("adc-tm block write failed with %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_tm5_set_mode(struct adc_tm_sensor *sensor,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
struct adc_tm_chip *chip = sensor->chip;
|
||||
int ret = 0;
|
||||
uint32_t btm_chan_idx = 0;
|
||||
|
||||
ret = adc_tm5_get_btm_idx(chip, sensor->btm_ch, &btm_chan_idx);
|
||||
if (ret < 0) {
|
||||
pr_err("Invalid btm channel idx with %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (mode == THERMAL_DEVICE_ENABLED) {
|
||||
ret = adc_tm5_configure(sensor, btm_chan_idx);
|
||||
if (ret < 0) {
|
||||
pr_err("Error during adc-tm configure:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = adc_tm5_enable(chip);
|
||||
if (ret < 0)
|
||||
pr_err("Error enabling adc-tm with %d\n", ret);
|
||||
|
||||
} else if (mode == THERMAL_DEVICE_DISABLED) {
|
||||
ret = adc_tm5_reg_update(chip,
|
||||
ADC_TM_Mn_EN(btm_chan_idx),
|
||||
ADC_TM_Mn_MEAS_EN, false);
|
||||
if (ret < 0)
|
||||
pr_err("Disable failed for ch:%d\n", btm_chan_idx);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc_tm5_activate_trip_type(struct adc_tm_sensor *adc_tm,
|
||||
int trip, enum thermal_device_mode mode)
|
||||
{
|
||||
struct adc_tm_chip *chip = adc_tm->chip;
|
||||
int ret = 0;
|
||||
bool state = false;
|
||||
uint32_t btm_chan_idx = 0, btm_chan = 0;
|
||||
|
||||
if (mode == THERMAL_DEVICE_ENABLED)
|
||||
state = true;
|
||||
|
||||
btm_chan = adc_tm->btm_ch;
|
||||
ret = adc_tm5_get_btm_idx(chip, btm_chan, &btm_chan_idx);
|
||||
if (ret < 0) {
|
||||
pr_err("Invalid btm channel idx\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (trip) {
|
||||
case THERMAL_TRIP_CONFIGURABLE_HI:
|
||||
/* low_thr (lower voltage) for higher temp */
|
||||
ret = adc_tm5_reg_update(chip,
|
||||
ADC_TM_Mn_EN(btm_chan_idx),
|
||||
ADC_TM_Mn_LOW_THR_INT_EN, state);
|
||||
if (ret)
|
||||
pr_err("channel:%x failed\n", btm_chan);
|
||||
break;
|
||||
case THERMAL_TRIP_CONFIGURABLE_LOW:
|
||||
/* high_thr (higher voltage) for cooler temp */
|
||||
ret = adc_tm5_reg_update(chip,
|
||||
ADC_TM_Mn_EN(btm_chan_idx),
|
||||
ADC_TM_Mn_HIGH_THR_INT_EN, state);
|
||||
if (ret)
|
||||
pr_err("channel:%x failed\n", btm_chan);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc_tm5_set_trip_temp(struct adc_tm_sensor *sensor,
|
||||
int low_temp, int high_temp)
|
||||
{
|
||||
struct adc_tm_chip *chip = sensor->chip;
|
||||
struct adc_tm_config tm_config;
|
||||
u8 trip_low_thr[2], trip_high_thr[2];
|
||||
uint16_t reg_low_thr_lsb, reg_high_thr_lsb;
|
||||
int ret;
|
||||
uint32_t btm_chan = 0, btm_chan_idx = 0, mask = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (!sensor)
|
||||
return -EINVAL;
|
||||
|
||||
pr_debug("%s:low_temp(mdegC):%d, high_temp(mdegC):%d\n", __func__,
|
||||
low_temp, high_temp);
|
||||
|
||||
tm_config.channel = sensor->adc_ch;
|
||||
tm_config.high_thr_temp = tm_config.low_thr_temp = 0;
|
||||
if (high_temp != INT_MAX)
|
||||
tm_config.high_thr_temp = high_temp;
|
||||
if (low_temp != INT_MIN)
|
||||
tm_config.low_thr_temp = low_temp;
|
||||
|
||||
if ((high_temp == INT_MAX) && (low_temp == INT_MIN)) {
|
||||
pr_err("No trips to set\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("requested a low temp- %d and high temp- %d\n",
|
||||
tm_config.low_thr_temp, tm_config.high_thr_temp);
|
||||
adc_tm_scale_therm_voltage_100k(&tm_config, chip->data);
|
||||
|
||||
/* Cool temperature corresponds to high voltage threshold */
|
||||
mask = lower_32_bits(tm_config.high_thr_voltage);
|
||||
trip_high_thr[0] = ADC_TM_LOWER_MASK(mask);
|
||||
trip_high_thr[1] = ADC_TM_UPPER_MASK(mask);
|
||||
/* Warm temperature corresponds to low voltage threshold */
|
||||
mask = lower_32_bits(tm_config.low_thr_voltage);
|
||||
trip_low_thr[0] = ADC_TM_LOWER_MASK(mask);
|
||||
trip_low_thr[1] = ADC_TM_UPPER_MASK(mask);
|
||||
|
||||
pr_debug("high_thr:0x%llx, low_thr:0x%llx\n",
|
||||
tm_config.high_thr_voltage, tm_config.low_thr_voltage);
|
||||
|
||||
btm_chan = sensor->btm_ch;
|
||||
ret = adc_tm5_get_btm_idx(chip, btm_chan, &btm_chan_idx);
|
||||
if (ret < 0) {
|
||||
pr_err("Invalid btm channel idx\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&chip->adc_tm_lock, flags);
|
||||
|
||||
reg_low_thr_lsb = ADC_TM_Mn_LOW_THR0(btm_chan_idx);
|
||||
reg_high_thr_lsb = ADC_TM_Mn_HIGH_THR0(btm_chan_idx);
|
||||
|
||||
if (high_temp != INT_MAX) {
|
||||
ret = adc_tm5_write_reg(chip, reg_low_thr_lsb,
|
||||
trip_low_thr, 2);
|
||||
if (ret) {
|
||||
pr_err("Warm set threshold err\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = adc_tm5_activate_trip_type(sensor,
|
||||
THERMAL_TRIP_CONFIGURABLE_HI,
|
||||
THERMAL_DEVICE_ENABLED);
|
||||
if (ret) {
|
||||
pr_err("adc-tm warm activation failed\n");
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
ret = adc_tm5_activate_trip_type(sensor,
|
||||
THERMAL_TRIP_CONFIGURABLE_HI,
|
||||
THERMAL_DEVICE_DISABLED);
|
||||
if (ret) {
|
||||
pr_err("adc-tm warm deactivation failed\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (low_temp != INT_MIN) {
|
||||
ret = adc_tm5_write_reg(chip, reg_high_thr_lsb,
|
||||
trip_high_thr, 2);
|
||||
if (ret) {
|
||||
pr_err("adc-tm cool temp set threshold err\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = adc_tm5_activate_trip_type(sensor,
|
||||
THERMAL_TRIP_CONFIGURABLE_LOW,
|
||||
THERMAL_DEVICE_ENABLED);
|
||||
if (ret) {
|
||||
pr_err("adc-tm cool activation failed\n");
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
ret = adc_tm5_activate_trip_type(sensor,
|
||||
THERMAL_TRIP_CONFIGURABLE_LOW,
|
||||
THERMAL_DEVICE_DISABLED);
|
||||
if (ret) {
|
||||
pr_err("adc-tm cool deactivation failed\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if ((high_temp != INT_MAX) || (low_temp != INT_MIN)) {
|
||||
ret = adc_tm5_set_mode(sensor, THERMAL_DEVICE_ENABLED);
|
||||
if (ret)
|
||||
pr_err("sensor enabled failed\n");
|
||||
} else {
|
||||
ret = adc_tm5_set_mode(sensor, THERMAL_DEVICE_DISABLED);
|
||||
if (ret)
|
||||
pr_err("sensor disable failed\n");
|
||||
}
|
||||
|
||||
fail:
|
||||
spin_unlock_irqrestore(&chip->adc_tm_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t adc_tm5_handler(int irq, void *data)
|
||||
{
|
||||
struct adc_tm_chip *chip = data;
|
||||
u8 status_low, status_high, ctl;
|
||||
int ret, i = 0;
|
||||
unsigned long flags;
|
||||
|
||||
ret = adc_tm5_read_reg(chip, ADC_TM_STATUS_LOW, &status_low, 1);
|
||||
if (ret < 0) {
|
||||
pr_err("adc-tm-tm read status low failed with %d\n", ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
ret = adc_tm5_read_reg(chip, ADC_TM_STATUS_HIGH, &status_high, 1);
|
||||
if (ret < 0) {
|
||||
pr_err("adc-tm-tm read status high failed with %d\n", ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
while (i < chip->dt_channels) {
|
||||
bool upper_set = false, lower_set = false;
|
||||
int temp;
|
||||
|
||||
if (IS_ERR(chip->sensor[i].tzd))
|
||||
continue;
|
||||
|
||||
ret = adc_tm5_get_temp(&chip->sensor[i], &temp);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
spin_lock_irqsave(&chip->adc_tm_lock, flags);
|
||||
|
||||
ret = adc_tm5_read_reg(chip, ADC_TM_Mn_EN(i), &ctl, 1);
|
||||
if (ret) {
|
||||
pr_err("ctl read failed with %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((status_low & 0x1) && (ctl & ADC_TM_Mn_MEAS_EN)
|
||||
&& (ctl & ADC_TM_Mn_LOW_THR_INT_EN))
|
||||
lower_set = true;
|
||||
|
||||
if ((status_high & 0x1) && (ctl & ADC_TM_Mn_MEAS_EN) &&
|
||||
(ctl & ADC_TM_Mn_HIGH_THR_INT_EN))
|
||||
upper_set = true;
|
||||
fail:
|
||||
status_low >>= 1;
|
||||
status_high >>= 1;
|
||||
spin_unlock_irqrestore(&chip->adc_tm_lock, flags);
|
||||
if (upper_set || lower_set) {
|
||||
/*
|
||||
* Expected behavior is while notifying of_thermal,
|
||||
* thermal core will call set_trips with new thresholds
|
||||
* and activate/disable the appropriate trips.
|
||||
*/
|
||||
pr_debug("notifying of_thermal\n");
|
||||
of_thermal_handle_trip(chip->sensor[i].tzd);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int adc_tm5_register_interrupts(struct adc_tm_chip *chip)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
int ret, irq;
|
||||
|
||||
if (!chip)
|
||||
return -EINVAL;
|
||||
|
||||
pdev = to_platform_device(chip->dev);
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "thr-int-en");
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get irq %s\n",
|
||||
"thr-int-en");
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
adc_tm5_handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
"thr-int-en", chip);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get irq %s\n",
|
||||
"thr-int-en");
|
||||
return ret;
|
||||
}
|
||||
|
||||
enable_irq_wake(irq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc_tm5_init(struct adc_tm_chip *chip, uint32_t dt_chans)
|
||||
{
|
||||
u8 buf[4], channels_available;
|
||||
int ret, i;
|
||||
|
||||
ret = adc_tm5_read_reg(chip, ADC_TM_NUM_BTM, &channels_available, 1);
|
||||
if (ret < 0) {
|
||||
pr_err("read failed for BTM channels\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dt_chans > channels_available) {
|
||||
pr_err("Number of nodes greater than channels supported:%d\n",
|
||||
channels_available);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Select decimation */
|
||||
buf[0] = chip->prop.decimation;
|
||||
|
||||
/* Select number of samples in fast average mode */
|
||||
buf[1] |= chip->prop.fast_avg_samples | ADC_TM_FAST_AVG_EN;
|
||||
|
||||
/* Select timer1 */
|
||||
buf[2] = chip->prop.timer1;
|
||||
|
||||
/* Select timer2 and timer2 */
|
||||
buf[3] |= chip->prop.timer2 << ADC_TM_MEAS_INTERVAL_CTL2_SHIFT;
|
||||
buf[3] |= chip->prop.timer3;
|
||||
|
||||
ret = adc_tm5_write_reg(chip,
|
||||
ADC_TM_ADC_DIG_PARAM, buf, 4);
|
||||
if (ret < 0)
|
||||
pr_err("adc-tm block write failed with %d\n", ret);
|
||||
|
||||
spin_lock_init(&chip->adc_tm_lock);
|
||||
|
||||
for (i = 0; i < dt_chans; i++)
|
||||
chip->sensor[i].btm_ch = adc_tm_ch_data[i].btm_amux_ch;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct adc_tm_ops ops_adc_tm5 = {
|
||||
.init = adc_tm5_init,
|
||||
.set_trips = adc_tm5_set_trip_temp,
|
||||
.interrupts_reg = adc_tm5_register_interrupts,
|
||||
.get_temp = adc_tm5_get_temp,
|
||||
};
|
||||
|
||||
const struct adc_tm_data data_adc_tm5 = {
|
||||
.ops = &ops_adc_tm5,
|
||||
.full_scale_code_volt = 0x70e4,
|
||||
.decimation = (unsigned int []) {250, 420, 840},
|
||||
.hw_settle = (unsigned int []) {15, 100, 200, 300, 400, 500, 600, 700,
|
||||
1, 2, 4, 8, 16, 32, 64, 128},
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user