Merge "USB: HSIC: Add support for HSIC core and SMSC HUB using DT"
diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt
index 8bb8a76..3e309e4 100644
--- a/Documentation/devicetree/bindings/ata/ahci-platform.txt
+++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt
@@ -4,7 +4,8 @@
Each SATA controller should have its own node.
Required properties:
-- compatible : compatible list, contains "calxeda,hb-ahci" or "snps,spear-ahci"
+- compatible : compatible list, contains "calxeda,hb-ahci" or
+ "snps,spear-ahci" or "qcom,msm-ahci"
- interrupts : <interrupt mapping for SATA IRQ>
- reg : <registers mapping>
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
index e458ea0..fbe8ffa 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
@@ -10,6 +10,8 @@
Required properties:
- compatible : should be "qcom,qpnp-iadc" for Current ADC driver.
- reg : offset and length of the PMIC Aribter register map.
+- address-cells : Must be one.
+- size-cells : Must be zero.
- interrupts : The USR bank peripheral IADC interrupt.
- interrupt-names : Should contain "eoc-int-en-set".
- qcom,adc-bit-resolution : Bit resolution of the ADC.
@@ -21,6 +23,7 @@
Required properties:
- label : Channel name used for sysfs entry.
+- reg : AMUX channel number.
- qcom,channel-num : Channel number associated to the AMUX input.
- qcom,decimation : Sampling rate to use for the individual channel measurement.
Select from the following unsigned int.
@@ -84,6 +87,8 @@
qcom,iadc@3200 {
compatible = "qcom,qpnp-iadc";
reg = <0x3200 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
interrupts = <0 0x36 0>;
interrupt-names = "eoc-int-en-set";
qcom,adc-bit-resolution = <16>;
@@ -93,7 +98,7 @@
/* Channel Node */
chan@0 = {
label = "rsense";
- qcom,channel-num = <0>;
+ reg = <0>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <20>;
qcom,calibration-type = "fresh";
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
index e23605c..bb66e7b 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
@@ -10,6 +10,8 @@
Required properties:
- compatible : should be "qcom,qpnp-vadc" for Voltage ADC driver.
- reg : offset and length of the PMIC Aribter register map.
+- address-cells : Must be one.
+- size-cells : Must be zero.
- interrupts : The USR bank peripheral VADC interrupt.
- interrupt-names : Should contain "eoc-int-en-set".
- qcom,adc-bit-resolution : Bit resolution of the ADC.
@@ -20,7 +22,7 @@
Required properties:
- label : Channel name used for sysfs entry.
-- qcom,channel-num : Channel number associated to the AMUX input.
+- reg : AMUX channel number.
- qcom,decimation : Sampling rate to use for the individual channel measurement.
Select from following unsigned int.
0 : 512
@@ -82,6 +84,8 @@
qcom,vadc@3100 {
compatible = "qcom,qpnp-vadc";
reg = <0x3100 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
interrupts = <0x0 0x31 0x0>;
interrupt-names = "eoc-int-en-set";
qcom,adc-bit-resolution = <15>;
@@ -90,7 +94,7 @@
/* Channel Node */
chan@0 {
label = "usb_in";
- qcom,channel-num = <0>;
+ reg = <0>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <20>;
qcom,calibration-type = "absolute";
diff --git a/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt b/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt
new file mode 100644
index 0000000..f1f4e94
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt
@@ -0,0 +1,120 @@
+Qualcomm's QPNP PMIC thermal monitor ADC driver (VADC_TM)
+
+QPNP 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.
+Seperate kernel api's are provided to usb_id and batt_therm
+to set thresholds and receive threshold notifications.
+
+VADC_TM node
+
+Required properties:
+- compatible : should be "qcom,qpnp-adc-tm" for thermal ADC driver.
+- reg : offset and length of the PMIC Aribter register map.
+- address-cells : Must be one.
+- size-cells : Must be zero.
+- interrupts : The thermal ADC bank peripheral interrupts for eoc, high and low interrupts.
+- interrupt-names : Should be "eoc-int-en-set", "high-thr-en-set" and "low-thr-en-set".
+- qcom,adc-bit-resolution : Bit resolution of the ADC.
+- qcom,adc-vdd-reference : Voltage reference used by the ADC.
+
+Channel nodes
+NOTE: Atleast one Channel node is required.
+
+Required properties:
+- label : Channel name used for sysfs entry.
+- reg : AMUX channel number.
+- qcom,decimation : Sampling rate to use for the individual channel measurement.
+ Select from the following unsigned int.
+ 0 : 512
+ 1 : 1K
+ 2 : 2K
+ 3 : 4K
+- qcom,pre-div-channel-scaling : Pre-div used for the channel before the signal is being measured.
+ Select from the following unsigned int for the corresponding
+ numerator/denominator pre-div ratio.
+ 0 : pre-div ratio of {1, 1}
+ 1 : pre-div ratio of {1, 3}
+ 2 : pre-div ratio of {1, 4}
+ 3 : pre-div ratio of {1, 6}
+ 4 : pre-div ratio of {1, 20}
+- qcom,calibration-type : Reference voltage to use for channel calibration.
+ Channel calibration is dependendent on the channel.
+ Certain channels like XO_THERM, BATT_THERM use ratiometric
+ calibration. Most other channels fall under absolute calibration.
+ Select from the following strings.
+ "absolute" : Uses the 625mv and 1.25V reference channels.
+ "ratiometric" : Uses the reference Voltage/GND for calibration.
+- qcom,scale-function : Scaling fuction used to convert raw ADC code to units specific to
+ a given channel.
+ Select from the following unsigned int.
+ 0 : Default scaling to convert raw adc code to voltage.
+ 1 : Conversion to temperature based on btm parameters.
+ 2 : Returns result in milli degree's Centigrade.
+ 3 : Returns current across 0.1 ohm resistor.
+ 4 : Returns XO thermistor voltage in degree's Centigrade.
+- qcom,hw-settle-time : Settling period for the channel before ADC read.
+ Select from the following unsigned int.
+ 0 : 0us
+ 1 : 100us
+ 2 : 200us
+ 3 : 300us
+ 4 : 400us
+ 5 : 500us
+ 6 : 600us
+ 7 : 700us
+ 8 : 800us
+ 9 : 900us
+ 0xa : 1ms
+ 0xb : 2ms
+ 0xc : 4ms
+ 0xd : 6ms
+ 0xe : 8ms
+ 0xf : 10ms
+- qcom,fast-avg-setup : Average number of samples to be used for measurement. Fast 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)
+ Select from
+ 0 : 1
+ 1 : 2
+ 2 : 4
+ 3 : 8
+ 4 : 16
+ 5 : 32
+ 6 : 64
+ 7 : 128
+ 8 : 256
+- qcom,btm-channel-number : There are 5 BTM channels. The BTM channel numbers are statically
+ allocated to the corresponding channel node.
+
+Example:
+ /* Main Node */
+ qcom,vadc@3400 {
+ compatible = "qcom,qpnp-adc-tm";
+ reg = <0x3400 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x34 0x0>,
+ <0x0 0x34 0x3>,
+ <0x0 0x34 0x4>;
+ interrupt-names = "eoc-int-en-set",
+ "high-thr-en-set",
+ "low-thr-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+
+ /* Channel Node */
+ chan@b5 {
+ label = "pa_therm1";
+ reg = <0xb5>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x70>;
+ };
+ };
diff --git a/arch/arm/boot/dts/mpq8092.dtsi b/arch/arm/boot/dts/mpq8092.dtsi
index f9e3bd5..0ab0a0c 100644
--- a/arch/arm/boot/dts/mpq8092.dtsi
+++ b/arch/arm/boot/dts/mpq8092.dtsi
@@ -271,6 +271,12 @@
qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
qcom,current-limit = <800>;
};
+
+ sata: sata@fc580000 {
+ compatible = "qcom,msm-ahci";
+ reg = <0xfc580000 0x17c>;
+ interrupts = <0 243 0>;
+ };
};
&gdsc_venus {
diff --git a/arch/arm/boot/dts/msm-pm8019.dtsi b/arch/arm/boot/dts/msm-pm8019.dtsi
index 2105e8a..322e601 100755
--- a/arch/arm/boot/dts/msm-pm8019.dtsi
+++ b/arch/arm/boot/dts/msm-pm8019.dtsi
@@ -156,13 +156,15 @@
pm8019_vadc: vadc@3100 {
compatible = "qcom,qpnp-vadc";
reg = <0x3100 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
interrupts = <0x0 0x31 0x0>;
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
chan@8 {
label = "die_temp";
- qcom,channel-num = <8>;
+ reg = <8>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "absolute";
@@ -173,7 +175,7 @@
chan@9 {
label = "ref_625mv";
- qcom,channel-num = <9>;
+ reg = <9>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "absolute";
@@ -182,9 +184,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@10 {
+ chan@a {
label = "ref_1250v";
- qcom,channel-num = <10>;
+ reg = <0xa>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "absolute";
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index fe7b3e9..d5f59de 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -492,6 +492,8 @@
vadc@3100 {
compatible = "qcom,qpnp-vadc";
reg = <0x3100 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
interrupts = <0x0 0x31 0x0>;
interrupt-names = "eoc-int-en-set";
qcom,adc-bit-resolution = <15>;
@@ -499,7 +501,7 @@
chan@0 {
label = "usb_in";
- qcom,channel-num = <0>;
+ reg = <0>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <4>;
qcom,calibration-type = "absolute";
@@ -510,7 +512,7 @@
chan@1 {
label = "dc_in";
- qcom,channel-num = <1>;
+ reg = <1>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <4>;
qcom,calibration-type = "absolute";
@@ -521,7 +523,7 @@
chan@2 {
label = "vchg_sns";
- qcom,channel-num = <2>;
+ reg = <2>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <3>;
qcom,calibration-type = "absolute";
@@ -532,7 +534,7 @@
chan@3 {
label = "spare1";
- qcom,channel-num = <3>;
+ reg = <3>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <6>;
qcom,calibration-type = "absolute";
@@ -543,7 +545,7 @@
chan@4 {
label = "spare2";
- qcom,channel-num = <4>;
+ reg = <4>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <6>;
qcom,calibration-type = "absolute";
@@ -554,7 +556,7 @@
chan@5 {
label = "vcoin";
- qcom,channel-num = <5>;
+ reg = <5>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <1>;
qcom,calibration-type = "absolute";
@@ -565,7 +567,7 @@
chan@6 {
label = "vbat_sns";
- qcom,channel-num = <6>;
+ reg = <6>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <1>;
qcom,calibration-type = "absolute";
@@ -576,7 +578,7 @@
chan@7 {
label = "vph_pwr";
- qcom,channel-num = <7>;
+ reg = <7>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <1>;
qcom,calibration-type = "absolute";
@@ -587,7 +589,7 @@
chan@8 {
label = "die_temp";
- qcom,channel-num = <8>;
+ reg = <8>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "absolute";
@@ -598,7 +600,7 @@
chan@9 {
label = "ref_625mv";
- qcom,channel-num = <9>;
+ reg = <9>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "absolute";
@@ -607,9 +609,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@10 {
+ chan@a {
label = "ref_1250v";
- qcom,channel-num = <10>;
+ reg = <0xa>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "absolute";
@@ -618,9 +620,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@48 {
+ chan@30 {
label = "batt_therm";
- qcom,channel-num = <48>;
+ reg = <0x30>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
@@ -629,9 +631,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@49 {
+ chan@31 {
label = "batt_id";
- qcom,channel-num = <49>;
+ reg = <0x31>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
@@ -640,9 +642,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@178 {
+ chan@b2 {
label = "xo_therm_pu2";
- qcom,channel-num = <178>;
+ reg = <0xb2>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
@@ -651,9 +653,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@179 {
+ chan@b3 {
label = "msm_therm";
- qcom,channel-num = <179>;
+ reg = <0xb3>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
@@ -662,9 +664,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@180 {
+ chan@b4 {
label = "emmc_therm";
- qcom,channel-num = <180>;
+ reg = <0xb4>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
@@ -673,9 +675,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@181 {
+ chan@b5 {
label = "pa_therm1";
- qcom,channel-num = <181>;
+ reg = <0xb5>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
@@ -684,9 +686,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@183 {
+ chan@b7 {
label = "pa_therm2";
- qcom,channel-num = <183>;
+ reg = <0xb7>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
@@ -695,9 +697,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@184 {
+ chan@b8 {
label = "quiet_therm";
- qcom,channel-num = <184>;
+ reg = <0xb8>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
@@ -706,9 +708,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@185 {
+ chan@b9 {
label = "usb_id";
- qcom,channel-num = <185>;
+ reg = <0xb9>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
@@ -721,6 +723,8 @@
iadc@3600 {
compatible = "qcom,qpnp-iadc";
reg = <0x3600 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
interrupts = <0x0 0x36 0x0>;
interrupt-names = "eoc-int-en-set";
qcom,adc-bit-resolution = <16>;
@@ -729,7 +733,7 @@
chan@0 {
label = "internal_rsense";
- qcom,channel-num = <0>;
+ reg = <0>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <1>;
qcom,calibration-type = "absolute";
@@ -738,6 +742,82 @@
qcom,fast-avg-setup = <0>;
};
};
+
+ qcom,vadc@3400 {
+ compatible = "qcom,qpnp-adc-tm";
+ reg = <0x3400 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x34 0x0>,
+ <0x0 0x34 0x3>,
+ <0x0 0x34 0x4>;
+ interrupt-names = "eoc-int-en-set",
+ "high-thr-en-set",
+ "low-thr-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+
+ /* Channel Node */
+ chan@b9 {
+ label = "usb_id";
+ reg = <0xb9>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x48>;
+ };
+
+ chan@30 {
+ label = "batt_therm";
+ reg = <0x30>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <1>;
+ qcom,hw-settle-time = <0xf>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x68>;
+ };
+
+ chan@b5 {
+ label = "pa_therm1";
+ reg = <0xb5>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x70>;
+ };
+
+ chan@b7 {
+ label = "pa_therm2";
+ reg = <0xb7>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x78>;
+ };
+
+ chan@b4 {
+ label = "emmc_therm";
+ reg = <0xb4>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x80>;
+ };
+ };
};
qcom,pm8941@1 {
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index 324d358..ddba4c3 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -583,9 +583,9 @@
/include/ "msm9625-regulator.dtsi"
&pm8019_vadc {
- chan@49 {
+ chan@31 {
label = "batt_id_therm";
- qcom,channel-num = <49>;
+ reg = <0x31>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
@@ -594,9 +594,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@51 {
+ chan@33 {
label = "pa_therm1";
- qcom,channel-num = <51>;
+ reg = <0x33>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
@@ -605,9 +605,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@52 {
+ chan@34 {
label = "pa_therm2";
- qcom,channel-num = <52>;
+ reg = <0x34>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
@@ -616,9 +616,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@50 {
+ chan@32 {
label = "xo_therm";
- qcom,channel-num = <50>;
+ reg = <0x32>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
@@ -627,9 +627,9 @@
qcom,fast-avg-setup = <0>;
};
- chan@60 {
+ chan@3c {
label = "xo_therm_amux";
- qcom,channel-num = <60>;
+ reg = <0x3c>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 94e2f36..f4f6b18 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -67,6 +67,8 @@
CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
CONFIG_MSM_OCMEM_DEBUG=y
CONFIG_MSM_OCMEM_NONSECURE=y
+CONFIG_MSM_RTB=y
+CONFIG_MSM_RTB_SEPARATE_CPUS=y
CONFIG_MSM_CACHE_ERP=y
CONFIG_MSM_L1_ERR_PANIC=y
CONFIG_MSM_L1_ERR_LOG=y
@@ -286,6 +288,7 @@
CONFIG_THERMAL_TSENS8974=y
CONFIG_THERMAL_MONITOR=y
CONFIG_THERMAL_QPNP=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
CONFIG_WCD9320_CODEC=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_STUB=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index 0428362..56b87ce 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -66,6 +66,8 @@
CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
CONFIG_MSM_OCMEM_DEBUG=y
CONFIG_MSM_OCMEM_NONSECURE=y
+CONFIG_MSM_RTB=y
+CONFIG_MSM_RTB_SEPARATE_CPUS=y
CONFIG_MSM_CACHE_ERP=y
CONFIG_MSM_L1_ERR_PANIC=y
CONFIG_MSM_L1_ERR_LOG=y
@@ -290,6 +292,7 @@
CONFIG_THERMAL_QPNP=y
CONFIG_WCD9320_CODEC=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
CONFIG_REGULATOR_STUB=y
CONFIG_REGULATOR_QPNP=y
CONFIG_MEDIA_SUPPORT=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index ccd23e8..0010971 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -1689,17 +1689,6 @@
endif # CPU_FREQ_MSM
-config MSM_CPU_AVS
- bool "Enable software controlled Adaptive Voltage Scaling (AVS)"
- depends on (ARCH_MSM_SCORPION && QSD_SVS)
- depends on ARCH_QSD8X50
- default n
- select MSM_AVS_HW
- help
- This enables the s/w control of Adaptive Voltage Scaling feature
- in Qualcomm ARMv7 CPUs. It adjusts the voltage for each frequency
- based on feedback from three ring oscillators in the CPU.
-
config MSM_AVS_HW
bool "Enable Adaptive Voltage Scaling (AVS)"
default n
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 78fe55f..0eacdca 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -57,7 +57,6 @@
endif
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
-obj-$(CONFIG_MSM_CPU_AVS) += avs.o
obj-$(CONFIG_MSM_AVS_HW) += avs_hw.o
obj-$(CONFIG_CPU_V6) += idle-v6.o
obj-$(CONFIG_CPU_V7) += idle-v7.o
diff --git a/arch/arm/mach-msm/acpuclock-8x50.c b/arch/arm/mach-msm/acpuclock-8x50.c
index 996f883..eed8000 100644
--- a/arch/arm/mach-msm/acpuclock-8x50.c
+++ b/arch/arm/mach-msm/acpuclock-8x50.c
@@ -423,14 +423,6 @@
}
if (reason == SETRATE_CPUFREQ) {
-#ifdef CONFIG_MSM_CPU_AVS
- /* Notify avs before changing frequency */
- rc = avs_adjust_freq(freq_index, 1);
- if (rc) {
- pr_err("Unable to increase ACPU vdd (%d)\n", rc);
- goto out;
- }
-#endif
/* Increase VDD if needed. */
if (tgt_s->vdd > strt_s->vdd) {
rc = acpuclk_set_vdd_level(tgt_s->vdd);
@@ -485,13 +477,6 @@
if (reason == SETRATE_PC)
goto out;
-#ifdef CONFIG_MSM_CPU_AVS
- /* notify avs after changing frequency */
- res = avs_adjust_freq(freq_index, 0);
- if (res)
- pr_warning("Unable to drop ACPU vdd (%d)\n", res);
-#endif
-
/* Drop VDD level if we can. */
if (tgt_s->vdd < strt_s->vdd) {
res = acpuclk_set_vdd_level(tgt_s->vdd);
@@ -658,22 +643,6 @@
}
}
-#ifdef CONFIG_MSM_CPU_AVS
-static int __devinit acpu_avs_init(int (*set_vdd) (int), int khz)
-{
- int i;
- int freq_count = 0;
- int freq_index = -1;
-
- for (i = 0; acpu_freq_tbl[i].acpuclk_khz; i++) {
- freq_count++;
- if (acpu_freq_tbl[i].acpuclk_khz == khz)
- freq_index = i;
- }
-
- return avs_init(set_vdd, freq_count, freq_index);
-}
-#endif
static int qsd8x50_tps65023_set_dcdc1(int mVolts)
{
@@ -728,13 +697,6 @@
cpufreq_table_init();
cpufreq_frequency_table_get_attr(freq_table, smp_processor_id());
#endif
-#ifdef CONFIG_MSM_CPU_AVS
- if (!acpu_avs_init(drv_state.acpu_set_vdd,
- drv_state.current_speed->acpuclk_khz)) {
- /* avs init successful. avs will handle voltage changes */
- drv_state.acpu_set_vdd = NULL;
- }
-#endif
return 0;
}
diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c
deleted file mode 100644
index 827adab..0000000
--- a/arch/arm/mach-msm/avs.c
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright (c) 2009, Code Aurora Forum. 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/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/kernel_stat.h>
-#include <linux/workqueue.h>
-#include <linux/slab.h>
-
-#include "avs.h"
-
-#define AVSDSCR_INPUT 0x01004860 /* magic # from circuit designer */
-#define TSCSR_INPUT 0x00000001 /* enable temperature sense */
-
-#define TEMPRS 16 /* total number of temperature regions */
-#define GET_TEMPR() (avs_get_tscsr() >> 28) /* scale TSCSR[CTEMP] to regions */
-
-struct mutex avs_lock;
-
-static struct avs_state_s
-{
- u32 freq_cnt; /* Frequencies supported list */
- short *avs_v; /* Dyanmically allocated storage for
- * 2D table of voltages over temp &
- * freq. Used as a set of 1D tables.
- * Each table is for a single temp.
- * For usage see avs_get_voltage
- */
- int (*set_vdd) (int); /* Function Ptr for setting voltage */
- int changing; /* Clock frequency is changing */
- u32 freq_idx; /* Current frequency index */
- int vdd; /* Current ACPU voltage */
-} avs_state;
-
-/*
- * Update the AVS voltage vs frequency table, for current temperature
- * Adjust based on the AVS delay circuit hardware status
- */
-static void avs_update_voltage_table(short *vdd_table)
-{
- u32 avscsr;
- int cpu;
- int vu;
- int l2;
- int i;
- u32 cur_freq_idx;
- short cur_voltage;
-
- cur_freq_idx = avs_state.freq_idx;
- cur_voltage = avs_state.vdd;
-
- avscsr = avs_test_delays();
- AVSDEBUG("avscsr=%x, avsdscr=%x\n", avscsr, avs_get_avsdscr());
-
- /*
- * Read the results for the various unit's AVS delay circuits
- * 2=> up, 1=>down, 0=>no-change
- */
- cpu = ((avscsr >> 23) & 2) + ((avscsr >> 16) & 1);
- vu = ((avscsr >> 28) & 2) + ((avscsr >> 21) & 1);
- l2 = ((avscsr >> 29) & 2) + ((avscsr >> 22) & 1);
-
- if ((cpu == 3) || (vu == 3) || (l2 == 3)) {
- printk(KERN_ERR "AVS: Dly Synth O/P error\n");
- } else if ((cpu == 2) || (l2 == 2) || (vu == 2)) {
- /*
- * even if one oscillator asks for up, increase the voltage,
- * as its an indication we are running outside the
- * critical acceptable range of v-f combination.
- */
- AVSDEBUG("cpu=%d l2=%d vu=%d\n", cpu, l2, vu);
- AVSDEBUG("Voltage up at %d\n", cur_freq_idx);
-
- if (cur_voltage >= VOLTAGE_MAX)
- printk(KERN_ERR
- "AVS: Voltage can not get high enough!\n");
-
- /* Raise the voltage for all frequencies */
- for (i = 0; i < avs_state.freq_cnt; i++) {
- vdd_table[i] = cur_voltage + VOLTAGE_STEP;
- if (vdd_table[i] > VOLTAGE_MAX)
- vdd_table[i] = VOLTAGE_MAX;
- }
- } else if ((cpu == 1) && (l2 == 1) && (vu == 1)) {
- if ((cur_voltage - VOLTAGE_STEP >= VOLTAGE_MIN) &&
- (cur_voltage <= vdd_table[cur_freq_idx])) {
- vdd_table[cur_freq_idx] = cur_voltage - VOLTAGE_STEP;
- AVSDEBUG("Voltage down for %d and lower levels\n",
- cur_freq_idx);
-
- /* clamp to this voltage for all lower levels */
- for (i = 0; i < cur_freq_idx; i++) {
- if (vdd_table[i] > vdd_table[cur_freq_idx])
- vdd_table[i] = vdd_table[cur_freq_idx];
- }
- }
- }
-}
-
-/*
- * Return the voltage for the target performance freq_idx and optionally
- * use AVS hardware to check the present voltage freq_idx
- */
-static short avs_get_target_voltage(int freq_idx, bool update_table)
-{
- unsigned cur_tempr = GET_TEMPR();
- unsigned temp_index = cur_tempr*avs_state.freq_cnt;
-
- /* Table of voltages vs frequencies for this temp */
- short *vdd_table = avs_state.avs_v + temp_index;
-
- if (update_table)
- avs_update_voltage_table(vdd_table);
-
- return vdd_table[freq_idx];
-}
-
-
-/*
- * Set the voltage for the freq_idx and optionally
- * use AVS hardware to update the voltage
- */
-static int avs_set_target_voltage(int freq_idx, bool update_table)
-{
- int rc = 0;
- int new_voltage = avs_get_target_voltage(freq_idx, update_table);
- if (avs_state.vdd != new_voltage) {
- AVSDEBUG("AVS setting V to %d mV @%d\n",
- new_voltage, freq_idx);
- rc = avs_state.set_vdd(new_voltage);
- if (rc)
- return rc;
- avs_state.vdd = new_voltage;
- }
- return rc;
-}
-
-/*
- * Notify avs of clk frquency transition begin & end
- */
-int avs_adjust_freq(u32 freq_idx, int begin)
-{
- int rc = 0;
-
- if (!avs_state.set_vdd) {
- /* AVS not initialized */
- return 0;
- }
-
- if (freq_idx >= avs_state.freq_cnt) {
- AVSDEBUG("Out of range :%d\n", freq_idx);
- return -EINVAL;
- }
-
- mutex_lock(&avs_lock);
- if ((begin && (freq_idx > avs_state.freq_idx)) ||
- (!begin && (freq_idx < avs_state.freq_idx))) {
- /* Update voltage before increasing frequency &
- * after decreasing frequency
- */
- rc = avs_set_target_voltage(freq_idx, 0);
- if (rc)
- goto aaf_out;
-
- avs_state.freq_idx = freq_idx;
- }
- avs_state.changing = begin;
-aaf_out:
- mutex_unlock(&avs_lock);
-
- return rc;
-}
-
-
-static struct delayed_work avs_work;
-static struct workqueue_struct *kavs_wq;
-#define AVS_DELAY ((CONFIG_HZ * 50 + 999) / 1000)
-
-static void do_avs_timer(struct work_struct *work)
-{
- int cur_freq_idx;
-
- mutex_lock(&avs_lock);
- if (!avs_state.changing) {
- /* Only adjust the voltage if clk is stable */
- cur_freq_idx = avs_state.freq_idx;
- avs_set_target_voltage(cur_freq_idx, 1);
- }
- mutex_unlock(&avs_lock);
- queue_delayed_work_on(0, kavs_wq, &avs_work, AVS_DELAY);
-}
-
-
-static void __init avs_timer_init(void)
-{
- INIT_DELAYED_WORK_DEFERRABLE(&avs_work, do_avs_timer);
- queue_delayed_work_on(0, kavs_wq, &avs_work, AVS_DELAY);
-}
-
-static void __exit avs_timer_exit(void)
-{
- cancel_delayed_work(&avs_work);
-}
-
-static int __init avs_work_init(void)
-{
- kavs_wq = create_workqueue("avs");
- if (!kavs_wq) {
- printk(KERN_ERR "AVS initialization failed\n");
- return -EFAULT;
- }
- avs_timer_init();
-
- return 1;
-}
-
-static void __exit avs_work_exit(void)
-{
- avs_timer_exit();
- destroy_workqueue(kavs_wq);
-}
-
-int __init avs_init(int (*set_vdd)(int), u32 freq_cnt, u32 freq_idx)
-{
- int i;
-
- mutex_init(&avs_lock);
-
- if (freq_cnt == 0)
- return -EINVAL;
-
- avs_state.freq_cnt = freq_cnt;
-
- if (freq_idx >= avs_state.freq_cnt)
- return -EINVAL;
-
- avs_state.avs_v = kmalloc(TEMPRS * avs_state.freq_cnt *
- sizeof(avs_state.avs_v[0]), GFP_KERNEL);
-
- if (avs_state.avs_v == 0)
- return -ENOMEM;
-
- for (i = 0; i < TEMPRS*avs_state.freq_cnt; i++)
- avs_state.avs_v[i] = VOLTAGE_MAX;
-
- avs_reset_delays(AVSDSCR_INPUT);
- avs_set_tscsr(TSCSR_INPUT);
-
- avs_state.set_vdd = set_vdd;
- avs_state.changing = 0;
- avs_state.freq_idx = -1;
- avs_state.vdd = -1;
- avs_adjust_freq(freq_idx, 0);
-
- avs_work_init();
-
- return 0;
-}
-
-void __exit avs_exit()
-{
- avs_work_exit();
-
- kfree(avs_state.avs_v);
-}
-
-
diff --git a/arch/arm/mach-msm/avs.h b/arch/arm/mach-msm/avs.h
index e87bded..556603a 100644
--- a/arch/arm/mach-msm/avs.h
+++ b/arch/arm/mach-msm/avs.h
@@ -14,23 +14,6 @@
#ifndef AVS_H
#define AVS_H
-#define VOLTAGE_MIN 1000 /* mV */
-#define VOLTAGE_MAX 1250
-#define VOLTAGE_STEP 25
-
-int __init avs_init(int (*set_vdd)(int), u32 freq_cnt, u32 freq_idx);
-void __exit avs_exit(void);
-
-int avs_adjust_freq(u32 freq_index, int begin);
-
-/* Routines exported from avs_hw.S */
-#ifdef CONFIG_MSM_CPU_AVS
-u32 avs_test_delays(void);
-#else
-static inline u32 avs_test_delays(void)
-{ return 0; }
-#endif
-
#ifdef CONFIG_MSM_AVS_HW
u32 avs_reset_delays(u32 avsdscr);
u32 avs_get_avscsr(void);
@@ -54,9 +37,6 @@
static inline void avs_enable(u32 avscsr) {}
#endif
-/*#define AVSDEBUG(x...) pr_info("AVS: " x);*/
-#define AVSDEBUG(...)
-
#define AVS_DISABLE(cpu) do { \
if (get_cpu() == (cpu)) \
avs_disable(); \
diff --git a/arch/arm/mach-msm/avs_hw.S b/arch/arm/mach-msm/avs_hw.S
index efb9c47..6fad8bd 100644
--- a/arch/arm/mach-msm/avs_hw.S
+++ b/arch/arm/mach-msm/avs_hw.S
@@ -13,52 +13,6 @@
.text
-#ifdef CONFIG_MSM_CPU_AVS
- .global avs_test_delays
-avs_test_delays:
-
-/* Read r1=CPMR and enable Never Sleep for VSLPDLY */
- mrc p15, 7, r1, c15, c0, 5
- orr r12, r1, #3, 24
- mcr p15, 7, r12, c15, c0, 5
-
-/* Read r2=CPACR and enable full access to CP10 and CP11 space */
- mrc p15, 0, r2, c1, c0, 2
- orr r12, r2, #(0xf << 20)
- mcr p15, 0, r12, c1, c0, 2
- isb
-
-/* Read r3=FPEXC and or in FP enable, VFP/ASE enable = FPEXC[30]; */
- fmrx r3, fpexc
- orr r12, r3, #1, 2
- fmxr fpexc, r12
-
-/*
- * Do floating-point operations to prime the VFP pipeline. Use
- * fcpyd d0, d0 as a floating point nop. This avoids changing VFP
- * state.
- */
- fcpyd d0, d0
- fcpyd d0, d0
- fcpyd d0, d0
-
-/* Read r0=AVSCSR to get status from CPU, VFP, and L2 ring oscillators */
- mrc p15, 7, r0, c15, c1, 7
-
-/* Restore FPEXC */
- fmxr fpexc, r3
-
-/* Restore CPACR */
- MCR p15, 0, r2, c1, c0, 2
-
-/* Restore CPMR */
- mcr p15, 7, r1, c15, c0, 5
- isb
-
- bx lr
-#endif
-
-
.global avs_get_avscsr
/* Read r0=AVSCSR to get status from CPU, VFP, and L2 ring oscillators */
diff --git a/arch/arm/mach-msm/board-msm7x27a.c b/arch/arm/mach-msm/board-msm7x27a.c
index 5cabe64..2bd92f2 100644
--- a/arch/arm/mach-msm/board-msm7x27a.c
+++ b/arch/arm/mach-msm/board-msm7x27a.c
@@ -403,7 +403,7 @@
.idle_enabled = 0,
.suspend_enabled = 0,
.latency = 500,
- .residency = 6000,
+ .residency = 500,
},
[MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = {
@@ -422,7 +422,7 @@
.idle_enabled = 0,
.suspend_enabled = 0,
.latency = 500,
- .residency = 6000,
+ .residency = 500,
},
[MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = {
@@ -441,7 +441,7 @@
.idle_enabled = 0,
.suspend_enabled = 0,
.latency = 500,
- .residency = 6000,
+ .residency = 500,
},
[MSM_PM_MODE(2, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = {
@@ -460,7 +460,7 @@
.idle_enabled = 0,
.suspend_enabled = 0,
.latency = 500,
- .residency = 6000,
+ .residency = 500,
},
[MSM_PM_MODE(3, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = {
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index 82fe67b..53d3023 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -382,7 +382,7 @@
.idle_enabled = 0,
.suspend_enabled = 0,
.latency = 500,
- .residency = 6000,
+ .residency = 500,
},
[MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = {
@@ -401,7 +401,7 @@
.idle_enabled = 0,
.suspend_enabled = 0,
.latency = 500,
- .residency = 6000,
+ .residency = 500,
},
[MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = {
@@ -420,7 +420,7 @@
.idle_enabled = 0,
.suspend_enabled = 0,
.latency = 500,
- .residency = 6000,
+ .residency = 500,
},
[MSM_PM_MODE(2, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = {
@@ -439,7 +439,7 @@
.idle_enabled = 0,
.suspend_enabled = 0,
.latency = 500,
- .residency = 6000,
+ .residency = 500,
},
[MSM_PM_MODE(3, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = {
diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index 5aa747a..2d9872e 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -1939,6 +1939,8 @@
else if (msm8625_cpu_id() == MSM8625)
msm_cpr_pdata.max_freq = 1008000;
+ if (machine_is_qrd_skud_prime() || cpu_is_msm8625q())
+ msm_cpr_pdata.step_size = 6250;
platform_device_register(&msm8625_vp_device);
platform_device_register(&msm8625_device_cpr);
}
diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S
index ccd0bf7..9a22996 100644
--- a/arch/arm/mach-msm/idle-v7.S
+++ b/arch/arm/mach-msm/idle-v7.S
@@ -96,20 +96,6 @@
mrc p15, 0, r9, c13, c0, 3 /* TPIDRURO */
mrc p15, 0, ip, c13, c0, 1 /* context ID */
stmia r0!, {r1-r9, ip}
-#ifdef CONFIG_MSM_CPU_AVS
- mrc p15, 7, r1, c15, c1, 7 /* AVSCSR is the Adaptive Voltage Scaling
- * Control and Status Register */
- mrc p15, 7, r2, c15, c0, 6 /* AVSDSCR is the Adaptive Voltage
- * Scaling Delay Synthesizer Control
- * Register */
-#ifndef CONFIG_ARCH_MSM_KRAIT
- mrc p15, 7, r3, c15, c1, 0 /* TSCSR is the Temperature Status and
- * Control Register
- */
-#endif
-
- stmia r0!, {r1-r3}
-#endif
#ifdef CONFIG_MSM_JTAG
bl msm_jtag_save_state
@@ -240,14 +226,6 @@
add r1, r1, r2
#endif
-#ifdef CONFIG_MSM_CPU_AVS
- ldmdb r1!, {r2-r4}
-#ifndef CONFIG_ARCH_MSM_KRAIT
- mcr p15, 7, r4, c15, c1, 0 /* TSCSR */
-#endif
- mcr p15, 7, r3, c15, c0, 6 /* AVSDSCR */
- mcr p15, 7, r2, c15, c1, 7 /* AVSCSR */
-#endif
ldmdb r1!, {r2-r11}
mcr p15, 0, r4, c3, c0, 0 /* dacr */
mcr p15, 0, r3, c2, c0, 0 /* TTBR0 */
diff --git a/arch/arm/mach-msm/idle.h b/arch/arm/mach-msm/idle.h
index ee3209c..5550e96 100644
--- a/arch/arm/mach-msm/idle.h
+++ b/arch/arm/mach-msm/idle.h
@@ -14,13 +14,8 @@
#ifndef _ARCH_ARM_MACH_MSM_IDLE_H_
#define _ARCH_ARM_MACH_MSM_IDLE_H_
-#ifdef CONFIG_MSM_CPU_AVS
-/* 11 general purpose registers (r4-r14), 10 cp15 registers, 3 AVS registers */
-#define CPU_SAVED_STATE_SIZE (4 * 11 + 4 * 10 + 4 * 3)
-#else
/* 11 general purpose registers (r4-r14), 10 cp15 registers */
#define CPU_SAVED_STATE_SIZE (4 * 11 + 4 * 10)
-#endif
#define ON 1
#define OFF 0
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 9e419e1..82d19e0 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -281,6 +281,7 @@
static const struct of_device_id ahci_of_match[] = {
{ .compatible = "calxeda,hb-ahci", },
{ .compatible = "snps,spear-ahci", },
+ { .compatible = "qcom,msm-ahci", },
{},
};
MODULE_DEVICE_TABLE(of, ahci_of_match);
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index 5fb041d..440dc42 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -632,7 +632,7 @@
do_div(adc_voltage, param1.dy);
- qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb,
+ qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
adc_voltage, result);
if (negative_offset)
@@ -649,7 +649,7 @@
qpnp_get_vadc_gain_and_offset(¶m1, CALIB_RATIOMETRIC);
- rc = qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
+ rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
param->low_thr_temp, ¶m->low_thr_voltage);
if (rc)
@@ -659,7 +659,7 @@
do_div(param->low_thr_voltage, param1.adc_vref);
param->low_thr_voltage += param1.adc_gnd;
- rc = qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
+ rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
param->high_thr_temp, ¶m->high_thr_voltage);
if (rc)
@@ -822,7 +822,7 @@
struct device_node *node = spmi->dev.of_node;
struct resource *res;
struct device_node *child;
- struct qpnp_vadc_amux *adc_channel_list;
+ struct qpnp_adc_amux *adc_channel_list;
struct qpnp_adc_properties *adc_prop;
struct qpnp_adc_amux_properties *amux_prop;
int count_adc_channel_list = 0, decimation, rc = 0, i = 0;
@@ -847,7 +847,7 @@
return -ENOMEM;
}
adc_channel_list = devm_kzalloc(&spmi->dev,
- ((sizeof(struct qpnp_vadc_amux)) * count_adc_channel_list),
+ ((sizeof(struct qpnp_adc_amux)) * count_adc_channel_list),
GFP_KERNEL);
if (!adc_channel_list) {
dev_err(&spmi->dev, "Unable to allocate memory\n");
@@ -877,8 +877,7 @@
return -EINVAL;
}
- rc = of_property_read_u32(child, "qcom,channel-num",
- &channel_num);
+ rc = of_property_read_u32(child, "reg", &channel_num);
if (rc) {
pr_err("Invalid channel num\n");
return -EINVAL;
diff --git a/drivers/misc/isa1200.c b/drivers/misc/isa1200.c
index 6c3e787..604ffd7 100644
--- a/drivers/misc/isa1200.c
+++ b/drivers/misc/isa1200.c
@@ -42,7 +42,7 @@
struct hrtimer timer;
struct timed_output_dev dev;
struct work_struct work;
- spinlock_t lock;
+ struct mutex lock;
unsigned int enable;
unsigned int period_ns;
bool is_len_gpio_valid;
@@ -216,9 +216,8 @@
{
struct isa1200_chip *haptic = container_of(dev, struct isa1200_chip,
dev);
- unsigned long flags;
- spin_lock_irqsave(&haptic->lock, flags);
+ mutex_lock(&haptic->lock);
hrtimer_cancel(&haptic->timer);
if (value == 0)
haptic->enable = 0;
@@ -230,7 +229,7 @@
ktime_set(value / 1000, (value % 1000) * 1000000),
HRTIMER_MODE_REL);
}
- spin_unlock_irqrestore(&haptic->lock, flags);
+ mutex_unlock(&haptic->lock);
schedule_work(&haptic->work);
}
@@ -501,7 +500,7 @@
}
}
- spin_lock_init(&haptic->lock);
+ mutex_init(&haptic->lock);
INIT_WORK(&haptic->work, isa1200_chip_work);
haptic->clk_on = false;
@@ -591,6 +590,7 @@
hen_gpio_fail:
timed_output_dev_unregister(&haptic->dev);
timed_reg_fail:
+ mutex_destroy(&haptic->lock);
if (pdata->power_on)
pdata->power_on(0);
pwr_up_fail:
@@ -637,6 +637,9 @@
ISA1200_HCTRL1_RESET);
+ /* destroy mutex */
+ mutex_destroy(&haptic->lock);
+
/* power-off the chip */
if (haptic->pdata->regulator_info) {
isa1200_reg_power(haptic, false);
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index f279d8d..751ba75 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -382,7 +382,11 @@
unsigned long value, freq;
int retval = -EINVAL;
- if (!host || !host->card || kstrtoul(buf, 0, &value))
+ if (!host)
+ goto out;
+
+ mmc_claim_host(host);
+ if (!host->card || kstrtoul(buf, 0, &value))
goto err;
if (value && !mmc_can_scale_clk(host)) {
@@ -409,8 +413,10 @@
}
host->clk_scaling.initialized = false;
}
- return count;
+ retval = count;
err:
+ mmc_release_host(host);
+out:
return retval;
}
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 00dc5bf..f3598cf 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -1640,6 +1640,11 @@
spin_lock(&host->lock);
+ if (!atomic_read(&host->clks_on)) {
+ spin_unlock(&host->lock);
+ return IRQ_NONE;
+ }
+
status = readl_relaxed(base + MMCISTATUS);
if (((readl_relaxed(host->base + MMCIMASK0) & status) &
@@ -5104,6 +5109,31 @@
}
}
+/*
+ * This function prints the testbus debug output for all the
+ * available SDCC controller test bus.
+ *
+ * Note: This function should only be called if the SDCC is clocked.
+ */
+static void msmsdcc_print_testbus_info(struct msmsdcc_host *host)
+{
+ int testbus_num;
+
+ if (!is_testbus_debug(host))
+ return;
+
+ pr_err("== SDCC Test Bus Debug ==");
+ for (testbus_num = 0; testbus_num < MAX_TESTBUS; testbus_num++) {
+ writel_relaxed(((testbus_num & MCI_TESTBUS_SEL_MASK)
+ | MCI_TESTBUS_ENA),
+ host->base + MCI_TESTBUS_CONFIG);
+ pr_err("TestBus(%d) = 0x%.8x\n", testbus_num,
+ (u32)readl_relaxed(host->base + MCI_SDCC_DEBUG_REG));
+ }
+ /* Disable the test bus output */
+ writel_relaxed(~MCI_TESTBUS_ENA, host->base + MCI_TESTBUS_CONFIG);
+}
+
static void msmsdcc_dump_sdcc_state(struct msmsdcc_host *host)
{
/* Dump current state of SDCC clocks, power and irq */
@@ -5123,6 +5153,7 @@
pr_err("%s: MCI_TEST_INPUT = 0x%.8x\n",
mmc_hostname(host->mmc),
readl_relaxed(host->base + MCI_TEST_INPUT));
+ msmsdcc_print_testbus_info(host);
}
if (host->curr.data) {
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index 500b5fb..7469c8e 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -193,6 +193,13 @@
#define MCI_TEST_INPUT 0x0D4
+#define MCI_TESTBUS_CONFIG 0x0CC
+#define MCI_TESTBUS_SEL_MASK (0x7)
+#define MAX_TESTBUS 8
+#define MCI_TESTBUS_ENA (1 << 3)
+
+#define MCI_SDCC_DEBUG_REG 0x124
+
#define MCI_IRQENABLE \
(MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK| \
MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \
@@ -449,6 +456,7 @@
#define MSMSDCC_AUTO_CMD21 (1 << 10)
#define MSMSDCC_SW_RST_CFG_BROKEN (1 << 11)
#define MSMSDCC_DATA_PEND_FOR_CMD53 (1 << 12)
+#define MSMSDCC_TESTBUS_DEBUG (1 << 13)
#define set_hw_caps(h, val) ((h)->hw_caps |= val)
#define is_sps_mode(h) ((h)->hw_caps & MSMSDCC_SPS_BAM_SUP)
@@ -465,6 +473,7 @@
#define is_sw_reset_save_config_broken(h) \
((h)->hw_caps & MSMSDCC_SW_RST_CFG_BROKEN)
#define is_data_pend_for_cmd53(h) ((h)->hw_caps & MSMSDCC_DATA_PEND_FOR_CMD53)
+#define is_testbus_debug(h) ((h)->hw_caps & MSMSDCC_TESTBUS_DEBUG)
/* Set controller capabilities based on version */
static inline void set_default_hw_caps(struct msmsdcc_host *host)
@@ -498,7 +507,8 @@
if (step >= 0x2b) /* SDCC v4 2.1.0 and greater */
host->hw_caps |= MSMSDCC_SW_RST | MSMSDCC_SW_RST_CFG |
MSMSDCC_AUTO_CMD21 |
- MSMSDCC_DATA_PEND_FOR_CMD53;
+ MSMSDCC_DATA_PEND_FOR_CMD53 |
+ MSMSDCC_TESTBUS_DEBUG;
if (step == 0x2b)
host->hw_caps |= MSMSDCC_SW_RST_CFG_BROKEN;
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index e70924c..347d9f2 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -107,3 +107,13 @@
available. If allowed via compile time configuration; enabling the
thermal zone device via the mode file results in shifting PMIC over
temperature shutdown control from hardware to software.
+
+config THERMAL_QPNP_ADC_TM
+ tristate "Qualcomm 8974 Thermal Monitor ADC Driver"
+ depends on THERMAL && SPMI
+ 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.
+ Disabling the thermal zone device via the mode file results in disabling
+ the sensor. Also able to set threshold temperature for both hot and cold
+ and update when a threshold is reached.
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 3b2b3a8..01dce2d 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -11,3 +11,4 @@
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
obj-$(CONFIG_THERMAL_TSENS8974) += msm8974-tsens.o
obj-$(CONFIG_THERMAL_QPNP) += qpnp-temp-alarm.o
+obj-$(CONFIG_THERMAL_QPNP_ADC_TM) += qpnp-adc-tm.o
diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c
new file mode 100644
index 0000000..bcc85f3
--- /dev/null
+++ b/drivers/thermal/qpnp-adc-tm.c
@@ -0,0 +1,1531 @@
+/* Copyright (c) 2012, 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.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/spmi.h>
+#include <linux/of_irq.h>
+#include <linux/wakelock.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+
+/* QPNP VADC TM register definition */
+#define QPNP_STATUS1 0x8
+#define QPNP_STATUS1_OP_MODE 4
+#define QPNP_STATUS1_MEAS_INTERVAL_EN_STS BIT(2)
+#define QPNP_STATUS1_REQ_STS BIT(1)
+#define QPNP_STATUS1_EOC BIT(0)
+#define QPNP_STATUS2 0x9
+#define QPNP_STATUS2_CONV_SEQ_STATE 6
+#define QPNP_STATUS2_FIFO_NOT_EMPTY_FLAG BIT(1)
+#define QPNP_STATUS2_CONV_SEQ_TIMEOUT_STS BIT(0)
+#define QPNP_CONV_TIMEOUT_ERR 2
+
+#define QPNP_MODE_CTL 0x40
+#define QPNP_OP_MODE_SHIFT 3
+#define QPNP_VREF_XO_THM_FORCE BIT(2)
+#define QPNP_AMUX_TRIM_EN BIT(1)
+#define QPNP_ADC_TRIM_EN BIT(0)
+#define QPNP_EN_CTL1 0x46
+#define QPNP_ADC_TM_EN BIT(7)
+#define QPNP_ADC_CH_SEL_CTL 0x48
+#define QPNP_ADC_DIG_PARAM 0x50
+#define QPNP_ADC_DIG_DEC_RATIO_SEL_SHIFT 3
+#define QPNP_HW_SETTLE_DELAY 0x51
+#define QPNP_CONV_REQ 0x52
+#define QPNP_CONV_REQ_SET BIT(7)
+#define QPNP_CONV_SEQ_CTL 0x54
+#define QPNP_CONV_SEQ_HOLDOFF_SHIFT 4
+#define QPNP_CONV_SEQ_TRIG_CTL 0x55
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL 0x57
+#define QPNP_ADC_TM_MEAS_INTERVAL_TIME_SHIFT 0x3
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL2 0x58
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL2_SHIFT 0x4
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL2_MASK 0xf0
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL3_MASK 0xf
+
+#define QPNP_ADC_MEAS_INTERVAL_OP_CTL 0x59
+#define QPNP_ADC_MEAS_INTERVAL_OP BIT(7)
+
+#define QPNP_FAST_AVG_CTL 0x5a
+#define QPNP_FAST_AVG_EN 0x5b
+
+#define QPNP_M0_LOW_THR_LSB 0x5c
+#define QPNP_M0_LOW_THR_MSB 0x5d
+#define QPNP_M0_HIGH_THR_LSB 0x5e
+#define QPNP_M0_HIGH_THR_MSB 0x5f
+#define QPNP_M1_LOW_THR_LSB 0x69
+#define QPNP_M1_LOW_THR_MSB 0x6a
+#define QPNP_M1_HIGH_THR_LSB 0x6b
+#define QPNP_M1_HIGH_THR_MSB 0x6c
+#define QPNP_M2_LOW_THR_LSB 0x71
+#define QPNP_M2_LOW_THR_MSB 0x72
+#define QPNP_M2_HIGH_THR_LSB 0x7b
+#define QPNP_M2_HIGH_THR_MSB 0x7c
+#define QPNP_M3_LOW_THR_LSB 0x79
+#define QPNP_M3_LOW_THR_MSB 0x7a
+#define QPNP_M3_HIGH_THR_LSB 0x7b
+#define QPNP_M3_HIGH_THR_MSB 0x7c
+#define QPNP_M4_LOW_THR_LSB 0x81
+#define QPNP_M4_LOW_THR_MSB 0x82
+#define QPNP_M4_HIGH_THR_LSB 0x83
+#define QPNP_M4_HIGH_THR_MSB 0x84
+
+#define QPNP_ADC_TM_MULTI_MEAS_EN 0x41
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M0 BIT(0)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M1 BIT(1)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M2 BIT(2)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M3 BIT(3)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M4 BIT(4)
+#define QPNP_ADC_TM_LOW_THR_INT_EN 0x42
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M0 BIT(0)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M1 BIT(1)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M2 BIT(2)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M3 BIT(3)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M4 BIT(4)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN 0x43
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M0 BIT(0)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M1 BIT(1)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M2 BIT(2)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M3 BIT(3)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M4 BIT(4)
+
+#define QPNP_ADC_TM_M0_MEAS_INTERVAL_CTL 0x57
+#define QPNP_ADC_TM_M1_MEAS_INTERVAL_CTL 0x6d
+#define QPNP_ADC_TM_M2_MEAS_INTERVAL_CTL 0x75
+#define QPNP_ADC_TM_M3_MEAS_INTERVAL_CTL 0x7d
+#define QPNP_ADC_TM_M4_MEAS_INTERVAL_CTL 0x85
+#define QPNP_ADC_TM_STATUS1 0x8
+#define QPNP_ADC_TM_STATUS_LOW 0xa
+#define QPNP_ADC_TM_STATUS_HIGH 0xb
+
+#define QPNP_ADC_TM_M0_LOW_THR 0x5d5c
+#define QPNP_ADC_TM_M0_HIGH_THR 0x5f5e
+#define QPNP_ADC_TM_MEAS_INTERVAL 0x0
+
+#define QPNP_ADC_TM_THR_LSB_MASK(val) (val & 0xff)
+#define QPNP_ADC_TM_THR_MSB_MASK(val) ((val & 0xff00) >> 8)
+
+struct qpnp_adc_tm_sensor {
+ struct thermal_zone_device *tz_dev;
+ enum thermal_device_mode mode;
+ uint32_t sensor_num;
+ enum qpnp_adc_meas_timer_select timer_select;
+ uint32_t meas_interval;
+ uint32_t low_thr;
+ uint32_t high_thr;
+ uint32_t btm_channel_num;
+ struct work_struct work;
+};
+
+struct qpnp_adc_tm_drv {
+ struct qpnp_adc_drv *adc;
+ struct qpnp_adc_tm_usbid_param *usb_id_param;
+ struct work_struct usbid_work;
+ struct qpnp_adc_tm_btm_param *battery_param;
+ struct work_struct batt_work;
+ bool adc_tm_initialized;
+ struct qpnp_adc_tm_sensor sensor[0];
+};
+
+struct qpnp_adc_tm_drv *qpnp_adc_tm;
+
+struct qpnp_adc_tm_trip_reg_type {
+ 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;
+};
+
+static struct qpnp_adc_tm_trip_reg_type adc_tm_data[] = {
+ [QPNP_ADC_TM_M0_ADC_CH_SEL_CTL] = {QPNP_M0_LOW_THR_LSB,
+ QPNP_M0_LOW_THR_MSB, QPNP_M0_HIGH_THR_LSB,
+ QPNP_M0_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M0,
+ QPNP_ADC_TM_LOW_THR_INT_EN_M0, QPNP_ADC_TM_HIGH_THR_INT_EN_M0},
+ [QPNP_ADC_TM_M1_ADC_CH_SEL_CTL] = {QPNP_M1_LOW_THR_LSB,
+ QPNP_M1_LOW_THR_MSB, QPNP_M1_HIGH_THR_LSB,
+ QPNP_M1_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M1,
+ QPNP_ADC_TM_LOW_THR_INT_EN_M1, QPNP_ADC_TM_HIGH_THR_INT_EN_M1},
+ [QPNP_ADC_TM_M2_ADC_CH_SEL_CTL] = {QPNP_M2_LOW_THR_LSB,
+ QPNP_M2_LOW_THR_MSB, QPNP_M2_HIGH_THR_LSB,
+ QPNP_M2_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M2,
+ QPNP_ADC_TM_LOW_THR_INT_EN_M2, QPNP_ADC_TM_HIGH_THR_INT_EN_M2},
+ [QPNP_ADC_TM_M3_ADC_CH_SEL_CTL] = {QPNP_M3_LOW_THR_LSB,
+ QPNP_M3_LOW_THR_MSB, QPNP_M3_HIGH_THR_LSB,
+ QPNP_M3_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M3,
+ QPNP_ADC_TM_LOW_THR_INT_EN_M3, QPNP_ADC_TM_HIGH_THR_INT_EN_M3},
+ [QPNP_ADC_TM_M4_ADC_CH_SEL_CTL] = {QPNP_M4_LOW_THR_LSB,
+ QPNP_M4_LOW_THR_MSB, QPNP_M4_HIGH_THR_LSB,
+ QPNP_M4_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M4,
+ QPNP_ADC_TM_LOW_THR_INT_EN_M4, QPNP_ADC_TM_HIGH_THR_INT_EN_M4},
+};
+
+static int32_t qpnp_adc_tm_read_reg(int16_t reg, u8 *data)
+{
+ struct qpnp_adc_tm_drv *adc_tm = qpnp_adc_tm;
+ int rc = 0;
+
+ rc = spmi_ext_register_readl(adc_tm->adc->spmi->ctrl,
+ adc_tm->adc->slave, (adc_tm->adc->offset + reg), data, 1);
+ if (rc < 0)
+ pr_err("adc-tm read reg %d failed with %d\n", reg, rc);
+
+ return rc;
+}
+
+static int32_t qpnp_adc_tm_write_reg(int16_t reg, u8 data)
+{
+ struct qpnp_adc_tm_drv *adc_tm = qpnp_adc_tm;
+ int rc = 0;
+ u8 *buf;
+
+ buf = &data;
+
+ rc = spmi_ext_register_writel(adc_tm->adc->spmi->ctrl,
+ adc_tm->adc->slave, (adc_tm->adc->offset + reg), buf, 1);
+ if (rc < 0)
+ pr_err("adc-tm write reg %d failed with %d\n", reg, rc);
+
+ return rc;
+}
+
+static int32_t qpnp_adc_tm_enable(bool state)
+{
+ int rc = 0;
+ u8 data = 0, enable_check = 0;
+
+ if (state) {
+ data = QPNP_ADC_TM_EN;
+ rc = qpnp_adc_tm_write_reg(QPNP_EN_CTL1,
+ data);
+ if (rc < 0)
+ pr_err("adc-tm enable failed\n");
+ } else {
+ rc = qpnp_adc_tm_read_reg(QPNP_ADC_TM_MULTI_MEAS_EN,
+ &enable_check);
+ if (rc < 0) {
+ pr_err("multi measurement read failed\n");
+ return rc;
+ }
+
+ if (!enable_check) {
+ data = 0;
+ rc = qpnp_adc_tm_write_reg(QPNP_EN_CTL1, data);
+ if (rc < 0)
+ pr_err("adc-tm disable failed\n");
+ }
+ }
+
+ return rc;
+}
+
+static int32_t qpnp_adc_tm_enable_req_sts_check(void)
+{
+ u8 status1;
+ int rc;
+
+ /* The VADC_TM bank needs to be disabled for new conversion request */
+ rc = qpnp_adc_tm_read_reg(QPNP_ADC_TM_STATUS1, &status1);
+ if (rc) {
+ pr_err("adc-tm read status1 failed\n");
+ return rc;
+ }
+
+ /* Disable the bank if a conversion is occuring */
+ if (status1 & QPNP_STATUS1_REQ_STS) {
+ rc = qpnp_adc_tm_write_reg(QPNP_EN_CTL1, 0);
+ if (rc < 0)
+ pr_err("adc-tm disable failed\n");
+ }
+
+ return rc;
+}
+
+static int32_t qpnp_adc_tm_mode_select(u8 mode_ctl)
+{
+ int rc;
+
+ /* VADC_BTM current sets mode to recurring measurements */
+ rc = qpnp_adc_tm_write_reg(QPNP_MODE_CTL, mode_ctl);
+ if (rc < 0)
+ pr_err("adc-tm write mode selection err\n");
+
+ return rc;
+}
+
+static int32_t qpnp_adc_tm_timer_interval_select(
+ struct qpnp_vadc_chan_properties *chan_prop)
+{
+ int rc;
+ u8 meas_interval_timer2 = 0;
+
+ /* Configure USB_ID to timer1, batt_therm to timer2 */
+ switch (chan_prop->timer_select) {
+ case ADC_MEAS_TIMER_SELECT1:
+ rc = qpnp_adc_tm_write_reg(QPNP_ADC_TM_MEAS_INTERVAL_CTL,
+ chan_prop->meas_interval1);
+ if (rc < 0) {
+ pr_err("timer1 configure failed\n");
+ return rc;
+ }
+ break;
+ case ADC_MEAS_TIMER_SELECT2:
+ rc = qpnp_adc_tm_read_reg(QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
+ &meas_interval_timer2);
+ if (rc < 0) {
+ pr_err("timer2 configure read failed\n");
+ return rc;
+ }
+ meas_interval_timer2 |=
+ (chan_prop->meas_interval2 <<
+ QPNP_ADC_TM_MEAS_INTERVAL_CTL2_SHIFT);
+ rc = qpnp_adc_tm_write_reg(QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
+ meas_interval_timer2);
+ if (rc < 0) {
+ pr_err("timer2 configure failed\n");
+ return rc;
+ }
+ break;
+ case ADC_MEAS_TIMER_SELECT3:
+ /* Thermal channels uses timer3, default to 1 second */
+ rc = qpnp_adc_tm_read_reg(QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
+ &meas_interval_timer2);
+ if (rc < 0) {
+ pr_err("timer3 read failed\n");
+ return rc;
+ }
+ chan_prop->meas_interval2 = ADC_MEAS3_INTERVAL_1S;
+ meas_interval_timer2 |= chan_prop->meas_interval2;
+ rc = qpnp_adc_tm_write_reg(QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
+ meas_interval_timer2);
+ if (rc < 0) {
+ pr_err("timer3 configure failed\n");
+ return rc;
+ }
+ break;
+ default:
+ pr_err("Invalid timer selection\n");
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int32_t qpnp_adc_tm_meas_int_update(uint16_t reg_addr_src,
+ u8 reg_addr_dst, bool state)
+{
+ u8 bit_mask_check = 0;
+ int rc = 0;
+
+ rc = qpnp_adc_tm_read_reg(reg_addr_src, &bit_mask_check);
+ if (rc < 0) {
+ pr_err("read failed for addr:%x\n", reg_addr_src);
+ return rc;
+ }
+
+ if (state)
+ bit_mask_check |= reg_addr_dst;
+ else
+ bit_mask_check &= ~reg_addr_dst;
+
+ rc = qpnp_adc_tm_write_reg(reg_addr_src, bit_mask_check);
+ if (rc < 0)
+ pr_err("write failed for addr:%x\n", reg_addr_src);
+
+ return rc;
+}
+
+static int32_t qpnp_adc_tm_usbid_btm_thr_en(uint32_t btm_chan,
+ struct qpnp_vadc_chan_properties *chan_prop)
+{
+ int rc = 0;
+
+ rc = qpnp_adc_tm_write_reg(
+ adc_tm_data[btm_chan].low_thr_lsb_addr,
+ QPNP_ADC_TM_THR_LSB_MASK(chan_prop->low_thr));
+ if (rc < 0) {
+ pr_err("low threshold lsb setting failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_write_reg(
+ adc_tm_data[btm_chan].low_thr_msb_addr,
+ QPNP_ADC_TM_THR_MSB_MASK(chan_prop->low_thr));
+ if (rc < 0) {
+ pr_err("low threshold msb setting failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_write_reg(
+ adc_tm_data[btm_chan].high_thr_lsb_addr,
+ QPNP_ADC_TM_THR_LSB_MASK(chan_prop->high_thr));
+ if (rc < 0) {
+ pr_err("high threshold lsb setting failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_write_reg(
+ adc_tm_data[btm_chan].high_thr_msb_addr,
+ QPNP_ADC_TM_THR_MSB_MASK(chan_prop->high_thr));
+ if (rc < 0)
+ pr_err("high threshold msb setting failed\n");
+
+ return rc;
+}
+
+static int32_t qpnp_adc_tm_channel_configure(uint32_t btm_chan,
+ struct qpnp_vadc_chan_properties *chan_prop,
+ uint32_t amux_channel)
+{
+ int rc = 0;
+
+ switch (btm_chan) {
+ case QPNP_ADC_TM_M0_ADC_CH_SEL_CTL:
+ case QPNP_ADC_TM_M1_ADC_CH_SEL_CTL:
+ /* Update low and high notification thresholds */
+ rc = qpnp_adc_tm_usbid_btm_thr_en(btm_chan,
+ chan_prop);
+ if (rc < 0) {
+ pr_err("setting chan:%d threshold failed\n", btm_chan);
+ return rc;
+ }
+
+ if ((chan_prop->state_request ==
+ ADC_TM_LOW_THR_ENABLE) ||
+ (chan_prop->state_request ==
+ ADC_TM_HIGH_LOW_THR_ENABLE)) {
+ /* Enable low threshold's interrupt */
+ rc = qpnp_adc_tm_meas_int_update(
+ QPNP_ADC_TM_LOW_THR_INT_EN,
+ adc_tm_data[btm_chan].low_thr_int_chan_en,
+ true);
+ if (rc < 0) {
+ pr_err("low thr enable err:%d\n", btm_chan);
+ return rc;
+ }
+ }
+
+ if ((chan_prop->state_request ==
+ ADC_TM_HIGH_THR_ENABLE) ||
+ (chan_prop->state_request ==
+ ADC_TM_HIGH_LOW_THR_ENABLE)) {
+ /* Enable high threshold's interrupt */
+ rc = qpnp_adc_tm_meas_int_update(
+ QPNP_ADC_TM_HIGH_THR_INT_EN,
+ adc_tm_data[btm_chan].high_thr_int_chan_en,
+ true);
+ if (rc < 0) {
+ pr_err("high thr enable err:%d\n", btm_chan);
+ return rc;
+ }
+ }
+ /* intention fall through to configure common chan meas */
+ case QPNP_ADC_TM_M2_ADC_CH_SEL_CTL:
+ case QPNP_ADC_TM_M3_ADC_CH_SEL_CTL:
+ case QPNP_ADC_TM_M4_ADC_CH_SEL_CTL:
+ /* Configure AMUX control register for channel selection */
+ rc = qpnp_adc_tm_write_reg(btm_chan, amux_channel);
+ if (rc < 0) {
+ pr_err("btm_chan:%d selection failed\n", btm_chan);
+ return rc;
+ }
+
+ /* Enable corresponding BTM channel measurement */
+ rc = qpnp_adc_tm_meas_int_update(
+ QPNP_ADC_TM_MULTI_MEAS_EN,
+ adc_tm_data[btm_chan].multi_meas_en, true);
+ if (rc < 0) {
+ pr_err("multi measurement en failed\n");
+ return rc;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int32_t qpnp_adc_tm_configure(
+ struct qpnp_adc_amux_properties *chan_prop)
+{
+ u8 decimation = 0;
+ int rc = 0;
+ uint32_t btm_chan = 0;
+
+ /* Check if a conversion is in progress */
+ rc = qpnp_adc_tm_enable_req_sts_check();
+ if (rc < 0) {
+ pr_err("adc-tm req_sts check failed\n");
+ return rc;
+ }
+
+ /* Set measurement in recurring mode */
+ rc = qpnp_adc_tm_mode_select(chan_prop->mode_sel);
+ if (rc < 0) {
+ pr_err("adc-tm mode select failed\n");
+ return rc;
+ }
+
+ /* Configure AMUX channel */
+ rc = qpnp_adc_tm_write_reg(QPNP_ADC_CH_SEL_CTL,
+ chan_prop->amux_channel);
+ if (rc < 0) {
+ pr_err("adc-tm channel selection err\n");
+ return rc;
+ }
+
+ /* Digital paramater setup */
+ decimation |= chan_prop->decimation <<
+ QPNP_ADC_DIG_DEC_RATIO_SEL_SHIFT;
+ rc = qpnp_adc_tm_write_reg(QPNP_ADC_DIG_PARAM, decimation);
+ if (rc < 0) {
+ pr_err("adc-tm digital parameter setup err\n");
+ return rc;
+ }
+
+ /* Hardware setting time */
+ rc = qpnp_adc_tm_write_reg(QPNP_HW_SETTLE_DELAY,
+ chan_prop->hw_settle_time);
+ if (rc < 0) {
+ pr_err("adc-tm hw settling time setup err\n");
+ return rc;
+ }
+
+ /* Fast averaging setup */
+ rc = qpnp_adc_tm_write_reg(QPNP_FAST_AVG_CTL,
+ chan_prop->fast_avg_setup);
+ if (rc < 0) {
+ pr_err("adc-tm fast-avg setup err\n");
+ return rc;
+ }
+
+ /* Measurement interval setup */
+ rc = qpnp_adc_tm_timer_interval_select(chan_prop->chan_prop);
+ if (rc < 0) {
+ pr_err("adc-tm timer select failed\n");
+ return rc;
+ }
+
+ /* Channel configuration setup */
+ btm_chan = chan_prop->chan_prop->tm_channel_select;
+ rc = qpnp_adc_tm_channel_configure(btm_chan, chan_prop->chan_prop,
+ chan_prop->amux_channel);
+ if (rc < 0) {
+ pr_err("adc-tm channel configure failed\n");
+ return rc;
+ }
+
+ /* Enable bank */
+ rc = qpnp_adc_tm_enable(true);
+ if (rc)
+ return rc;
+
+ /* Recurring interval measurement enable */
+ rc = qpnp_adc_tm_meas_int_update(QPNP_ADC_MEAS_INTERVAL_OP_CTL,
+ QPNP_ADC_MEAS_INTERVAL_OP, true);
+ if (rc < 0) {
+ pr_err("adc-tm meas interval op configure failed\n");
+ return rc;
+ }
+
+ /* Request conversion */
+ rc = qpnp_adc_tm_write_reg(QPNP_CONV_REQ, QPNP_CONV_REQ_SET);
+ if (rc < 0) {
+ pr_err("adc-tm request conversion failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int qpnp_adc_tm_get_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode *mode)
+{
+ struct qpnp_adc_tm_sensor *adc_tm = thermal->devdata;
+
+ if (!adc_tm || !mode)
+ return -EINVAL;
+
+ *mode = adc_tm->mode;
+
+ return 0;
+}
+
+static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode mode)
+{
+ struct qpnp_adc_tm_sensor *adc_tm = thermal->devdata;
+ struct qpnp_adc_tm_drv *adc_drv = qpnp_adc_tm;
+ int rc = 0, channel;
+
+ if (!adc_tm)
+ return -EINVAL;
+
+ if (mode == THERMAL_DEVICE_ENABLED) {
+ adc_drv->adc->amux_prop->amux_channel = adc_tm->sensor_num;
+ channel = adc_tm->sensor_num;
+ adc_drv->adc->amux_prop->decimation =
+ adc_drv->adc->adc_channels[channel].adc_decimation;
+ adc_drv->adc->amux_prop->hw_settle_time =
+ adc_drv->adc->adc_channels[channel].hw_settle_time;
+ adc_drv->adc->amux_prop->fast_avg_setup =
+ adc_drv->adc->adc_channels[channel].fast_avg_setup;
+ adc_drv->adc->amux_prop->mode_sel =
+ ADC_OP_MEASUREMENT_INTERVAL << QPNP_OP_MODE_SHIFT;
+ adc_drv->adc->amux_prop->chan_prop->timer_select =
+ ADC_MEAS_TIMER_SELECT1;
+ adc_drv->adc->amux_prop->chan_prop->meas_interval1 =
+ ADC_MEAS1_INTERVAL_1S;
+ adc_drv->adc->amux_prop->chan_prop->low_thr = adc_tm->low_thr;
+ adc_drv->adc->amux_prop->chan_prop->high_thr = adc_tm->high_thr;
+ adc_drv->adc->amux_prop->chan_prop->tm_channel_select =
+ adc_tm->btm_channel_num;
+
+ rc = qpnp_adc_tm_configure(adc_drv->adc->amux_prop);
+ if (rc) {
+ pr_err("adc-tm tm configure failed with %d\n", rc);
+ return -EINVAL;
+ }
+ } else if (mode == THERMAL_DEVICE_DISABLED) {
+ rc = qpnp_adc_tm_meas_int_update(QPNP_ADC_TM_MULTI_MEAS_EN,
+ adc_tm_data[adc_tm->btm_channel_num].multi_meas_en,
+ false);
+ if (rc < 0) {
+ pr_err("multi measurement update failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_enable(false);
+ if (rc < 0) {
+ pr_err("adc-tm disable failed\n");
+ return rc;
+ }
+ }
+
+ adc_tm->mode = mode;
+
+ return 0;
+}
+
+static int qpnp_adc_tm_get_trip_type(struct thermal_zone_device *thermal,
+ int trip, enum thermal_trip_type *type)
+{
+ struct qpnp_adc_tm_sensor *adc_tm = thermal->devdata;
+
+ if (!adc_tm || !type || type < 0)
+ return -EINVAL;
+
+ switch (trip) {
+ case ADC_TM_TRIP_HIGH_WARM:
+ *type = THERMAL_TRIP_CONFIGURABLE_HI;
+ break;
+ case ADC_TM_TRIP_LOW_COOL:
+ *type = THERMAL_TRIP_CONFIGURABLE_LOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int qpnp_adc_tm_get_trip_temp(struct thermal_zone_device *thermal,
+ int trip, unsigned long *temp)
+{
+ struct qpnp_adc_tm_sensor *adc_tm_sensor = thermal->devdata;
+ struct qpnp_adc_tm_drv *adc_tm = qpnp_adc_tm;
+ int64_t result = 0;
+ u8 trip_cool_thr0, trip_cool_thr1, trip_warm_thr0, trip_warm_thr1;
+ unsigned int reg, rc = 0, btm_channel_num;
+ uint16_t reg_low_thr_lsb, reg_low_thr_msb;
+ uint16_t reg_high_thr_lsb, reg_high_thr_msb;
+
+ if (!adc_tm)
+ return -EINVAL;
+
+ btm_channel_num = adc_tm_sensor->btm_channel_num;
+ reg_low_thr_lsb = adc_tm_data[btm_channel_num].low_thr_lsb_addr;
+ reg_low_thr_msb = adc_tm_data[btm_channel_num].low_thr_msb_addr;
+ reg_high_thr_lsb = adc_tm_data[btm_channel_num].high_thr_lsb_addr;
+ reg_high_thr_msb = adc_tm_data[btm_channel_num].high_thr_msb_addr;
+
+ switch (trip) {
+ case ADC_TM_TRIP_HIGH_WARM:
+ rc = qpnp_adc_tm_read_reg(reg_low_thr_lsb, &trip_warm_thr0);
+ if (rc) {
+ pr_err("adc-tm low_thr_lsb err\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_read_reg(reg_low_thr_msb, &trip_warm_thr1);
+ if (rc) {
+ pr_err("adc-tm low_thr_msb err\n");
+ return rc;
+ }
+ reg = (trip_warm_thr1 << 8) | trip_warm_thr0;
+ break;
+ case ADC_TM_TRIP_LOW_COOL:
+ rc = qpnp_adc_tm_read_reg(reg_high_thr_lsb, &trip_cool_thr0);
+ if (rc) {
+ pr_err("adc-tm_tm high_thr_lsb err\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_read_reg(reg_high_thr_msb, &trip_cool_thr1);
+ if (rc) {
+ pr_err("adc-tm_tm high_thr_lsb err\n");
+ return rc;
+ }
+ reg = (trip_cool_thr1 << 8) | trip_cool_thr0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rc = qpnp_adc_tm_scale_voltage_therm_pu2(reg, &result);
+ if (rc < 0) {
+ pr_err("Failed to lookup the therm thresholds\n");
+ return rc;
+ }
+
+ *temp = result;
+
+ return 0;
+}
+
+static int qpnp_adc_tm_set_trip_temp(struct thermal_zone_device *thermal,
+ int trip, long temp)
+{
+ struct qpnp_adc_tm_sensor *adc_tm_sensor = thermal->devdata;
+ struct qpnp_adc_tm_drv *adc_tm = qpnp_adc_tm;
+ struct qpnp_adc_tm_config tm_config;
+ u8 trip_cool_thr0, trip_cool_thr1, trip_warm_thr0, trip_warm_thr1;
+ uint16_t reg_low_thr_lsb, reg_low_thr_msb;
+ uint16_t reg_high_thr_lsb, reg_high_thr_msb;
+ int rc = 0, btm_channel_num;
+
+ if (!adc_tm)
+ return -EINVAL;
+
+ tm_config.channel = adc_tm_sensor->sensor_num;
+ switch (trip) {
+ case ADC_TM_TRIP_HIGH_WARM:
+ tm_config.high_thr_temp = temp;
+ break;
+ case ADC_TM_TRIP_LOW_COOL:
+ tm_config.low_thr_temp = temp;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rc = qpnp_adc_tm_scale_therm_voltage_pu2(&tm_config);
+ if (rc < 0) {
+ pr_err("Failed to lookup the adc-tm thresholds\n");
+ return rc;
+ }
+
+ trip_warm_thr0 = ((tm_config.low_thr_voltage << 24) >> 24);
+ trip_warm_thr1 = ((tm_config.low_thr_voltage << 16) >> 24);
+ trip_cool_thr0 = ((tm_config.high_thr_voltage << 24) >> 24);
+ trip_cool_thr1 = ((tm_config.high_thr_voltage << 16) >> 24);
+
+ btm_channel_num = adc_tm_sensor->btm_channel_num;
+ reg_low_thr_lsb = adc_tm_data[btm_channel_num].low_thr_lsb_addr;
+ reg_low_thr_msb = adc_tm_data[btm_channel_num].low_thr_msb_addr;
+ reg_high_thr_lsb = adc_tm_data[btm_channel_num].high_thr_lsb_addr;
+ reg_high_thr_msb = adc_tm_data[btm_channel_num].high_thr_msb_addr;
+
+ switch (trip) {
+ case ADC_TM_TRIP_HIGH_WARM:
+ rc = qpnp_adc_tm_write_reg(reg_low_thr_lsb, trip_cool_thr0);
+ if (rc) {
+ pr_err("adc-tm_tm read threshold err\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_write_reg(reg_low_thr_msb, trip_cool_thr1);
+ if (rc) {
+ pr_err("adc-tm_tm read threshold err\n");
+ return rc;
+ }
+ adc_tm_sensor->low_thr = tm_config.high_thr_voltage;
+ break;
+ case ADC_TM_TRIP_LOW_COOL:
+ rc = qpnp_adc_tm_write_reg(reg_high_thr_lsb, trip_warm_thr0);
+ if (rc) {
+ pr_err("adc-tm_tm read threshold err\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_write_reg(reg_high_thr_msb, trip_warm_thr1);
+ if (rc) {
+ pr_err("adc-tm_tm read threshold err\n");
+ return rc;
+ }
+ adc_tm_sensor->high_thr = tm_config.low_thr_voltage;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void notify_uspace_qpnp_adc_tm_fn(struct work_struct *work)
+{
+ struct qpnp_adc_tm_sensor *adc_tm = container_of(work,
+ struct qpnp_adc_tm_sensor, work);
+
+ sysfs_notify(&adc_tm->tz_dev->device.kobj,
+ NULL, "btm");
+}
+
+static void notify_usb_fn(struct work_struct *work)
+{
+ struct qpnp_adc_tm_drv *adc_tm = container_of(work,
+ struct qpnp_adc_tm_drv, usbid_work);
+ int rc;
+ u8 status_low, status_high;
+
+ if (adc_tm->usb_id_param->threshold_notification != NULL) {
+ rc = qpnp_adc_tm_read_reg(QPNP_ADC_TM_STATUS_LOW,
+ &status_low);
+ if (rc) {
+ pr_err("adc-tm read low status failed\n");
+ return;
+ }
+
+ rc = qpnp_adc_tm_read_reg(QPNP_ADC_TM_STATUS_HIGH,
+ &status_high);
+ if (rc) {
+ pr_err("adc-tm read high status failed\n");
+ return;
+ }
+
+ if (status_low & 1)
+ adc_tm->usb_id_param->threshold_notification(
+ ADC_TM_LOW_STATE, adc_tm->usb_id_param->usbid_ctx);
+ else if (status_high & 1)
+ adc_tm->usb_id_param->threshold_notification(
+ ADC_TM_HIGH_STATE, adc_tm->usb_id_param->usbid_ctx);
+ }
+
+ return;
+}
+
+static void notify_batt_fn(struct work_struct *work)
+{
+ struct qpnp_adc_tm_drv *adc_tm = container_of(work,
+ struct qpnp_adc_tm_drv, batt_work);
+ int rc;
+ u8 status_low, status_high;
+
+ if (adc_tm->battery_param->threshold_notification != NULL) {
+ rc = qpnp_adc_tm_read_reg(QPNP_ADC_TM_STATUS_LOW,
+ &status_low);
+ if (rc) {
+ pr_err("adc-tm read low status failed\n");
+ return;
+ }
+
+ rc = qpnp_adc_tm_read_reg(QPNP_ADC_TM_STATUS_HIGH,
+ &status_high);
+ if (rc) {
+ pr_err("adc-tm read high status failed\n");
+ return;
+ }
+
+ if (status_low & QPNP_ADC_TM_LOW_THR_INT_EN_M1)
+ adc_tm->battery_param->threshold_notification(
+ ADC_TM_LOW_STATE);
+ else if (status_high & QPNP_ADC_TM_HIGH_THR_INT_EN_M1)
+ adc_tm->battery_param->threshold_notification(
+ ADC_TM_HIGH_STATE);
+ }
+
+ return;
+}
+
+static int qpnp_adc_tm_activate_trip_type_fn(uint32_t btm_channel_num,
+ enum thermal_trip_activation_mode mode, u8 *data, uint32_t reg)
+{
+ u8 thr_int = 0;
+ int rc = 0;
+
+ rc = qpnp_adc_tm_read_reg(reg, &thr_int);
+ if (rc) {
+ pr_err("multi meas read failed\n");
+ return rc;
+ }
+
+ thr_int = adc_tm_data[btm_channel_num].low_thr_int_chan_en;
+
+ if (mode == THERMAL_TRIP_ACTIVATION_ENABLED)
+ thr_int |= *data;
+ else
+ thr_int &= ~*data;
+
+ rc = qpnp_adc_tm_write_reg(reg, thr_int);
+ if (rc)
+ pr_err("multi meas write failed\n");
+
+ return rc;
+}
+
+static int qpnp_adc_tm_activate_trip_type(struct thermal_zone_device *thermal,
+ int trip, enum thermal_trip_activation_mode mode)
+{
+ struct qpnp_adc_tm_sensor *adc_tm = thermal->devdata;
+ int rc = 0;
+ u8 thr_int_en = 0;
+
+ if (!adc_tm)
+ return -EINVAL;
+
+ switch (trip) {
+ case ADC_TM_TRIP_HIGH_WARM:
+ thr_int_en = adc_tm_data[adc_tm->btm_channel_num].
+ low_thr_int_chan_en;
+ rc = qpnp_adc_tm_activate_trip_type_fn(adc_tm->btm_channel_num,
+ mode, &thr_int_en, QPNP_ADC_TM_LOW_THR_INT_EN);
+ if (rc)
+ pr_err("channel:%x failed\n", adc_tm->btm_channel_num);
+ break;
+ case ADC_TM_TRIP_LOW_COOL:
+ thr_int_en = adc_tm_data[adc_tm->btm_channel_num].
+ high_thr_int_chan_en;
+ rc = qpnp_adc_tm_activate_trip_type_fn(adc_tm->btm_channel_num,
+ mode, &thr_int_en, QPNP_ADC_TM_HIGH_THR_INT_EN);
+ if (rc)
+ pr_err("channel:%x failed\n", adc_tm->btm_channel_num);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int qpnp_adc_tm_read_status(void)
+{
+ struct qpnp_adc_tm_drv *adc_tm = qpnp_adc_tm;
+ u8 status_low = 0, status_high = 0, qpnp_adc_tm_meas_en = 0;
+ u8 adc_tm_low_enable = 0, adc_tm_high_enable = 0;
+ u8 thr_int_disable = 0;
+ int rc = 0, sensor_notify_num = 0;
+
+ rc = qpnp_adc_tm_read_reg(QPNP_ADC_TM_STATUS_LOW, &status_low);
+ if (rc) {
+ pr_err("adc-tm-tm read status low failed with %d\n", rc);
+ goto fail;
+ }
+
+ rc = qpnp_adc_tm_read_reg(QPNP_ADC_TM_STATUS_HIGH, &status_high);
+ if (rc) {
+ pr_err("adc-tm-tm read status high failed with %d\n", rc);
+ goto fail;
+ }
+
+ /* Check which interrupt threshold is lower and measure against the
+ * enabled channel */
+ rc = qpnp_adc_tm_read_reg(QPNP_ADC_TM_MULTI_MEAS_EN,
+ &qpnp_adc_tm_meas_en);
+ if (rc) {
+ pr_err("adc-tm-tm read status high failed with %d\n", rc);
+ goto fail;
+ }
+
+ adc_tm_low_enable = qpnp_adc_tm_meas_en & status_low;
+ adc_tm_high_enable = qpnp_adc_tm_meas_en & status_high;
+
+ if (adc_tm_high_enable) {
+ sensor_notify_num = (adc_tm_high_enable >> 3);
+ switch (adc_tm_high_enable) {
+ case 1:
+ case 2:
+ {
+ if (adc_tm_high_enable == 1)
+ thr_int_disable =
+ QPNP_ADC_TM_HIGH_THR_INT_EN_M0;
+ else if (adc_tm_high_enable == 2)
+ thr_int_disable =
+ QPNP_ADC_TM_HIGH_THR_INT_EN_M1;
+
+ rc = qpnp_adc_tm_meas_int_update(
+ QPNP_ADC_TM_HIGH_THR_INT_EN,
+ thr_int_disable, false);
+ if (rc < 0) {
+ pr_err("high threshold int read failed\n");
+ goto fail;
+ }
+
+ if (adc_tm_high_enable == 1)
+ schedule_work(&adc_tm->usbid_work);
+ else if (adc_tm_high_enable == 2)
+ schedule_work(&adc_tm->batt_work);
+ }
+ break;
+ case 4:
+ case 8:
+ case 16:
+ {
+ /* High voltage threshold is triggered by low temp */
+ rc = qpnp_adc_tm_activate_trip_type(
+ adc_tm->sensor[sensor_notify_num].tz_dev,
+ ADC_TM_TRIP_LOW_COOL,
+ THERMAL_TRIP_ACTIVATION_DISABLED);
+ if (rc < 0) {
+ pr_err("notify error:%d\n", sensor_notify_num);
+ goto fail;
+ }
+ schedule_work(&adc_tm->sensor[sensor_notify_num].work);
+ }
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ }
+
+ if (adc_tm_low_enable) {
+ sensor_notify_num = (adc_tm_low_enable >> 3);
+ switch (adc_tm_low_enable) {
+ case 1:
+ case 2:
+ {
+ if (adc_tm_low_enable == 1)
+ thr_int_disable = QPNP_ADC_TM_LOW_THR_INT_EN_M0;
+ else if (adc_tm_low_enable == 2)
+ thr_int_disable = QPNP_ADC_TM_LOW_THR_INT_EN_M1;
+
+ rc = qpnp_adc_tm_meas_int_update(
+ QPNP_ADC_TM_LOW_THR_INT_EN,
+ thr_int_disable, false);
+ if (rc < 0) {
+ pr_err("low threshold int disable failed\n");
+ goto fail;
+ }
+
+ if (adc_tm_low_enable == 1)
+ schedule_work(&adc_tm->usbid_work);
+ else if (adc_tm_low_enable == 2)
+ schedule_work(&adc_tm->batt_work);
+ }
+ break;
+ case 4:
+ case 8:
+ case 16:
+ {
+ /* Low voltage threshold is triggered by high temp */
+ rc = qpnp_adc_tm_activate_trip_type(
+ adc_tm->sensor[sensor_notify_num].tz_dev,
+ ADC_TM_TRIP_HIGH_WARM,
+ THERMAL_TRIP_ACTIVATION_DISABLED);
+ if (rc < 0) {
+ pr_err("notify error:%d\n", sensor_notify_num);
+ goto fail;
+ }
+ schedule_work(&adc_tm->sensor[sensor_notify_num].work);
+ }
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ }
+
+fail:
+ return rc;
+}
+
+static void qpnp_adc_tm_high_thr_work(struct work_struct *work)
+{
+ int rc;
+
+ rc = qpnp_adc_tm_read_status();
+
+ return;
+}
+DECLARE_WORK(trigger_completion_adc_tm_high_thr_work,
+ qpnp_adc_tm_high_thr_work);
+
+static irqreturn_t qpnp_adc_tm_high_thr_isr(int irq, void *data)
+{
+ schedule_work(&trigger_completion_adc_tm_high_thr_work);
+
+ return IRQ_HANDLED;
+}
+
+static void qpnp_adc_tm_low_thr_work(struct work_struct *work)
+{
+ int rc;
+
+ rc = qpnp_adc_tm_read_status();
+
+ return;
+}
+DECLARE_WORK(trigger_completion_adc_tm_low_thr_work, qpnp_adc_tm_low_thr_work);
+
+static irqreturn_t qpnp_adc_tm_low_thr_isr(int irq, void *data)
+{
+ schedule_work(&trigger_completion_adc_tm_low_thr_work);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_adc_tm_isr(int irq, void *dev_id)
+{
+ struct qpnp_adc_tm_drv *adc_tm = dev_id;
+
+ complete(&adc_tm->adc->adc_rslt_completion);
+
+ return IRQ_HANDLED;
+}
+
+static int qpnp_adc_read_temp(struct thermal_zone_device *thermal,
+ unsigned long *temp)
+{
+ struct qpnp_adc_tm_sensor *adc_tm_sensor = thermal->devdata;
+ struct qpnp_vadc_result result;
+ int rc = 0;
+
+ rc = qpnp_vadc_read(adc_tm_sensor->sensor_num, &result);
+ if (rc)
+ return rc;
+
+ *temp = result.physical;
+
+ return rc;
+}
+
+static struct thermal_zone_device_ops qpnp_adc_tm_thermal_ops = {
+ .get_temp = qpnp_adc_read_temp,
+ .get_mode = qpnp_adc_tm_get_mode,
+ .set_mode = qpnp_adc_tm_set_mode,
+ .get_trip_type = qpnp_adc_tm_get_trip_type,
+ .activate_trip_type = qpnp_adc_tm_activate_trip_type,
+ .get_trip_temp = qpnp_adc_tm_get_trip_temp,
+ .set_trip_temp = qpnp_adc_tm_set_trip_temp,
+};
+
+int32_t qpnp_adc_tm_usbid_configure(struct qpnp_adc_tm_usbid_param *param)
+{
+ struct qpnp_adc_tm_drv *adc_tm = qpnp_adc_tm;
+ uint32_t channel;
+ int rc = 0;
+
+ if (!adc_tm || !adc_tm->adc_tm_initialized)
+ return -ENODEV;
+
+ if (param->threshold_notification == NULL) {
+ pr_err("No USB_ID high/low voltage notificaton??\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&adc_tm->adc->adc_lock);
+
+ adc_tm->adc->amux_prop->amux_channel = LR_MUX10_PU2_AMUX_USB_ID_LV;
+ channel = LR_MUX10_PU2_AMUX_USB_ID_LV;
+ adc_tm->adc->amux_prop->decimation =
+ adc_tm->adc->adc_channels[channel].adc_decimation;
+ adc_tm->adc->amux_prop->hw_settle_time =
+ adc_tm->adc->adc_channels[channel].hw_settle_time;
+ adc_tm->adc->amux_prop->fast_avg_setup =
+ adc_tm->adc->adc_channels[channel].fast_avg_setup;
+ adc_tm->adc->amux_prop->mode_sel =
+ ADC_OP_MEASUREMENT_INTERVAL << QPNP_OP_MODE_SHIFT;
+ adc_tm->adc->amux_prop->chan_prop->meas_interval1 =
+ ADC_MEAS1_INTERVAL_1S;
+ qpnp_adc_usb_scaler(param, &adc_tm->adc->amux_prop->chan_prop->low_thr,
+ &adc_tm->adc->amux_prop->chan_prop->high_thr);
+ adc_tm->adc->amux_prop->chan_prop->tm_channel_select =
+ QPNP_ADC_TM_M0_ADC_CH_SEL_CTL;
+ adc_tm->adc->amux_prop->chan_prop->timer_select =
+ ADC_MEAS_TIMER_SELECT1;
+ adc_tm->adc->amux_prop->chan_prop->state_request =
+ param->state_request;
+ rc = qpnp_adc_tm_configure(adc_tm->adc->amux_prop);
+ if (rc) {
+ pr_err("adc-tm configure failed with %d\n", rc);
+ goto fail_unlock;
+ }
+
+ adc_tm->usb_id_param = param;
+
+fail_unlock:
+ mutex_unlock(&adc_tm->adc->adc_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(qpnp_adc_tm_usbid_configure);
+
+static int32_t qpnp_adc_tm_chan_usbid_chan_btm_end(
+ uint32_t btm_chan_num)
+{
+ int32_t rc = 0;
+
+ rc = qpnp_adc_tm_meas_int_update(QPNP_ADC_TM_LOW_THR_INT_EN,
+ adc_tm_data[btm_chan_num].low_thr_int_chan_en,
+ false);
+ if (rc < 0) {
+ pr_err("low threshold int write failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_meas_int_update(QPNP_ADC_TM_HIGH_THR_INT_EN,
+ adc_tm_data[btm_chan_num].high_thr_int_chan_en,
+ false);
+ if (rc < 0) {
+ pr_err("high threshold int enable failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_meas_int_update(QPNP_ADC_TM_MULTI_MEAS_EN,
+ adc_tm_data[btm_chan_num].multi_meas_en,
+ false);
+ if (rc < 0) {
+ pr_err("multi measurement en failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_enable(false);
+ if (rc < 0)
+ pr_err("TM disable failed\n");
+
+ return rc;
+}
+
+int32_t qpnp_adc_tm_usbid_end(void)
+{
+ struct qpnp_adc_tm_drv *adc_tm = qpnp_adc_tm;
+ int rc = 0;
+
+ if (!adc_tm || !adc_tm->adc_tm_initialized)
+ return -ENODEV;
+
+ mutex_lock(&adc_tm->adc->adc_lock);
+
+ rc = qpnp_adc_tm_chan_usbid_chan_btm_end(
+ QPNP_ADC_TM_M0_ADC_CH_SEL_CTL);
+ if (rc < 0)
+ pr_err("disabling thresholds for usb channel failed\n");
+
+ mutex_unlock(&adc_tm->adc->adc_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(qpnp_adc_tm_usbid_end);
+
+int32_t qpnp_adc_tm_btm_configure(struct qpnp_adc_tm_btm_param *param)
+{
+ struct qpnp_adc_tm_drv *adc_tm = qpnp_adc_tm;
+ uint32_t channel;
+ int rc = 0;
+
+ if (!adc_tm || !adc_tm->adc_tm_initialized)
+ return -ENODEV;
+
+ if (param->threshold_notification == NULL) {
+ pr_err("No battery high/low temp notificaton??\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&adc_tm->adc->adc_lock);
+
+ adc_tm->adc->amux_prop->amux_channel = LR_MUX1_BATT_THERM;
+ channel = LR_MUX1_BATT_THERM;
+ adc_tm->adc->amux_prop->decimation =
+ adc_tm->adc->adc_channels[channel].adc_decimation;
+ adc_tm->adc->amux_prop->hw_settle_time =
+ adc_tm->adc->adc_channels[channel].hw_settle_time;
+ adc_tm->adc->amux_prop->fast_avg_setup =
+ adc_tm->adc->adc_channels[channel].fast_avg_setup;
+ adc_tm->adc->amux_prop->mode_sel =
+ ADC_OP_MEASUREMENT_INTERVAL << QPNP_OP_MODE_SHIFT;
+ adc_tm->adc->amux_prop->chan_prop->meas_interval2 =
+ ADC_MEAS2_INTERVAL_1S;
+ qpnp_adc_btm_scaler(param, &adc_tm->adc->amux_prop->chan_prop->low_thr,
+ &adc_tm->adc->amux_prop->chan_prop->high_thr);
+ adc_tm->adc->amux_prop->chan_prop->tm_channel_select =
+ QPNP_ADC_TM_M1_ADC_CH_SEL_CTL;
+ adc_tm->adc->amux_prop->chan_prop->timer_select =
+ ADC_MEAS_TIMER_SELECT2;
+ adc_tm->adc->amux_prop->chan_prop->state_request =
+ param->state_request;
+ rc = qpnp_adc_tm_configure(adc_tm->adc->amux_prop);
+ if (rc) {
+ pr_err("adc-tm configure failed with %d\n", rc);
+ goto fail_unlock;
+ }
+
+ adc_tm->battery_param = param;
+
+fail_unlock:
+ mutex_unlock(&adc_tm->adc->adc_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(qpnp_adc_tm_btm_configure);
+
+int32_t qpnp_adc_tm_btm_end(void)
+{
+ struct qpnp_adc_tm_drv *adc_tm = qpnp_adc_tm;
+ int rc = 0;
+
+ if (!adc_tm || !adc_tm->adc_tm_initialized)
+ return -ENODEV;
+
+ mutex_lock(&adc_tm->adc->adc_lock);
+
+ rc = qpnp_adc_tm_chan_usbid_chan_btm_end(
+ QPNP_ADC_TM_M1_ADC_CH_SEL_CTL);
+ if (rc < 0)
+ pr_err("disabling thresholds for batt channel failed\n");
+
+ mutex_unlock(&adc_tm->adc->adc_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(qpnp_adc_tm_btm_end);
+
+int32_t qpnp_adc_tm_is_ready(void)
+{
+ struct qpnp_adc_tm_drv *adc_tm = qpnp_adc_tm;
+
+ if (!adc_tm || !adc_tm->adc_tm_initialized)
+ return -EPROBE_DEFER;
+ else
+ return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_tm_is_ready);
+
+static int __devinit qpnp_adc_tm_probe(struct spmi_device *spmi)
+{
+ struct device_node *node = spmi->dev.of_node, *child;
+ struct qpnp_adc_tm_drv *adc_tm;
+ struct qpnp_adc_drv *adc_qpnp;
+ int32_t count_adc_channel_list = 0, rc, i = 0, j = 0;
+ u8 thr_init = 0;
+
+ if (!node)
+ return -EINVAL;
+
+ if (qpnp_adc_tm) {
+ pr_err("adc-tm already in use\n");
+ return -EBUSY;
+ }
+
+ for_each_child_of_node(node, child)
+ count_adc_channel_list++;
+
+ if (!count_adc_channel_list) {
+ pr_err("No channel listing\n");
+ return -EINVAL;
+ }
+
+ adc_tm = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_adc_tm_drv) +
+ (count_adc_channel_list *
+ sizeof(struct qpnp_adc_tm_sensor)),
+ GFP_KERNEL);
+ if (!adc_tm) {
+ dev_err(&spmi->dev, "Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ adc_qpnp = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_adc_drv),
+ GFP_KERNEL);
+ if (!adc_qpnp) {
+ dev_err(&spmi->dev, "Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ adc_tm->adc = adc_qpnp;
+
+ rc = qpnp_adc_get_devicetree_data(spmi, adc_tm->adc);
+ if (rc) {
+ dev_err(&spmi->dev, "failed to read device tree\n");
+ return rc;
+ }
+
+ /* Register the ADC peripheral interrupt */
+ adc_tm->adc->adc_high_thr_irq = spmi_get_irq_byname(spmi,
+ NULL, "high-thr-en-set");
+ if (adc_tm->adc->adc_high_thr_irq < 0) {
+ pr_err("Invalid irq\n");
+ return -ENXIO;
+ }
+
+ adc_tm->adc->adc_low_thr_irq = spmi_get_irq_byname(spmi,
+ NULL, "low-thr-en-set");
+ if (adc_tm->adc->adc_low_thr_irq < 0) {
+ pr_err("Invalid irq\n");
+ return -ENXIO;
+ }
+
+ rc = devm_request_irq(&spmi->dev, adc_tm->adc->adc_irq_eoc,
+ qpnp_adc_tm_isr, IRQF_TRIGGER_RISING,
+ "qpnp_adc_tm_interrupt", adc_tm);
+ if (rc) {
+ dev_err(&spmi->dev,
+ "failed to request adc irq with error %d\n", rc);
+ return rc;
+ } else {
+ enable_irq_wake(adc_tm->adc->adc_irq_eoc);
+ }
+
+ rc = devm_request_irq(&spmi->dev, adc_tm->adc->adc_high_thr_irq,
+ qpnp_adc_tm_high_thr_isr,
+ IRQF_TRIGGER_RISING, "qpnp_adc_tm_high_interrupt", adc_tm);
+ if (rc) {
+ dev_err(&spmi->dev, "failed to request adc irq\n");
+ return rc;
+ } else {
+ enable_irq_wake(adc_tm->adc->adc_high_thr_irq);
+ }
+
+ rc = devm_request_irq(&spmi->dev, adc_tm->adc->adc_low_thr_irq,
+ qpnp_adc_tm_low_thr_isr,
+ IRQF_TRIGGER_RISING, "qpnp_adc_tm_low_interrupt", adc_tm);
+ if (rc) {
+ dev_err(&spmi->dev, "failed to request adc irq\n");
+ return rc;
+ } else {
+ enable_irq_wake(adc_tm->adc->adc_low_thr_irq);
+ }
+
+ for_each_child_of_node(node, child) {
+ char name[25];
+ int btm_channel_num;
+ rc = of_property_read_u32(child,
+ "qcom,btm-channel-number", &btm_channel_num);
+ if (rc) {
+ pr_err("Invalid btm channel number\n");
+ return -EINVAL;
+ }
+
+ if ((btm_channel_num != QPNP_ADC_TM_M0_ADC_CH_SEL_CTL) &&
+ (btm_channel_num != QPNP_ADC_TM_M1_ADC_CH_SEL_CTL)) {
+ /* Register with the thermal zone */
+ adc_tm->sensor[i].mode = THERMAL_DEVICE_DISABLED;
+ snprintf(name, sizeof(name), "qpnp_adc_tm_sensor%d", i);
+ adc_tm->sensor[i].sensor_num =
+ adc_tm->adc->adc_channels[j].channel_num;
+ adc_tm->sensor[i].btm_channel_num = btm_channel_num;
+ adc_tm->sensor[i].meas_interval =
+ QPNP_ADC_TM_MEAS_INTERVAL;
+ adc_tm->sensor[i].low_thr = QPNP_ADC_TM_M0_LOW_THR;
+ adc_tm->sensor[i].high_thr = QPNP_ADC_TM_M0_HIGH_THR;
+ adc_tm->sensor[i].tz_dev =
+ thermal_zone_device_register(name,
+ ADC_TM_TRIP_NUM,
+ &adc_tm->sensor[i],
+ &qpnp_adc_tm_thermal_ops, 0, 0, 0, 0);
+ if (IS_ERR(adc_tm->sensor[i].tz_dev))
+ pr_err("thermal device register failed.\n");
+ INIT_WORK(&adc_tm->sensor[i].work,
+ notify_uspace_qpnp_adc_tm_fn);
+ i++;
+ }
+ j++;
+ }
+ INIT_WORK(&adc_tm->usbid_work, notify_usb_fn);
+ INIT_WORK(&adc_tm->batt_work, notify_batt_fn);
+ qpnp_adc_tm = adc_tm;
+ dev_set_drvdata(&spmi->dev, adc_tm);
+ rc = qpnp_adc_tm_write_reg(QPNP_ADC_TM_HIGH_THR_INT_EN, thr_init);
+ if (rc < 0) {
+ pr_err("high thr init failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_write_reg(QPNP_ADC_TM_LOW_THR_INT_EN, thr_init);
+ if (rc < 0) {
+ pr_err("low thr init failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_write_reg(QPNP_ADC_TM_MULTI_MEAS_EN, thr_init);
+ if (rc < 0) {
+ pr_err("multi meas en failed\n");
+ return rc;
+ }
+
+ adc_tm->adc_tm_initialized = true;
+
+ return 0;
+}
+
+static int __devexit qpnp_adc_tm_remove(struct spmi_device *spmi)
+{
+ struct qpnp_adc_tm_drv *adc_tm = dev_get_drvdata(&spmi->dev);
+ struct device_node *node = spmi->dev.of_node;
+ struct device_node *child;
+ int i = 0;
+
+ for_each_child_of_node(node, child) {
+ thermal_zone_device_unregister(adc_tm->sensor[i].tz_dev);
+ i++;
+ }
+
+ adc_tm->adc_tm_initialized = false;
+ dev_set_drvdata(&spmi->dev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id qpnp_adc_tm_match_table[] = {
+ { .compatible = "qcom,qpnp-adc-tm" },
+ {}
+};
+
+static struct spmi_driver qpnp_adc_tm_driver = {
+ .driver = {
+ .name = "qcom,qpnp-adc-tm",
+ .of_match_table = qpnp_adc_tm_match_table,
+ },
+ .probe = qpnp_adc_tm_probe,
+ .remove = qpnp_adc_tm_remove,
+};
+
+static int __init qpnp_adc_tm_init(void)
+{
+ return spmi_driver_register(&qpnp_adc_tm_driver);
+}
+module_init(qpnp_adc_tm_init);
+
+static void __exit qpnp_adc_tm_exit(void)
+{
+ spmi_driver_unregister(&qpnp_adc_tm_driver);
+}
+module_exit(qpnp_adc_tm_exit);
+
+MODULE_DESCRIPTION("QPNP PMIC ADC Threshold Monitoring driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/msm_serial_hs_lite.c b/drivers/tty/serial/msm_serial_hs_lite.c
index 0189a07..72a12d1 100644
--- a/drivers/tty/serial/msm_serial_hs_lite.c
+++ b/drivers/tty/serial/msm_serial_hs_lite.c
@@ -669,15 +669,15 @@
* Check requested baud rate and for higher baud rate than 460800,
* calculate required uart clock frequency and set the same.
*/
- if (baud > 460800) {
-
+ if (baud > 460800)
port->uartclk = baud * 16;
- if (clk_set_rate(msm_hsl_port->clk, port->uartclk)) {
- pr_err("%s(): Error setting uartclk rate %u\n",
- __func__, port->uartclk);
- WARN_ON(1);
- return;
- }
+ else
+ port->uartclk = 7372800;
+
+ if (clk_set_rate(msm_hsl_port->clk, port->uartclk)) {
+ pr_err("Error: setting uartclk rate %u\n", port->uartclk);
+ WARN_ON(1);
+ return;
}
/* Set timeout to be ~600x the character transmit time */
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index c0b4b57..265a685 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -300,19 +300,7 @@
}
}
-/* XHCI reset, resets other CORE registers as well, re-init those */
-void dwc3_post_host_reset_core_init(struct dwc3 *dwc)
-{
- /*
- * XHCI reset clears EVENT buffer register as well, re-init
- * EVENT buffers and also do device specific re-initialization
- */
- dwc3_event_buffers_setup(dwc);
-
- dwc3_gadget_restart(dwc);
-}
-
-static void __devinit dwc3_cache_hwparams(struct dwc3 *dwc)
+static void dwc3_cache_hwparams(struct dwc3 *dwc)
{
struct dwc3_hwparams *parms = &dwc->hwparams;
@@ -333,7 +321,7 @@
*
* Returns 0 on success otherwise negative errno.
*/
-static int __devinit dwc3_core_init(struct dwc3 *dwc)
+static int dwc3_core_init(struct dwc3 *dwc)
{
unsigned long timeout;
u32 reg;
@@ -434,11 +422,13 @@
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
}
- ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
- if (ret) {
- dev_err(dwc->dev, "failed to allocate event buffers\n");
- ret = -ENOMEM;
- goto err1;
+ if (!dwc->ev_buffs) {
+ ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
+ if (ret) {
+ dev_err(dwc->dev, "failed to allocate event buffers\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
}
ret = dwc3_event_buffers_setup(dwc);
@@ -462,6 +452,13 @@
dwc3_free_event_buffers(dwc);
}
+/* XHCI reset, resets other CORE registers as well, re-init those */
+void dwc3_post_host_reset_core_init(struct dwc3 *dwc)
+{
+ dwc3_core_init(dwc);
+ dwc3_gadget_restart(dwc);
+}
+
#define DWC3_ALIGN_MASK (16 - 1)
static int __devinit dwc3_probe(struct platform_device *pdev)
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 2b0a79f..cb1da06 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, 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
@@ -34,16 +34,31 @@
#include <linux/usb/msm_hsusb.h>
#include <linux/regulator/consumer.h>
#include <linux/power_supply.h>
+#include <linux/qpnp/qpnp-adc.h>
#include <mach/rpm-regulator.h>
#include <mach/rpm-regulator-smd.h>
#include <mach/msm_xo.h>
#include <mach/msm_bus.h>
+#include <mach/clk.h>
#include "dwc3_otg.h"
#include "core.h"
#include "gadget.h"
+/* ADC threshold values */
+static int adc_low_threshold = 700;
+module_param(adc_low_threshold, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(adc_low_threshold, "ADC ID Low voltage threshold");
+
+static int adc_high_threshold = 950;
+module_param(adc_high_threshold, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(adc_high_threshold, "ADC ID High voltage threshold");
+
+static int adc_meas_interval = ADC_MEAS1_INTERVAL_1S;
+module_param(adc_meas_interval, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(adc_meas_interval, "ADC ID polling period");
+
/**
* USB DBM Hardware registers.
*
@@ -160,6 +175,9 @@
struct usb_phy *otg_xceiv;
struct delayed_work chg_work;
enum usb_chg_state chg_state;
+ struct qpnp_adc_tm_usbid_param adc_param;
+ struct delayed_work init_adc_work;
+ bool id_adc_detect;
u8 dcd_retries;
u32 bus_perf_client;
struct msm_bus_scale_pdata *bus_scale_table;
@@ -1099,6 +1117,95 @@
return rc < 0 ? rc : 0;
}
+static int dwc3_msm_link_clk_reset(bool assert)
+{
+ int ret = 0;
+ struct dwc3_msm *mdwc = context;
+
+ if (assert) {
+ /* Using asynchronous block reset to the hardware */
+ dev_dbg(mdwc->dev, "block_reset ASSERT\n");
+ clk_disable_unprepare(mdwc->ref_clk);
+ clk_disable_unprepare(mdwc->iface_clk);
+ clk_disable_unprepare(mdwc->core_clk);
+ ret = clk_reset(mdwc->core_clk, CLK_RESET_ASSERT);
+ if (ret)
+ dev_err(mdwc->dev, "dwc3 core_clk assert failed\n");
+ } else {
+ dev_dbg(mdwc->dev, "block_reset DEASSERT\n");
+ ret = clk_reset(mdwc->core_clk, CLK_RESET_DEASSERT);
+ ndelay(200);
+ clk_prepare_enable(mdwc->core_clk);
+ clk_prepare_enable(mdwc->ref_clk);
+ clk_prepare_enable(mdwc->iface_clk);
+ if (ret)
+ dev_err(mdwc->dev, "dwc3 core_clk deassert failed\n");
+ }
+
+ return ret;
+}
+
+/* Initialize QSCRATCH registers for HSPHY and SSPHY operation */
+static void dwc3_msm_qscratch_reg_init(struct dwc3_msm *msm)
+{
+ u32 data = 0;
+
+ /* SSPHY Initialization: Use ref_clk from pads and set its parameters */
+ dwc3_msm_write_reg(msm->base, SS_PHY_CTRL_REG, 0x10210002);
+ msleep(30);
+ /* Assert SSPHY reset */
+ dwc3_msm_write_reg(msm->base, SS_PHY_CTRL_REG, 0x10210082);
+ usleep_range(2000, 2200);
+ /* De-assert SSPHY reset - power and ref_clock must be ON */
+ dwc3_msm_write_reg(msm->base, SS_PHY_CTRL_REG, 0x10210002);
+ usleep_range(2000, 2200);
+ /* Ref clock must be stable now, enable ref clock for HS mode */
+ dwc3_msm_write_reg(msm->base, SS_PHY_CTRL_REG, 0x10210102);
+ usleep_range(2000, 2200);
+ /*
+ * HSPHY Initialization: Enable UTMI clock and clamp enable HVINTs,
+ * and disable RETENTION (power-on default is ENABLED)
+ */
+ dwc3_msm_write_reg(msm->base, HS_PHY_CTRL_REG, 0x5220bb2);
+ usleep_range(2000, 2200);
+ /* Disable (bypass) VBUS and ID filters */
+ dwc3_msm_write_reg(msm->base, QSCRATCH_GENERAL_CFG, 0x78);
+
+ /*
+ * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates
+ * in HS mode instead of SS mode. Workaround it by asserting
+ * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode
+ */
+ data = dwc3_msm_ssusb_read_phycreg(msm->base, 0x102D);
+ data |= (1 << 7);
+ dwc3_msm_ssusb_write_phycreg(msm->base, 0x102D, data);
+
+ data = dwc3_msm_ssusb_read_phycreg(msm->base, 0x1010);
+ data &= ~0xFF0;
+ data |= 0x40;
+ dwc3_msm_ssusb_write_phycreg(msm->base, 0x1010, data);
+}
+
+static void dwc3_msm_block_reset(void)
+{
+ struct dwc3_msm *mdwc = context;
+ int ret = 0;
+
+ ret = dwc3_msm_link_clk_reset(1);
+ if (ret)
+ return;
+
+ usleep_range(1000, 1200);
+ ret = dwc3_msm_link_clk_reset(0);
+ if (ret)
+ return;
+
+ usleep_range(10000, 12000);
+
+ /* Reinitialize QSCRATCH registers after block reset */
+ dwc3_msm_qscratch_reg_init(mdwc);
+}
+
static void dwc3_chg_enable_secondary_det(struct dwc3_msm *mdwc)
{
u32 chg_ctrl;
@@ -1482,7 +1589,7 @@
}
}
-static u32 debug_id, debug_bsv, debug_connect;
+static u32 debug_id = true, debug_bsv, debug_connect;
static int dwc3_connect_show(struct seq_file *s, void *unused)
{
@@ -1628,7 +1735,6 @@
if (mdwc->otg_xceiv && (mdwc->ext_xceiv.otg_capability ||
!init)) {
mdwc->ext_xceiv.bsv = val->intval;
- mdwc->ext_xceiv.id = DWC3_ID_FLOAT;
if (atomic_read(&mdwc->in_lpm)) {
dev_dbg(mdwc->dev,
"%s received in LPM\n", __func__);
@@ -1669,6 +1775,88 @@
POWER_SUPPLY_PROP_SCOPE,
};
+static void dwc3_adc_notification(enum qpnp_tm_state state, void *ctx)
+{
+ struct dwc3_msm *mdwc = ctx;
+
+ if (state >= ADC_TM_STATE_NUM) {
+ pr_err("%s: invalid notification %d\n", __func__, state);
+ return;
+ }
+
+ dev_dbg(mdwc->dev, "%s: state = %s\n", __func__,
+ state == ADC_TM_HIGH_STATE ? "high" : "low");
+
+ if (state == ADC_TM_HIGH_STATE) {
+ mdwc->ext_xceiv.id = DWC3_ID_FLOAT;
+ mdwc->adc_param.state_request = ADC_TM_LOW_THR_ENABLE;
+ } else {
+ mdwc->ext_xceiv.id = DWC3_ID_GROUND;
+ mdwc->adc_param.state_request = ADC_TM_HIGH_THR_ENABLE;
+ }
+
+ /* notify OTG */
+ queue_delayed_work(system_nrt_wq, &mdwc->resume_work, 0);
+
+ /* re-arm notification interrupt */
+ qpnp_adc_tm_usbid_configure(&mdwc->adc_param);
+}
+
+static void dwc3_init_adc_work(struct work_struct *w)
+{
+ struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm,
+ init_adc_work.work);
+ int ret;
+
+ ret = qpnp_adc_tm_is_ready();
+ if (ret == -EPROBE_DEFER) {
+ queue_delayed_work(system_nrt_wq, to_delayed_work(w), 100);
+ return;
+ }
+
+ mdwc->adc_param.low_thr = adc_low_threshold;
+ mdwc->adc_param.high_thr = adc_high_threshold;
+ mdwc->adc_param.timer_interval = adc_meas_interval;
+ mdwc->adc_param.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
+ mdwc->adc_param.usbid_ctx = mdwc;
+ mdwc->adc_param.threshold_notification = dwc3_adc_notification;
+
+ ret = qpnp_adc_tm_usbid_configure(&mdwc->adc_param);
+ if (ret) {
+ dev_err(mdwc->dev, "%s: request ADC error %d\n", __func__, ret);
+ return;
+ }
+
+ mdwc->id_adc_detect = true;
+}
+
+static ssize_t adc_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", context->id_adc_detect ?
+ "enabled" : "disabled");
+}
+
+static ssize_t adc_enable_store(struct device *dev,
+ struct device_attribute *attr, const char
+ *buf, size_t size)
+{
+ if (!strnicmp(buf, "enable", 6)) {
+ if (!context->id_adc_detect)
+ dwc3_init_adc_work(&context->init_adc_work.work);
+ return size;
+ } else if (!strnicmp(buf, "disable", 7)) {
+ qpnp_adc_tm_usbid_end();
+ context->id_adc_detect = false;
+ return size;
+ }
+
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(adc_enable, S_IRUGO | S_IWUSR, adc_enable_show,
+ adc_enable_store);
+
static int __devinit dwc3_msm_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -1679,7 +1867,6 @@
int ret = 0;
int len = 0;
u32 tmp[3];
- u32 data = 0;
msm = devm_kzalloc(&pdev->dev, sizeof(*msm), GFP_KERNEL);
if (!msm) {
@@ -1694,6 +1881,7 @@
INIT_LIST_HEAD(&msm->req_complete_list);
INIT_DELAYED_WORK(&msm->chg_work, dwc3_chg_detect_work);
INIT_DELAYED_WORK(&msm->resume_work, dwc3_resume_work);
+ INIT_DELAYED_WORK(&msm->init_adc_work, dwc3_init_adc_work);
msm->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, "usb");
if (IS_ERR(msm->xo_handle)) {
@@ -1832,6 +2020,7 @@
goto free_hs_ldo_init;
}
+ msm->ext_xceiv.id = DWC3_ID_FLOAT;
msm->ext_xceiv.otg_capability = of_property_read_bool(node,
"qcom,otg-capability");
msm->charger.charging_disabled = of_property_read_bool(node,
@@ -1852,6 +2041,10 @@
}
enable_irq_wake(msm->hs_phy_irq);
}
+ } else {
+ /* Use ADC for ID pin detection */
+ queue_delayed_work(system_nrt_wq, &msm->init_adc_work, 0);
+ device_create_file(&pdev->dev, &dev_attr_adc_enable);
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
@@ -1902,40 +2095,7 @@
msm->resource_size = resource_size(res);
msm->dwc3 = dwc3;
- /* SSPHY Initialization: Use ref_clk from pads and set its parameters */
- dwc3_msm_write_reg(msm->base, SS_PHY_CTRL_REG, 0x10210002);
- msleep(30);
- /* Assert SSPHY reset */
- dwc3_msm_write_reg(msm->base, SS_PHY_CTRL_REG, 0x10210082);
- usleep_range(2000, 2200);
- /* De-assert SSPHY reset - power and ref_clock must be ON */
- dwc3_msm_write_reg(msm->base, SS_PHY_CTRL_REG, 0x10210002);
- usleep_range(2000, 2200);
- /* Ref clock must be stable now, enable ref clock for HS mode */
- dwc3_msm_write_reg(msm->base, SS_PHY_CTRL_REG, 0x10210102);
- usleep_range(2000, 2200);
- /*
- * HSPHY Initialization: Enable UTMI clock and clamp enable HVINTs,
- * and disable RETENTION (power-on default is ENABLED)
- */
- dwc3_msm_write_reg(msm->base, HS_PHY_CTRL_REG, 0x5220bb2);
- usleep_range(2000, 2200);
- /* Disable (bypass) VBUS and ID filters */
- dwc3_msm_write_reg(msm->base, QSCRATCH_GENERAL_CFG, 0x78);
-
- /*
- * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates
- * in HS mode instead of SS mode. Workaround it by asserting
- * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode
- */
- data = dwc3_msm_ssusb_read_phycreg(msm->base, 0x102D);
- data |= (1 << 7);
- dwc3_msm_ssusb_write_phycreg(msm->base, 0x102D, data);
-
- data = dwc3_msm_ssusb_read_phycreg(msm->base, 0x1010);
- data &= ~0xFF0;
- data |= 0x40;
- dwc3_msm_ssusb_write_phycreg(msm->base, 0x1010, data);
+ dwc3_msm_qscratch_reg_init(msm);
pm_runtime_set_active(msm->dev);
pm_runtime_enable(msm->dev);
@@ -2018,6 +2178,8 @@
goto put_xcvr;
}
+ if (msm->ext_xceiv.otg_capability)
+ msm->ext_xceiv.ext_block_reset = dwc3_msm_block_reset;
ret = dwc3_set_ext_xceiv(msm->otg_xceiv->otg, &msm->ext_xceiv);
if (ret || !msm->ext_xceiv.notify_ext_events) {
dev_err(&pdev->dev, "failed to register xceiver: %d\n",
@@ -2080,12 +2242,15 @@
{
struct dwc3_msm *msm = platform_get_drvdata(pdev);
+ if (msm->id_adc_detect)
+ qpnp_adc_tm_usbid_end();
if (dwc3_debugfs_root)
debugfs_remove_recursive(dwc3_debugfs_root);
if (msm->otg_xceiv) {
dwc3_start_chg_det(&msm->charger, false);
usb_put_transceiver(msm->otg_xceiv);
}
+
pm_runtime_disable(msm->dev);
platform_device_unregister(msm->dwc3);
wake_lock_destroy(&msm->wlock);
diff --git a/drivers/usb/dwc3/dwc3_otg.c b/drivers/usb/dwc3/dwc3_otg.c
index fab443c..136cc5d 100644
--- a/drivers/usb/dwc3/dwc3_otg.c
+++ b/drivers/usb/dwc3/dwc3_otg.c
@@ -1,7 +1,7 @@
/**
* dwc3_otg.c - DesignWare USB3 DRD Controller OTG
*
- * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -38,12 +38,21 @@
*/
static void dwc3_otg_set_host_regs(struct dwc3_otg *dotg)
{
- u32 octl;
+ u32 reg;
+ struct dwc3 *dwc = dotg->dwc;
+ struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv;
- /* Set OCTL[6](PeriMode) to 0 (host) */
- octl = dwc3_readl(dotg->regs, DWC3_OCTL);
- octl &= ~DWC3_OTG_OCTL_PERIMODE;
- dwc3_writel(dotg->regs, DWC3_OCTL, octl);
+ if (ext_xceiv && !ext_xceiv->otg_capability) {
+ /* Set OCTL[6](PeriMode) to 0 (host) */
+ reg = dwc3_readl(dotg->regs, DWC3_OCTL);
+ reg &= ~DWC3_OTG_OCTL_PERIMODE;
+ dwc3_writel(dotg->regs, DWC3_OCTL, reg);
+ } else {
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
+ reg |= DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_HOST);
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ }
}
/**
@@ -76,17 +85,25 @@
*/
static void dwc3_otg_set_peripheral_regs(struct dwc3_otg *dotg)
{
- u32 octl;
+ u32 reg;
+ struct dwc3 *dwc = dotg->dwc;
+ struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv;
- /* Set OCTL[6](PeriMode) to 1 (peripheral) */
- octl = dwc3_readl(dotg->regs, DWC3_OCTL);
- octl |= DWC3_OTG_OCTL_PERIMODE;
- dwc3_writel(dotg->regs, DWC3_OCTL, octl);
-
- /*
- * TODO: add more OTG registers writes for PERIPHERAL mode here,
- * see figure 12-19 B-device flow in dwc3 Synopsis spec
- */
+ if (ext_xceiv && !ext_xceiv->otg_capability) {
+ /* Set OCTL[6](PeriMode) to 1 (peripheral) */
+ reg = dwc3_readl(dotg->regs, DWC3_OCTL);
+ reg |= DWC3_OTG_OCTL_PERIMODE;
+ dwc3_writel(dotg->regs, DWC3_OCTL, reg);
+ /*
+ * TODO: add more OTG registers writes for PERIPHERAL mode here,
+ * see figure 12-19 B-device flow in dwc3 Synopsis spec
+ */
+ } else {
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
+ reg |= DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE);
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ }
}
/**
@@ -100,6 +117,7 @@
static int dwc3_otg_start_host(struct usb_otg *otg, int on)
{
struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+ struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv;
struct dwc3 *dwc = dotg->dwc;
int ret = 0;
@@ -148,7 +166,8 @@
}
/* re-init OTG EVTEN register as XHCI reset clears it */
- dwc3_otg_reset(dotg);
+ if (ext_xceiv && !ext_xceiv->otg_capability)
+ dwc3_otg_reset(dotg);
} else {
dev_dbg(otg->phy->dev, "%s: turn off host\n", __func__);
@@ -161,9 +180,15 @@
}
dwc3_otg_notify_host_mode(otg, on);
+ /* Do block reset for Host <-> peripheral switching to work */
+ if (ext_xceiv && ext_xceiv->otg_capability &&
+ ext_xceiv->ext_block_reset)
+ ext_xceiv->ext_block_reset();
+
/* re-init core and OTG register as XHCI reset clears it */
dwc3_post_host_reset_core_init(dwc);
- dwc3_otg_reset(dotg);
+ if (ext_xceiv && !ext_xceiv->otg_capability)
+ dwc3_otg_reset(dotg);
}
return 0;
@@ -312,6 +337,7 @@
struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv;
struct usb_phy *phy = dotg->otg.phy;
int ret = 0;
+ int work = 0;
if (event == DWC3_EVENT_PHY_RESUME) {
if (!pm_runtime_status_suspended(phy->dev)) {
@@ -331,17 +357,28 @@
}
}
} else if (event == DWC3_EVENT_XCEIV_STATE) {
- if (ext_xceiv->id == DWC3_ID_FLOAT)
- set_bit(ID, &dotg->inputs);
- else
- clear_bit(ID, &dotg->inputs);
+ if (ext_xceiv->id == DWC3_ID_FLOAT) {
+ if (!test_and_set_bit(ID, &dotg->inputs)) {
+ dev_dbg(phy->dev, "XCVR: ID set\n");
+ work = 1;
+ }
+ } else {
+ if (test_and_clear_bit(ID, &dotg->inputs)) {
+ dev_dbg(phy->dev, "XCVR: ID clear\n");
+ work = 1;
+ }
+ }
if (ext_xceiv->bsv) {
- dev_dbg(phy->dev, "XCVR: BSV set\n");
- set_bit(B_SESS_VLD, &dotg->inputs);
+ if (!test_and_set_bit(B_SESS_VLD, &dotg->inputs)) {
+ dev_dbg(phy->dev, "XCVR: BSV set\n");
+ work = 1;
+ }
} else {
- dev_dbg(phy->dev, "XCVR: BSV clear\n");
- clear_bit(B_SESS_VLD, &dotg->inputs);
+ if (test_and_clear_bit(B_SESS_VLD, &dotg->inputs)) {
+ dev_dbg(phy->dev, "XCVR: BSV clear\n");
+ work = 1;
+ }
}
if (!init) {
@@ -350,7 +387,8 @@
dev_dbg(phy->dev, "XCVR: BSV init complete\n");
return;
}
- schedule_work(&dotg->sm_work);
+ if (work)
+ schedule_work(&dotg->sm_work);
}
}
@@ -457,6 +495,7 @@
u32 osts, oevt_reg;
int ret = IRQ_NONE;
int handled_irqs = 0;
+ struct usb_phy *phy = dotg->otg.phy;
oevt_reg = dwc3_readl(dotg->regs, DWC3_OEVT);
@@ -472,23 +511,30 @@
* function, switch from A to B or from B to A.
*/
- if (osts & DWC3_OTG_OSTS_CONIDSTS)
- set_bit(ID, &dotg->inputs);
- else
- clear_bit(ID, &dotg->inputs);
+ if (oevt_reg & DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT) {
+ if (osts & DWC3_OTG_OSTS_CONIDSTS) {
+ dev_dbg(phy->dev, "ID set\n");
+ set_bit(ID, &dotg->inputs);
+ } else {
+ dev_dbg(phy->dev, "ID clear\n");
+ clear_bit(ID, &dotg->inputs);
+ }
+ handled_irqs |= DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT;
+ }
- if (osts & DWC3_OTG_OSTS_BSESVALID)
- set_bit(B_SESS_VLD, &dotg->inputs);
- else
- clear_bit(B_SESS_VLD, &dotg->inputs);
+ if (oevt_reg & DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT) {
+ if (osts & DWC3_OTG_OSTS_BSESVALID) {
+ dev_dbg(phy->dev, "BSV set\n");
+ set_bit(B_SESS_VLD, &dotg->inputs);
+ } else {
+ dev_dbg(phy->dev, "BSV clear\n");
+ clear_bit(B_SESS_VLD, &dotg->inputs);
+ }
+ handled_irqs |= DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT;
+ }
schedule_work(&dotg->sm_work);
- handled_irqs |= (oevt_reg & DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT) ?
- DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT : 0;
- handled_irqs |= (oevt_reg & DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT) ?
- DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT : 0;
-
ret = IRQ_HANDLED;
/* Clear the interrupts we handled */
diff --git a/drivers/usb/dwc3/dwc3_otg.h b/drivers/usb/dwc3/dwc3_otg.h
index c93ce5f..5a36a4f 100644
--- a/drivers/usb/dwc3/dwc3_otg.h
+++ b/drivers/usb/dwc3/dwc3_otg.h
@@ -103,6 +103,8 @@
/* to notify OTG about LPM exit event, provided by OTG */
void (*notify_ext_events)(struct usb_otg *otg,
enum dwc3_ext_events ext_event);
+ /* for block reset USB core */
+ void (*ext_block_reset)(void);
};
/* for external transceiver driver */
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 3679191..c2bc3f3 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1546,11 +1546,63 @@
{
struct dwc3_ep *dep;
int ret = 0;
+ u32 reg;
- /* reinitialize physical ep0-1 */
+ /* Enable all but Start and End of Frame IRQs */
+ reg = (DWC3_DEVTEN_EVNTOVERFLOWEN |
+ DWC3_DEVTEN_CMDCMPLTEN |
+ DWC3_DEVTEN_ERRTICERREN |
+ DWC3_DEVTEN_WKUPEVTEN |
+ DWC3_DEVTEN_ULSTCNGEN |
+ DWC3_DEVTEN_CONNECTDONEEN |
+ DWC3_DEVTEN_USBRSTEN |
+ DWC3_DEVTEN_DISCONNEVTEN);
+ dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
+
+ /* Enable USB2 LPM and automatic phy suspend only on recent versions */
+ if (dwc->revision >= DWC3_REVISION_194A) {
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg |= DWC3_DCFG_LPM_CAP;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
+
+ /* TODO: This should be configurable */
+ reg |= DWC3_DCTL_HIRD_THRES(28);
+
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ }
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~(DWC3_DCFG_SPEED_MASK);
+
+ /**
+ * WORKAROUND: DWC3 revision < 2.20a have an issue
+ * which would cause metastability state on Run/Stop
+ * bit if we try to force the IP to USB2-only mode.
+ *
+ * Because of that, we cannot configure the IP to any
+ * speed other than the SuperSpeed
+ *
+ * Refers to:
+ *
+ * STAR#9000525659: Clock Domain Crossing on DCTL in
+ * USB 2.0 Mode
+ */
+ if (dwc->revision < DWC3_REVISION_220A)
+ reg |= DWC3_DCFG_SUPERSPEED;
+ else
+ reg |= dwc->maximum_speed;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+ dwc->start_config_issued = false;
+
+ /* Start with SuperSpeed Default */
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
dwc->delayed_status = false;
-
+ /* reinitialize physical ep0-1 */
dep = dwc->eps[0];
dep->flags = 0;
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 372122c..4ad228d 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -1378,34 +1378,49 @@
#define MAX_VSYNC_GAP 4
#define DEFAULT_FRAME_RATE 60
-static u32 mdp_get_panel_framerate(struct msm_fb_data_type *mfd)
+u32 mdp_get_panel_framerate(struct msm_fb_data_type *mfd)
{
- u32 frame_rate = 0, total_pixel;
+ u32 frame_rate = 0, pixel_rate = 0, total_pixel;
struct msm_panel_info *panel_info = &mfd->panel_info;
+
+ pixel_rate =
+ (panel_info->type == MIPI_CMD_PANEL ||
+ panel_info->type == MIPI_VIDEO_PANEL) ?
+ panel_info->mipi.dsi_pclk_rate :
+ panel_info->clk_rate;
+
+ if (!pixel_rate)
+ pr_warn("%s pixel rate is zero\n", __func__);
+
+ total_pixel =
+ (panel_info->lcdc.h_back_porch +
+ panel_info->lcdc.h_front_porch +
+ panel_info->lcdc.h_pulse_width +
+ panel_info->xres) *
+ (panel_info->lcdc.v_back_porch +
+ panel_info->lcdc.v_front_porch +
+ panel_info->lcdc.v_pulse_width +
+ panel_info->yres);
+
+ if (total_pixel)
+ frame_rate = pixel_rate / total_pixel;
+ else
+ pr_warn("%s total pixels are zero\n", __func__);
+
if (mfd->dest == DISPLAY_LCD) {
if (panel_info->type == MDDI_PANEL && panel_info->mddi.is_type1)
frame_rate = panel_info->lcd.refx100 / (100 * 2);
- else
+ else if (panel_info->type != MIPI_CMD_PANEL)
frame_rate = panel_info->lcd.refx100 / 100;
- } else {
- if (panel_info->type == MIPI_VIDEO_PANEL) {
- frame_rate = panel_info->mipi.frame_rate;
- } else {
- total_pixel = (panel_info->lcdc.h_back_porch +
- panel_info->lcdc.h_front_porch +
- panel_info->lcdc.h_pulse_width +
- panel_info->xres) *
- (panel_info->lcdc.v_back_porch +
- panel_info->lcdc.v_front_porch +
- panel_info->lcdc.v_pulse_width +
- panel_info->yres);
- if (total_pixel)
- frame_rate = panel_info->clk_rate /
- total_pixel;
- }
}
- if (frame_rate == 0)
+
+ if (frame_rate == 0) {
frame_rate = DEFAULT_FRAME_RATE;
+ pr_warn("%s frame rate=%d is default\n", __func__, frame_rate);
+ }
+ pr_debug("%s frame rate=%d total_pixel=%d, pixel_rate=%d\n", __func__,
+ frame_rate, total_pixel, pixel_rate);
+
return frame_rate;
}
diff --git a/drivers/video/msm/mdp.h b/drivers/video/msm/mdp.h
index 0bc2532..d7f707f 100644
--- a/drivers/video/msm/mdp.h
+++ b/drivers/video/msm/mdp.h
@@ -932,6 +932,8 @@
unsigned long srcp1_addr, unsigned long srcp1_size);
void mdp_update_pm(struct msm_fb_data_type *mfd, ktime_t pre_vsync);
+u32 mdp_get_panel_framerate(struct msm_fb_data_type *mfd);
+
#ifdef CONFIG_FB_MSM_DTV
void mdp_vid_quant_set(void);
#else
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index 993ec01..b7e0bbf 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -1286,27 +1286,8 @@
var->yres_virtual = panel_info->yres * mfd->fb_page +
((PAGE_SIZE - remainder)/fix->line_length) * mfd->fb_page;
var->bits_per_pixel = bpp * 8; /* FrameBuffer color depth */
- if (mfd->dest == DISPLAY_LCD) {
- if (panel_info->type == MDDI_PANEL && panel_info->mddi.is_type1)
- var->reserved[4] = panel_info->lcd.refx100 / (100 * 2);
- else
- var->reserved[4] = panel_info->lcd.refx100 / 100;
- } else {
- if (panel_info->type == MIPI_VIDEO_PANEL) {
- var->reserved[4] = panel_info->mipi.frame_rate;
- } else {
- var->reserved[4] = panel_info->clk_rate /
- ((panel_info->lcdc.h_back_porch +
- panel_info->lcdc.h_front_porch +
- panel_info->lcdc.h_pulse_width +
- panel_info->xres) *
- (panel_info->lcdc.v_back_porch +
- panel_info->lcdc.v_front_porch +
- panel_info->lcdc.v_pulse_width +
- panel_info->yres));
- }
- }
- pr_debug("reserved[4] %u\n", var->reserved[4]);
+
+ var->reserved[4] = mdp_get_panel_framerate(mfd);
/*
* id field for fb app
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index fc34b22..7ba91f1 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -660,13 +660,16 @@
* thresholds.
* @timer_interval: Select polling rate from qpnp_adc_meas_timer_1 type.
* @threshold_notification: Notification callback once threshold are crossed.
+ * @usbid_ctx: A context of void type.
*/
struct qpnp_adc_tm_usbid_param {
int32_t high_thr;
int32_t low_thr;
enum qpnp_state_request state_request;
enum qpnp_adc_meas_timer_1 timer_interval;
- void (*threshold_notification) (enum qpnp_tm_state state);
+ void *usbid_ctx;
+ void (*threshold_notification) (enum qpnp_tm_state state,
+ void *ctx);
};
/**
@@ -743,16 +746,32 @@
* input channel.
* @offset_gain_denominator: The inverse denominator of the gain applied to the
* input channel.
+ * @high_thr: High threshold voltage that is requested to be set.
+ * @low_thr: Low threshold voltage that is requested to be set.
+ * @timer_select: Choosen from one of the 3 timers to set the polling rate for
+ * the VADC_BTM channel.
+ * @meas_interval1: Polling rate to set for timer 1.
+ * @meas_interval2: Polling rate to set for timer 2.
+ * @tm_channel_select: BTM channel number for the 5 VADC_BTM channels.
+ * @state_request: User can select either enable or disable high/low or both
+ * activation levels based on the qpnp_state_request type.
* @adc_graph: ADC graph for the channel of struct type qpnp_adc_linear_graph.
*/
struct qpnp_vadc_chan_properties {
uint32_t offset_gain_numerator;
uint32_t offset_gain_denominator;
+ uint32_t high_thr;
+ uint32_t low_thr;
+ enum qpnp_adc_meas_timer_select timer_select;
+ enum qpnp_adc_meas_timer_1 meas_interval1;
+ enum qpnp_adc_meas_timer_2 meas_interval2;
+ enum qpnp_adc_tm_channel_select tm_channel_select;
+ enum qpnp_state_request state_request;
struct qpnp_vadc_linear_graph adc_graph[2];
};
/**
- * struct qpnp_adc_result - Represent the result of the QPNP ADC.
+ * struct qpnp_vadc_result - Represent the result of the QPNP ADC.
* @chan: The channel number of the requested conversion.
* @adc_code: The pre-calibrated digital output of a given ADC relative to the
* the ADC reference.
@@ -774,7 +793,7 @@
};
/**
- * struct qpnp_vadc_amux - AMUX properties for individual channel
+ * struct qpnp_adc_amux - AMUX properties for individual channel
* @name: Channel string name.
* @channel_num: Channel in integer used from qpnp_adc_channels.
* @chan_path_prescaling: Channel scaling performed on the input signal.
@@ -783,7 +802,7 @@
* each individual channel whether it is voltage, current,
* temperature, etc and compensates the channel properties.
*/
-struct qpnp_vadc_amux {
+struct qpnp_adc_amux {
char *name;
enum qpnp_vadc_channels channel_num;
enum qpnp_adc_channel_scaling_param chan_path_prescaling;
@@ -858,6 +877,11 @@
* @amux_prop - AMUX properties representing the ADC peripheral.
* @adc_channels - ADC channel properties for the ADC peripheral.
* @adc_irq_eoc - End of Conversion IRQ.
+ * @adc_irq_fifo_not_empty - Conversion sequencer request written
+ * to FIFO when not empty.
+ * @adc_irq_conv_seq_timeout - Conversion sequencer trigger timeout.
+ * @adc_high_thr_irq - Output higher than high threshold set for measurement.
+ * @adc_low_thr_irq - Output lower than low threshold set for measurement.
* @adc_lock - ADC lock for access to the peripheral.
* @adc_rslt_completion - ADC result notification after interrupt
* is received.
@@ -869,8 +893,12 @@
uint16_t offset;
struct qpnp_adc_properties *adc_prop;
struct qpnp_adc_amux_properties *amux_prop;
- struct qpnp_vadc_amux *adc_channels;
+ struct qpnp_adc_amux *adc_channels;
int adc_irq_eoc;
+ int adc_irq_fifo_not_empty;
+ int adc_irq_conv_seq_timeout;
+ int adc_high_thr_irq;
+ int adc_low_thr_irq;
struct mutex adc_lock;
struct completion adc_rslt_completion;
struct qpnp_iadc_calib calib;
@@ -1234,4 +1262,65 @@
{ return -ENXIO; }
#endif
+/* Public API */
+#if defined(CONFIG_THERMAL_QPNP_ADC_TM) \
+ || defined(CONFIG_THERMAL_QPNP_ADC_TM_MODULE)
+/**
+ * qpnp_adc_tm_usbid_configure() - Configures Channel 0 of VADC_BTM to
+ * monitor USB_ID channel using 100k internal pull-up.
+ * USB driver passes the high/low voltage threshold along
+ * with the notification callback once the set thresholds
+ * are crossed.
+ * @param: Structure pointer of qpnp_adc_tm_usbid_param type.
+ * Clients pass the low/high voltage along with the threshold
+ * notification callback.
+ */
+int32_t qpnp_adc_tm_usbid_configure(struct qpnp_adc_tm_usbid_param *param);
+/**
+ * qpnp_adc_tm_usbid_end() - Disables the monitoring of channel 0 thats
+ * assigned for monitoring USB_ID. Disables the low/high
+ * threshold activation for channel 0 as well.
+ * @param: none.
+ */
+int32_t qpnp_adc_tm_usbid_end(void);
+/**
+ * qpnp_adc_tm_usbid_configure() - Configures Channel 1 of VADC_BTM to
+ * monitor batt_therm channel using 100k internal pull-up.
+ * Battery driver passes the high/low voltage threshold along
+ * with the notification callback once the set thresholds
+ * are crossed.
+ * @param: Structure pointer of qpnp_adc_tm_btm_param type.
+ * Clients pass the low/high temperature along with the threshold
+ * notification callback.
+ */
+int32_t qpnp_adc_tm_btm_configure(struct qpnp_adc_tm_btm_param *param);
+/**
+ * qpnp_adc_tm_btm_end() - Disables the monitoring of channel 1 thats
+ * assigned for monitoring batt_therm. Disables the low/high
+ * threshold activation for channel 1 as well.
+ * @param: none.
+ */
+int32_t qpnp_adc_tm_btm_end(void);
+/**
+ * qpnp_adc_tm_is_ready() - Clients can use this API to check if the
+ * device is ready to use.
+ * @result: 0 on success and -EPROBE_DEFER when probe for the device
+ * has not occured.
+ */
+int32_t qpnp_adc_tm_is_ready(void);
+#else
+static inline int32_t qpnp_adc_tm_usbid_configure(
+ struct qpnp_adc_tm_usbid_param *param)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_tm_usbid_end(void)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_tm_btm_configure(
+ struct qpnp_adc_tm_btm_param *param)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_tm_btm_end(void)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_tm_is_ready(void)
+{ return -ENXIO; }
+#endif
+
#endif
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index 4fe002b..a351f7b 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -92,7 +92,7 @@
static int msm_btsco_rate = BTSCO_RATE_8KHZ;
static int msm_btsco_ch = 1;
-
+static int hdmi_rate_variable;
static int rec_mode = INCALL_REC_MONO;
static struct clk *codec_clk;
@@ -642,11 +642,13 @@
static const char *spk_function[] = {"Off", "On"};
static const char *slim0_rx_ch_text[] = {"One", "Two"};
static const char *slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
+static const char * const hdmi_rate[] = {"Default", "Variable"};
static const struct soc_enum msm_enum[] = {
SOC_ENUM_SINGLE_EXT(2, spk_function),
SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
+ SOC_ENUM_SINGLE_EXT(2, hdmi_rate),
};
static const char *btsco_rate_text[] = {"8000", "16000"};
@@ -756,6 +758,21 @@
return 0;
}
+static int msm_hdmi_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ hdmi_rate_variable = ucontrol->value.integer.value[0];
+ pr_debug("%s: hdmi_rate_variable = %d\n", __func__, hdmi_rate_variable);
+ return 0;
+}
+
+static int msm_hdmi_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = hdmi_rate_variable;
+ return 0;
+}
+
static const struct snd_kcontrol_new tabla_msm_controls[] = {
SOC_ENUM_EXT("Speaker Function", msm_enum[0], msm_get_spk,
msm_set_spk),
@@ -769,6 +786,9 @@
msm_incall_rec_mode_get, msm_incall_rec_mode_put),
SOC_ENUM_EXT("SLIM_3_RX Channels", msm_enum[1],
msm_slim_3_rx_ch_get, msm_slim_3_rx_ch_put),
+ SOC_ENUM_EXT("HDMI RX Rate", msm_enum[3],
+ msm_hdmi_rate_get,
+ msm_hdmi_rate_put),
};
static void *def_tabla_mbhc_cal(void)
@@ -1328,7 +1348,8 @@
if (channels->max < 2)
channels->min = channels->max = 2;
- rate->min = rate->max = 48000;
+ if (!hdmi_rate_variable)
+ rate->min = rate->max = 48000;
return 0;
}
diff --git a/sound/soc/msm/msm8930.c b/sound/soc/msm/msm8930.c
index 42699c9..bb9f2be 100644
--- a/sound/soc/msm/msm8930.c
+++ b/sound/soc/msm/msm8930.c
@@ -58,7 +58,7 @@
static int msm8930_ext_spk_pamp;
static int msm8930_btsco_rate = BTSCO_RATE_8KHZ;
static int msm8930_btsco_ch = 1;
-
+static int hdmi_rate_variable;
static struct clk *codec_clk;
static int clk_users;
@@ -395,10 +395,13 @@
static const char *slim0_rx_ch_text[] = {"One", "Two"};
static const char *slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
+static const char * const hdmi_rate[] = {"Default", "Variable"};
+
static const struct soc_enum msm8930_enum[] = {
SOC_ENUM_SINGLE_EXT(2, spk_function),
SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
+ SOC_ENUM_SINGLE_EXT(2, hdmi_rate),
};
static const char *btsco_rate_text[] = {"8000", "16000"};
@@ -505,6 +508,21 @@
return ret;
}
+static int msm8930_hdmi_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ hdmi_rate_variable = ucontrol->value.integer.value[0];
+ pr_debug("%s: hdmi_rate_variable = %d\n", __func__, hdmi_rate_variable);
+ return 0;
+}
+
+static int msm8930_hdmi_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = hdmi_rate_variable;
+ return 0;
+}
+
static const struct snd_kcontrol_new sitar_msm8930_controls[] = {
SOC_ENUM_EXT("Speaker Function", msm8930_enum[0], msm8930_get_spk,
msm8930_set_spk),
@@ -516,6 +534,9 @@
msm8930_pmic_gain_get, msm8930_pmic_gain_put),
SOC_ENUM_EXT("Internal BTSCO SampleRate", msm8930_btsco_enum[0],
msm8930_btsco_rate_get, msm8930_btsco_rate_put),
+ SOC_ENUM_EXT("HDMI RX Rate", msm8930_enum[3],
+ msm8930_hdmi_rate_get,
+ msm8930_hdmi_rate_put),
};
static void *def_sitar_mbhc_cal(void)
@@ -751,7 +772,8 @@
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
- rate->min = rate->max = 48000;
+ if (!hdmi_rate_variable)
+ rate->min = rate->max = 48000;
channels->min = channels->max = 2;
return 0;
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index ad78255..da62729 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -73,7 +73,7 @@
static int msm8960_btsco_rate = SAMPLE_RATE_8KHZ;
static int msm8960_btsco_ch = 1;
-
+static int hdmi_rate_variable;
static int msm8960_auxpcm_rate = SAMPLE_RATE_8KHZ;
static struct clk *codec_clk;
@@ -549,11 +549,13 @@
static const char *spk_function[] = {"Off", "On"};
static const char *slim0_rx_ch_text[] = {"One", "Two"};
static const char *slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
+static const char * const hdmi_rate[] = {"Default", "Variable"};
static const struct soc_enum msm8960_enum[] = {
SOC_ENUM_SINGLE_EXT(2, spk_function),
SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
+ SOC_ENUM_SINGLE_EXT(2, hdmi_rate),
};
static const char *btsco_rate_text[] = {"8000", "16000"};
@@ -660,6 +662,21 @@
return 0;
}
+static int msm8960_hdmi_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ hdmi_rate_variable = ucontrol->value.integer.value[0];
+ pr_debug("%s: hdmi_rate_variable = %d\n", __func__, hdmi_rate_variable);
+ return 0;
+}
+
+static int msm8960_hdmi_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = hdmi_rate_variable;
+ return 0;
+}
+
static const struct snd_kcontrol_new tabla_msm8960_controls[] = {
SOC_ENUM_EXT("Speaker Function", msm8960_enum[0], msm8960_get_spk,
msm8960_set_spk),
@@ -671,6 +688,9 @@
msm8960_btsco_rate_get, msm8960_btsco_rate_put),
SOC_ENUM_EXT("AUX PCM SampleRate", msm8960_auxpcm_enum[0],
msm8960_auxpcm_rate_get, msm8960_auxpcm_rate_put),
+ SOC_ENUM_EXT("HDMI RX Rate", msm8960_enum[3],
+ msm8960_hdmi_rate_get,
+ msm8960_hdmi_rate_put),
};
static void *def_tabla_mbhc_cal(void)
@@ -1003,7 +1023,8 @@
if (channels->max < 2)
channels->min = channels->max = 2;
- rate->min = rate->max = 48000;
+ if (!hdmi_rate_variable)
+ rate->min = rate->max = 48000;
return 0;
}