Merge "media: dvb: Secure demux API updates"
diff --git a/Documentation/devicetree/bindings/platform/msm/ssm.txt b/Documentation/devicetree/bindings/platform/msm/ssm.txt
new file mode 100644
index 0000000..8fb3356
--- /dev/null
+++ b/Documentation/devicetree/bindings/platform/msm/ssm.txt
@@ -0,0 +1,30 @@
+* Qualcomm Secure Service Module (SSM)
+
+SSM provides an interface for OEM driver to communicate with Modem and
+trustzone.
+
+This module provides following features:
+ - Keyexchange between Modem and trustzone for encryption/Decryption
+ of mode information
+ - Interface to third party driver to send mode updates to modem
+ - Interface for loading the trustzone application
+
+Required properties:
+- compatible: Must be "qcom,ssm"
+
+Optional properties:
+- qcom,channel-name: Name of the SMD channel used for communication
+ between MODEM and SSM driver.
+- qcom,need-keyexhg This property controls initial key exchange
+ between APPS(application processor) and MODEM.
+ If not mentioned the initial key exchange is
+ not required.
+ If this property is mentioned then it is mandatory
+ for modem to perform initial key exchange with APPS.
+
+Example:
+ qcom,ssm {
+ compatible = "qcom,ssm";
+ qcom,channel-name = "SSM_RTR";
+ qcom,need-keyexhg;
+ }
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index c1d8664..54f603d 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -632,7 +632,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <1>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
@@ -643,7 +643,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <0>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
@@ -654,7 +654,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <4>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
@@ -665,7 +665,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <2>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
@@ -676,7 +676,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <2>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
@@ -687,7 +687,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <2>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
@@ -698,7 +698,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <2>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
@@ -709,7 +709,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <2>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
@@ -720,7 +720,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <0>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
};
@@ -770,7 +770,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <2>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
qcom,btm-channel-number = <0x48>;
};
@@ -782,7 +782,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <1>;
- qcom,hw-settle-time = <0xf>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
qcom,btm-channel-number = <0x68>;
};
@@ -794,7 +794,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "absolute";
qcom,scale-function = <2>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
qcom,btm-channel-number = <0x70>;
};
@@ -806,7 +806,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <2>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
qcom,btm-channel-number = <0x78>;
};
@@ -818,7 +818,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <2>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
qcom,btm-channel-number = <0x80>;
};
diff --git a/arch/arm/boot/dts/msm8226-regulator.dtsi b/arch/arm/boot/dts/msm8226-regulator.dtsi
index 50d2dba..3c0dd1e 100644
--- a/arch/arm/boot/dts/msm8226-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8226-regulator.dtsi
@@ -13,7 +13,7 @@
/* Stub Regulators */
/ {
- pm8026_s1_corner: regulator-s1-corner {
+ pm8226_s1_corner: regulator-s1-corner {
compatible = "qcom,stub-regulator";
regulator-name = "8226_s1_corner";
qcom,hpm-min-load = <100000>;
@@ -35,8 +35,8 @@
qcom,enable-time = <500>;
qcom,system-load = <100000>;
regulator-always-on;
- regulator-min-microvolt = <1287500>;
- regulator-max-microvolt = <1287500>;
+ regulator-min-microvolt = <1150000>;
+ regulator-max-microvolt = <1150000>;
};
pm8226_s2: regulator@1700 {
@@ -104,8 +104,8 @@
qcom,system-load = <10000>;
regulator-always-on;
qcom,enable-time = <200>;
- regulator-min-microvolt = <1287500>;
- regulator-max-microvolt = <1287500>;
+ regulator-min-microvolt = <1150000>;
+ regulator-max-microvolt = <1150000>;
};
pm8226_l4: regulator@4300 {
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index 1147b03..3533d19 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -524,7 +524,7 @@
reg = <0xfe200000 0x00100>,
<0xfd485100 0x00010>;
reg-names = "qdsp6_base", "halt_base";
- vdd_cx-supply = <&pm8026_s1_corner>;
+ vdd_cx-supply = <&pm8226_s1_corner>;
interrupts = <0 162 1>;
qcom,firmware-name = "adsp";
@@ -541,8 +541,8 @@
<0xfc4b8000 0x1000>;
reg-names = "tsens_physical", "tsens_eeprom_physical";
interrupts = <0 184 0>;
- qcom,sensors = <7>;
- qcom,slope = <3200 3200 3200 3200 3200 3200 3200>;
+ qcom,sensors = <6>;
+ qcom,slope = <3200 3200 3200 3200 3200 3200>;
qcom,calib-mode = "fuse_map2";
};
@@ -670,7 +670,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <1>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
@@ -692,7 +692,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <4>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
};
diff --git a/arch/arm/boot/dts/msm8974-v1-pm.dtsi b/arch/arm/boot/dts/msm8974-pm.dtsi
similarity index 100%
rename from arch/arm/boot/dts/msm8974-v1-pm.dtsi
rename to arch/arm/boot/dts/msm8974-pm.dtsi
diff --git a/arch/arm/boot/dts/msm8974-v1.dtsi b/arch/arm/boot/dts/msm8974-v1.dtsi
index f4f387f..a7b5919 100644
--- a/arch/arm/boot/dts/msm8974-v1.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1.dtsi
@@ -18,7 +18,6 @@
/include/ "msm8974.dtsi"
/include/ "msm8974-v1-iommu.dtsi"
-/include/ "msm8974-v1-pm.dtsi"
/ {
android_usb@fc42b0c8 {
diff --git a/arch/arm/boot/dts/msm8974-v2-pm.dtsi b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
deleted file mode 100644
index 0ed55ff..0000000
--- a/arch/arm/boot/dts/msm8974-v2-pm.dtsi
+++ /dev/null
@@ -1,426 +0,0 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-/include/ "skeleton.dtsi"
-
-/ {
- qcom,spm@f9089000 {
- compatible = "qcom,spm-v2";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = <0xf9089000 0x1000>;
- qcom,core-id = <0>;
- qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
- qcom,saw2-avs-ctl = <0>;
- qcom,saw2-avs-hysteresis = <0>;
- qcom,saw2-avs-limit = <0>;
- qcom,saw2-avs-dly= <0>;
- qcom,saw2-spm-dly= <0x20000400>;
- qcom,saw2-spm-ctl = <0x1>;
- qcom,saw2-spm-cmd-wfi = [03 0b 0f];
- qcom,saw2-spm-cmd-ret = [42 1b 00 d0 03 d4 5b 0b 00 42 1b 0f];
- qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 E0 03 6E 70 3B
- E4 5B 82 2B 50 10 0B 30 06 26 30 0F];
- qcom,saw2-spm-cmd-pc = [00 20 50 80 60 70 10 E0 07 6E 70 3B
- E4 5B 82 2B 50 10 0B 30 06 26 30 0F];
- };
-
- qcom,spm@f9099000 {
- compatible = "qcom,spm-v2";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = <0xf9099000 0x1000>;
- qcom,core-id = <1>;
- qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
- qcom,saw2-avs-ctl = <0>;
- qcom,saw2-avs-hysteresis = <0>;
- qcom,saw2-avs-limit = <0>;
- qcom,saw2-avs-dly= <0>;
- qcom,saw2-spm-dly= <0x20000400>;
- qcom,saw2-spm-ctl = <0x1>;
- qcom,saw2-spm-cmd-wfi = [03 0b 0f];
- qcom,saw2-spm-cmd-ret = [42 1b 00 d0 03 d4 5b 0b 00 42 1b 0f];
- qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 E0 03 6E 70 3B
- E4 5B 82 2B 50 10 0B 30 06 26 30 0F];
- qcom,saw2-spm-cmd-pc = [00 20 50 80 60 70 10 E0 07 6E 70 3B
- E4 5B 82 2B 50 10 0B 30 06 26 30 0F];
- };
-
- qcom,spm@f90a9000 {
- compatible = "qcom,spm-v2";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = <0xf90a9000 0x1000>;
- qcom,core-id = <2>;
- qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
- qcom,saw2-avs-ctl = <0>;
- qcom,saw2-avs-hysteresis = <0>;
- qcom,saw2-avs-limit = <0>;
- qcom,saw2-avs-dly= <0>;
- qcom,saw2-spm-dly= <0x20000400>;
- qcom,saw2-spm-ctl = <0x1>;
- qcom,saw2-spm-cmd-wfi = [03 0b 0f];
- qcom,saw2-spm-cmd-ret = [42 1b 00 d0 03 d4 5b 0b 00 42 1b 0f];
- qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 E0 03 6E 70 3B
- E4 5B 82 2B 50 10 0B 30 06 26 30 0F];
- qcom,saw2-spm-cmd-pc = [00 20 50 80 60 70 10 E0 07 6E 70 3B
- E4 5B 82 2B 50 10 0B 30 06 26 30 0F];
- };
-
- qcom,spm@f90b9000 {
- compatible = "qcom,spm-v2";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = <0xf90b9000 0x1000>;
- qcom,core-id = <3>;
- qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
- qcom,saw2-avs-ctl = <0>;
- qcom,saw2-avs-hysteresis = <0>;
- qcom,saw2-avs-limit = <0>;
- qcom,saw2-avs-dly= <0>;
- qcom,saw2-spm-dly= <0x20000400>;
- qcom,saw2-spm-ctl = <0x1>;
- qcom,saw2-spm-cmd-wfi = [03 0b 0f];
- qcom,saw2-spm-cmd-ret = [42 1b 00 d0 03 d4 5b 0b 00 42 1b 0f];
- qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 E0 03 6E 70 3B
- E4 5B 82 2B 50 10 0B 30 06 26 30 0F];
- qcom,saw2-spm-cmd-pc = [00 20 50 80 60 70 10 E0 07 6E 70 3B
- E4 5B 82 2B 50 10 0B 30 06 26 30 0F];
- };
-
- qcom,spm@f9012000 {
- compatible = "qcom,spm-v2";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = <0xf9012000 0x1000>;
- qcom,core-id = <0xffff>; /* L2/APCS SAW */
- qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x14>;
- qcom,saw2-avs-ctl = <0>;
- qcom,saw2-avs-hysteresis = <0>;
- qcom,saw2-avs-limit = <0>;
- qcom,saw2-avs-dly= <0>;
- qcom,saw2-spm-dly= <0x20000400>;
- qcom,saw2-spm-ctl = <0x1>;
- qcom,saw2-pmic-data0 = <0x02030080>;
- qcom,saw2-pmic-data1 = <0x00030000>;
- qcom,vctl-timeout-us = <50>;
- qcom,vctl-port = <0x0>;
- qcom,phase-port = <0x1>;
- qcom,pfm-port = <0x2>;
- qcom,saw2-spm-cmd-ret = [1f 00 20 03 22 00 0f];
- qcom,saw2-spm-cmd-gdhs = [00 20 32 60 70 80 42 07 78 80 44 22 50
- 3b 60 02 32 50 0f];
- qcom,saw2-spm-cmd-pc = [00 10 32 60 70 80 b0 11 42 07 01 b0 78
- 80 12 44 50 3b 60 02 32 50 0f];
- };
-
- qcom,lpm-resources {
- compatible = "qcom,lpm-resources";
- #address-cells = <1>;
- #size-cells = <0>;
-
- qcom,lpm-resources@0 {
- reg = <0x0>;
- qcom,name = "vdd-dig";
- qcom,resource-type = <0>;
- qcom,type = <0x62706d73>; /* "smpb" */
- qcom,id = <0x02>;
- qcom,key = <0x6e726f63>; /* "corn" */
- qcom,init-value = <5>; /* Super Turbo */
- };
-
- qcom,lpm-resources@1 {
- reg = <0x1>;
- qcom,name = "vdd-mem";
- qcom,resource-type = <0>;
- qcom,type = <0x62706d73>; /* "smpb" */
- qcom,id = <0x01>;
- qcom,key = <0x7675>; /* "uv" */
- qcom,init-value = <1050000>; /* Super Turbo */
- };
-
- qcom,lpm-resources@2 {
- reg = <0x2>;
- qcom,name = "pxo";
- qcom,resource-type = <0>;
- qcom,type = <0x306b6c63>; /* "clk0" */
- qcom,id = <0x00>;
- qcom,key = <0x62616e45>; /* "Enab" */
- qcom,init-value = <1>; /* On */
- };
-
- qcom,lpm-resources@3 {
- reg = <0x3>;
- qcom,name = "l2";
- qcom,resource-type = <1>;
- qcom,init-value = <2>; /* Retention */
- };
- };
-
- qcom,lpm-levels {
- compatible = "qcom,lpm-levels";
- #address-cells = <1>;
- #size-cells = <0>;
-
- qcom,lpm-level@0 {
- reg = <0x0>;
- qcom,mode = <0>; /* MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT */
- qcom,xo = <1>; /* ON */
- qcom,l2 = <2>; /* Retention */
- qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
- qcom,irqs-detectable;
- qcom.gpios-detectable;
- qcom,latency-us = <1>;
- qcom,ss-power = <784>;
- qcom,energy-overhead = <190000>;
- qcom,time-overhead = <100>;
- };
-
- qcom,lpm-level@1 {
- reg = <0x1>;
- qcom,mode = <4>; /* MSM_PM_SLEEP_MODE_RETENTION*/
- qcom,xo = <1>; /* ON */
- qcom,l2 = <2>; /* Retention */
- qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
- qcom,irqs-detectable;
- qcom.gpios-detectable;
- qcom,latency-us = <75>;
- qcom,ss-power = <735>;
- qcom,energy-overhead = <77341>;
- qcom,time-overhead = <105>;
- };
-
- qcom,lpm-level@2 {
- reg = <0x2>;
- qcom,mode = <2>; /* MSM_PM_SLEEP_MODE_STANDALONE_POWER_COLLAPSE */
- qcom,xo = <1>; /* ON */
- qcom,l2 = <2>; /* Retention */
- qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
- qcom,irqs-detectable;
- qcom.gpios-detectable;
- qcom,latency-us = <95>;
- qcom,ss-power = <725>;
- qcom,energy-overhead = <99500>;
- qcom,time-overhead = <130>;
- };
-
- qcom,lpm-level@3 {
- reg = <0x3>;
- qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
- qcom,xo = <1>; /* ON */
- qcom,l2 = <1>; /* GDHS */
- qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
- qcom,irqs-detectable;
- qcom.gpios-detectable;
- qcom,latency-us = <2000>;
- qcom,ss-power = <138>;
- qcom,energy-overhead = <1208400>;
- qcom,time-overhead = <3200>;
- };
-
- qcom,lpm-level@4 {
- reg = <0x4>;
- qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
- qcom,xo = <1>; /* ON */
- qcom,l2 = <1>; /* GDHS */
- qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <950000>; /* SVS SOC */
- qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
- qcom,irqs-detectable;
- qcom.gpios-detectable;
- qcom,latency-us = <3000>;
- qcom,ss-power = <110>;
- qcom,energy-overhead = <1250300>;
- qcom,time-overhead = <3500>;
- };
-
- qcom,lpm-level@5 {
- reg = <0x5>;
- qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
- qcom,xo = <0>; /* OFF */
- qcom,l2 = <1>; /* GDHS */
- qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
- qcom,latency-us = <3000>;
- qcom,ss-power = <68>;
- qcom,energy-overhead = <1350200>;
- qcom,time-overhead = <4000>;
- };
-
- qcom,lpm-level@6 {
- reg = <0x6>;
- qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
- qcom,xo = <0>; /* OFF */
- qcom,l2 = <1>; /* GDHS */
- qcom,vdd-mem-upper-bound = <950000>; /* NORMAL */
- qcom,vdd-mem-lower-bound = <950000>; /* SVS SOC */
- qcom,vdd-dig-upper-bound = <4>; /* NORMAL */
- qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
- qcom,latency-us = <18000>;
- qcom,ss-power = <10>;
- qcom,energy-overhead = <3202600>;
- qcom,time-overhead = <27000>;
- };
-
- qcom,lpm-level@7 {
- reg = <0x7>;
- qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
- qcom,xo = <0>; /* OFF */
- qcom,l2 = <0>; /* OFF */
- qcom,vdd-mem-upper-bound = <950000>; /* SVS SOC */
- qcom,vdd-mem-lower-bound = <675000>; /* RETENTION */
- qcom,vdd-dig-upper-bound = <3>; /* SVS SOC */
- qcom,vdd-dig-lower-bound = <1>; /* RETENTION */
- qcom,latency-us = <20000>;
- qcom,ss-power = <2>;
- qcom,energy-overhead = <4252000>;
- qcom,time-overhead = <32000>;
- };
- };
-
- qcom,pm-boot {
- compatible = "qcom,pm-boot";
- qcom,mode = <0>; /* MSM_PM_BOOT_CONFIG_TZ */
- };
-
- qcom,mpm@fc4281d0 {
- compatible = "qcom,mpm-v2";
- reg = <0xfc4281d0 0x1000>, /* MSM_RPM_MPM_BASE 4K */
- <0xf9011008 0x4>; /* MSM_APCS_GCC_BASE 4K */
- reg-names = "vmpm", "ipc";
- interrupts = <0 171 1>;
-
- qcom,ipc-bit-offset = <1>;
-
- qcom,gic-parent = <&intc>;
- qcom,gic-map = <47 172>, /* usb2_hsic_async_wakeup_irq */
- <53 104>, /* mdss_irq */
- <62 222>, /* ee0_krait_hlos_spmi_periph_irq */
- <0xff 57>, /* mss_to_apps_irq(0) */
- <0xff 58>, /* mss_to_apps_irq(1) */
- <0xff 59>, /* mss_to_apps_irq(2) */
- <0xff 60>, /* mss_to_apps_irq(3) */
- <0xff 173>, /* o_wcss_apss_smd_hi */
- <0xff 174>, /* o_wcss_apss_smd_med */
- <0xff 175>, /* o_wcss_apss_smd_low */
- <0xff 176>, /* o_wcss_apss_smsm_irq */
- <0xff 177>, /* o_wcss_apss_wlan_data_xfer_done */
- <0xff 178>, /* o_wcss_apss_wlan_rx_data_avail */
- <0xff 179>, /* o_wcss_apss_asic_intr
-
- <0xff 188>, /* lpass_irq_out_apcs(0) */
- <0xff 189>, /* lpass_irq_out_apcs(1) */
- <0xff 190>, /* lpass_irq_out_apcs(2) */
- <0xff 191>, /* lpass_irq_out_apcs(3) */
- <0xff 192>, /* lpass_irq_out_apcs(4) */
- <0xff 193>, /* lpass_irq_out_apcs(5) */
- <0xff 194>, /* lpass_irq_out_apcs(6) */
- <0xff 195>, /* lpass_irq_out_apcs(7) */
- <0xff 196>, /* lpass_irq_out_apcs(8) */
- <0xff 197>, /* lpass_irq_out_apcs(9) */
- <0xff 200>, /* rpm_ipc(4) */
- <0xff 201>, /* rpm_ipc(5) */
- <0xff 202>, /* rpm_ipc(6) */
- <0xff 203>, /* rpm_ipc(7) */
- <0xff 204>, /* rpm_ipc(24) */
- <0xff 205>, /* rpm_ipc(25) */
- <0xff 206>, /* rpm_ipc(26) */
- <0xff 207>, /* rpm_ipc(27) */
- <0xff 240>; /* summary_irq_kpss */
-
- qcom,gpio-parent = <&msmgpio>;
- qcom,gpio-map = <3 102>,
- <4 1 >,
- <5 5 >,
- <6 9 >,
- <7 18>,
- <8 20>,
- <9 24>,
- <10 27>,
- <11 28>,
- <12 34>,
- <13 35>,
- <14 37>,
- <15 42>,
- <16 44>,
- <17 46>,
- <18 50>,
- <19 54>,
- <20 59>,
- <21 61>,
- <22 62>,
- <23 64>,
- <24 65>,
- <25 66>,
- <26 67>,
- <27 68>,
- <28 71>,
- <29 72>,
- <30 73>,
- <31 74>,
- <32 75>,
- <33 77>,
- <34 79>,
- <35 80>,
- <36 82>,
- <37 86>,
- <38 92>,
- <39 93>,
- <40 95>;
- };
-
- qcom,pm-8x60@fe805664 {
- compatible = "qcom,pm-8x60";
- reg = <0xfe805664 0x40>;
- qcom,pc-mode = <0>; /*MSM_PC_TZ_L2_INT */
- qcom,use-sync-timer;
- qcom,saw-turns-off-pll;
- };
-
- qcom,rpm-log@fc19dc00 {
- compatible = "qcom,rpm-log";
- reg = <0xfc19dc00 0x4000>;
- qcom,rpm-addr-phys = <0xfc000000>;
- qcom,offset-version = <4>;
- qcom,offset-page-buffer-addr = <36>;
- qcom,offset-log-len = <40>;
- qcom,offset-log-len-mask = <44>;
- qcom,offset-page-indices = <56>;
- };
-
- qcom,rpm-stats@0xfc19dbd0{
- compatible = "qcom,rpm-stats";
- reg = <0xfc19dbd0 0x1000>;
- reg-names = "phys_addr_base";
- qcom,sleep-stats-version = <2>;
- };
-};
diff --git a/arch/arm/boot/dts/msm8974-v2.dtsi b/arch/arm/boot/dts/msm8974-v2.dtsi
index a1afda1..f0b7f3f 100644
--- a/arch/arm/boot/dts/msm8974-v2.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2.dtsi
@@ -18,7 +18,6 @@
/include/ "msm8974.dtsi"
/include/ "msm8974-v2-iommu.dtsi"
-/include/ "msm8974-v2-pm.dtsi"
/ {
android_usb@fe8050c8 {
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index b342fd8..68f1082 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -11,6 +11,7 @@
*/
/include/ "skeleton.dtsi"
+/include/ "msm8974-pm.dtsi"
/include/ "msm8974-camera.dtsi"
/include/ "msm8974-coresight.dtsi"
/include/ "msm-gdsc.dtsi"
@@ -1225,6 +1226,11 @@
qcom,bcl {
compatible = "qcom,bcl";
};
+
+ qcom,ssm {
+ compatible = "qcom,ssm";
+ qcom,channel-name = "SSM_RTR";
+ };
};
&gdsc_venus {
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index 9247826..e9ca053 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -735,7 +735,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <0>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
@@ -746,7 +746,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <2>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
@@ -757,7 +757,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <2>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
@@ -768,7 +768,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <4>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
@@ -779,7 +779,7 @@
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <4>;
- qcom,hw-settle-time = <0>;
+ qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
};
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index ecf43bb..9a1f872 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -247,6 +247,7 @@
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_POWER_ON=y
CONFIG_IPA=y
+CONFIG_ECM_IPA=y
CONFIG_CORESIGHT=y
CONFIG_CORESIGHT_TMC=y
CONFIG_CORESIGHT_TPIU=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index e35a806..32e9391 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -411,6 +411,7 @@
select CPU_FREQ_GOV_USERSPACE
select CPU_FREQ_GOV_ONDEMAND
select MSM_PIL
+ select MSM_RUN_QUEUE_STATS
config ARCH_MSM8226
bool "MSM8226"
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index cfa9281..f9e7863 100644
--- a/arch/arm/mach-msm/devices-msm8x60.c
+++ b/arch/arm/mach-msm/devices-msm8x60.c
@@ -412,6 +412,7 @@
.uart_tx_gpio = 67,
.uart_rx_gpio = 66,
.line = 1,
+ .set_uart_clk_zero = true,
};
static struct resource msm_uart_gsbi9_resources[] = {
diff --git a/arch/arm/mach-msm/include/mach/ecm_ipa.h b/arch/arm/mach-msm/include/mach/ecm_ipa.h
new file mode 100644
index 0000000..008a659
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/ecm_ipa.h
@@ -0,0 +1,76 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef _ECM_IPA_H_
+#define _ECM_IPA_H_
+
+#include <mach/ipa.h>
+
+/*
+ * @priv: private data given upon ipa_connect
+ * @evt: event enum, should be IPA_WRITE_DONE
+ * @data: for tx path the data field is the sent socket buffer.
+ */
+typedef void (*ecm_ipa_callback)(void *priv,
+ enum ipa_dp_evt_type evt,
+ unsigned long data);
+
+
+#ifdef CONFIG_ECM_IPA
+
+int ecm_ipa_init(ecm_ipa_callback * ecm_ipa_rx_dp_notify,
+ ecm_ipa_callback * ecm_ipa_tx_dp_notify,
+ void **priv);
+
+int ecm_ipa_configure(u8 host_ethaddr[], u8 device_ethaddr[],
+ void *priv);
+
+int ecm_ipa_connect(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl,
+ void *priv);
+
+int ecm_ipa_disconnect(void *priv);
+
+void ecm_ipa_cleanup(void *priv);
+
+#else /* CONFIG_ECM_IPA*/
+
+static inline int ecm_ipa_init(ecm_ipa_callback *ecm_ipa_rx_dp_notify,
+ ecm_ipa_callback *ecm_ipa_tx_dp_notify,
+ void **priv)
+{
+ return 0;
+}
+
+static inline int ecm_ipa_configure(u8 host_ethaddr[], u8 device_ethaddr[],
+ void *priv)
+{
+ return 0;
+}
+
+static inline int ecm_ipa_connect(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl,
+ void *priv)
+{
+ return 0;
+}
+
+static inline int ecm_ipa_disconnect(void *priv)
+{
+ return 0;
+}
+
+static inline void ecm_ipa_cleanup(void *priv)
+{
+
+}
+#endif /* CONFIG_ECM_IPA*/
+
+#endif /* _ECM_IPA_H_ */
diff --git a/arch/arm/mach-msm/include/mach/ipa.h b/arch/arm/mach-msm/include/mach/ipa.h
index 26a055d..564c523 100644
--- a/arch/arm/mach-msm/include/mach/ipa.h
+++ b/arch/arm/mach-msm/include/mach/ipa.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -354,6 +354,90 @@
IPA_BRIDGE_TYPE_MAX
};
+/**
+ * enum ipa_rm_resource_name - IPA RM clients identification names
+ *
+ * Add new mapping to ipa_rm_dep_prod_index() / ipa_rm_dep_cons_index()
+ * when adding new entry to this enum.
+ */
+enum ipa_rm_resource_name {
+ IPA_RM_RESOURCE_PROD = 0,
+ IPA_RM_RESOURCE_BRIDGE_PROD = IPA_RM_RESOURCE_PROD,
+ IPA_RM_RESOURCE_A2_PROD,
+ IPA_RM_RESOURCE_USB_PROD,
+ IPA_RM_RESOURCE_HSIC_PROD,
+ IPA_RM_RESOURCE_STD_ECM_PROD,
+ IPA_RM_RESOURCE_WWAN_0_PROD,
+ IPA_RM_RESOURCE_WWAN_1_PROD,
+ IPA_RM_RESOURCE_WWAN_2_PROD,
+ IPA_RM_RESOURCE_WWAN_3_PROD,
+ IPA_RM_RESOURCE_WWAN_4_PROD,
+ IPA_RM_RESOURCE_WWAN_5_PROD,
+ IPA_RM_RESOURCE_WWAN_6_PROD,
+ IPA_RM_RESOURCE_WWAN_7_PROD,
+ IPA_RM_RESOURCE_WLAN_PROD,
+ IPA_RM_RESOURCE_PROD_MAX,
+
+ IPA_RM_RESOURCE_A2_CONS = IPA_RM_RESOURCE_PROD_MAX,
+ IPA_RM_RESOURCE_USB_CONS,
+ IPA_RM_RESOURCE_HSIC_CONS,
+ IPA_RM_RESOURCE_MAX
+};
+
+/**
+ * enum ipa_rm_event - IPA RM events
+ *
+ * Indicate the resource state change
+ */
+enum ipa_rm_event {
+ IPA_RM_RESOURCE_GRANTED,
+ IPA_RM_RESOURCE_RELEASED
+};
+
+typedef void (*ipa_rm_notify_cb)(void *user_data,
+ enum ipa_rm_event event,
+ unsigned long data);
+/**
+ * struct ipa_rm_register_params - information needed to
+ * register IPA RM client with IPA RM
+ *
+ * @user_data: IPA RM client provided information
+ * to be passed to notify_cb callback below
+ * @notify_cb: callback which is called by resource
+ * to notify the IPA RM client about its state
+ * change IPA RM client is expected to perform non
+ * blocking operations only in notify_cb and
+ * release notification context as soon as
+ * possible.
+ */
+struct ipa_rm_register_params {
+ void *user_data;
+ ipa_rm_notify_cb notify_cb;
+};
+
+/**
+ * struct ipa_rm_create_params - information needed to initialize
+ * the resource
+ * @name: resource name
+ * @reg_params: register parameters, contains are ignored
+ * for consumer resource NULL should be provided
+ * for consumer resource
+ * @request_resource: function which should be called to request resource,
+ * NULL should be provided for producer resource
+ * @release_resource: function which should be called to release resource,
+ * NULL should be provided for producer resource
+ *
+ * IPA RM client is expected to perform non blocking operations only
+ * in request_resource and release_resource functions and
+ * release notification context as soon as possible.
+ */
+struct ipa_rm_create_params {
+ enum ipa_rm_resource_name name;
+ struct ipa_rm_register_params reg_params;
+ int (*request_resource)(void);
+ int (*release_resource)(void);
+};
+
#ifdef CONFIG_IPA
/*
@@ -489,6 +573,41 @@
int ipa_teardown_sys_pipe(u32 clnt_hdl);
+/*
+ * Resource manager
+ */
+int ipa_rm_create_resource(struct ipa_rm_create_params *create_params);
+
+int ipa_rm_register(enum ipa_rm_resource_name resource_name,
+ struct ipa_rm_register_params *reg_params);
+
+int ipa_rm_deregister(enum ipa_rm_resource_name resource_name,
+ struct ipa_rm_register_params *reg_params);
+
+int ipa_rm_add_dependency(enum ipa_rm_resource_name resource_name,
+ enum ipa_rm_resource_name depends_on_name);
+
+int ipa_rm_delete_dependency(enum ipa_rm_resource_name resource_name,
+ enum ipa_rm_resource_name depends_on_name);
+
+int ipa_rm_request_resource(enum ipa_rm_resource_name resource_name);
+
+int ipa_rm_release_resource(enum ipa_rm_resource_name resource_name);
+
+int ipa_rm_notify_completion(enum ipa_rm_event event,
+ enum ipa_rm_resource_name resource_name);
+
+int ipa_rm_inactivity_timer_init(enum ipa_rm_resource_name resource_name,
+ unsigned long msecs);
+
+int ipa_rm_inactivity_timer_destroy(enum ipa_rm_resource_name resource_name);
+
+int ipa_rm_inactivity_timer_request_resource(
+ enum ipa_rm_resource_name resource_name);
+
+int ipa_rm_inactivity_timer_release_resource(
+ enum ipa_rm_resource_name resource_name);
+
#else /* CONFIG_IPA */
/*
@@ -778,6 +897,84 @@
return -EPERM;
}
+/*
+ * Resource manager
+ */
+static inline int ipa_rm_create_resource(
+ struct ipa_rm_create_params *create_params)
+{
+ return -EPERM;
+}
+
+static inline int ipa_rm_register(enum ipa_rm_resource_name resource_name,
+ struct ipa_rm_register_params *reg_params)
+{
+ return -EPERM;
+}
+
+static inline int ipa_rm_deregister(enum ipa_rm_resource_name resource_name,
+ struct ipa_rm_register_params *reg_params)
+{
+ return -EPERM;
+}
+
+static inline int ipa_rm_add_dependency(
+ enum ipa_rm_resource_name resource_name,
+ enum ipa_rm_resource_name depends_on_name)
+{
+ return -EPERM;
+}
+
+static inline int ipa_rm_delete_dependency(
+ enum ipa_rm_resource_name resource_name,
+ enum ipa_rm_resource_name depends_on_name)
+{
+ return -EPERM;
+}
+
+static inline int ipa_rm_request_resource(
+ enum ipa_rm_resource_name resource_name)
+{
+ return -EPERM;
+}
+
+static inline int ipa_rm_release_resource(
+ enum ipa_rm_resource_name resource_name)
+{
+ return -EPERM;
+}
+
+static inline int ipa_rm_notify_completion(enum ipa_rm_event event,
+ enum ipa_rm_resource_name resource_name)
+{
+ return -EPERM;
+}
+
+static inline int ipa_rm_inactivity_timer_init(
+ enum ipa_rm_resource_name resource_name,
+ unsigned long msecs)
+{
+ return -EPERM;
+}
+
+static inline int ipa_rm_inactivity_timer_destroy(
+ enum ipa_rm_resource_name resource_name)
+{
+ return -EPERM;
+}
+
+static inline int ipa_rm_inactivity_timer_request_resource(
+ enum ipa_rm_resource_name resource_name)
+{
+ return -EPERM;
+}
+
+static inline int ipa_rm_inactivity_timer_release_resource(
+ enum ipa_rm_resource_name resource_name)
+{
+ return -EPERM;
+}
+
#endif /* CONFIG_IPA*/
#endif /* _IPA_H_ */
diff --git a/arch/arm/mach-msm/include/mach/msm_ipc_logging.h b/arch/arm/mach-msm/include/mach/msm_ipc_logging.h
index ec9fdb0..b675c00 100644
--- a/arch/arm/mach-msm/include/mach/msm_ipc_logging.h
+++ b/arch/arm/mach-msm/include/mach/msm_ipc_logging.h
@@ -113,7 +113,7 @@
* @ilctxt: Debug Log Context created using ipc_log_context_create()
* @fmt: Data specified using format specifiers
*/
-int ipc_log_string(void *ilctxt, const char *fmt, ...);
+int ipc_log_string(void *ilctxt, const char *fmt, ...) __printf(2, 3);
/*
* Print a string to decode context.
diff --git a/arch/arm/mach-msm/include/mach/msm_serial_hs_lite.h b/arch/arm/mach-msm/include/mach/msm_serial_hs_lite.h
index 7bdd35a..7a24190 100644
--- a/arch/arm/mach-msm/include/mach/msm_serial_hs_lite.h
+++ b/arch/arm/mach-msm/include/mach/msm_serial_hs_lite.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013 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
@@ -23,6 +23,9 @@
* @uart_rx_gpio: GPIO number for UART Rx Line.
* @uart_cts_gpio: GPIO number for UART CTS Line.
* @uart_rfr_gpio: GPIO number for UART RFR Line.
+ * @set_uart_clk_zero: use this if setting UART Clock to zero is required
+ * It is mainly required where same UART is used across different processor.
+ * Make sure that Clock driver for platform support setting clock rate to zero.
* @use_pm: use this to enable power management
* @line: Used to set UART Port number.
*/
@@ -32,6 +35,7 @@
unsigned uart_rx_gpio;
unsigned uart_cts_gpio;
unsigned uart_rfr_gpio;
+ bool set_uart_clk_zero;
bool use_pm;
int line;
};
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c b/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
index 658c07b..8153145 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
@@ -185,6 +185,25 @@
}
break;
}
+ case AUDIO_SET_AAC_MIX_CONFIG: {
+ pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG", __func__);
+ if (copy_from_user(audio->codec_cfg, (void *)arg,
+ sizeof(unsigned long))) {
+ rc = -EFAULT;
+ break;
+ } else {
+ unsigned long *mix_coeff =
+ (unsigned long *)audio->codec_cfg;
+ pr_debug("%s, value of coeff = %lu",
+ __func__, *mix_coeff);
+ q6asm_cfg_aac_sel_mix_coef(audio->ac, *mix_coeff);
+ if (rc < 0)
+ pr_err("%s asm aac_sel_mix_coef failed rc=%d\n",
+ __func__, rc);
+ break;
+ }
+ break;
+ }
default:
pr_debug("Calling utils ioctl\n");
rc = audio->codec_ioctl(file, cmd, arg);
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
index 11b1405..ff7ba33 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -327,6 +327,7 @@
return usc;
fail:
+ kfree(p_mem_handle);
q6usm_us_client_free(usc);
return NULL;
fail_session:
diff --git a/drivers/char/diag/diagfwd_hsic.c b/drivers/char/diag/diagfwd_hsic.c
index aa55578..616c498 100644
--- a/drivers/char/diag/diagfwd_hsic.c
+++ b/drivers/char/diag/diagfwd_hsic.c
@@ -45,6 +45,7 @@
struct diag_hsic_dev *hsic_struct = container_of(work,
struct diag_hsic_dev, diag_read_hsic_work);
int index = hsic_struct->id;
+ static DEFINE_RATELIMIT_STATE(rl, 10*HZ, 1);
if (!diag_hsic[index].hsic_ch) {
pr_err("DIAG in %s: diag_hsic[index].hsic_ch == 0\n", __func__);
@@ -103,7 +104,8 @@
diagmem_free(driver, buf_in_hsic,
index+POOL_TYPE_HSIC);
- pr_err_ratelimited("diag: Error initiating HSIC read, err: %d\n",
+ if (__ratelimit(&rl))
+ pr_err("diag: Error initiating HSIC read, err: %d\n",
err);
/*
* An error occurred, discontinue queuing
@@ -132,6 +134,7 @@
{
int err = -2;
int index = (int)ctxt;
+ static DEFINE_RATELIMIT_STATE(rl, 10*HZ, 1);
if (!diag_hsic[index].hsic_ch) {
/*
@@ -164,7 +167,8 @@
if (err) {
diagmem_free(driver, buf, index +
POOL_TYPE_HSIC);
- pr_err_ratelimited("diag: In %s, error calling diag_device_write, err: %d\n",
+ if (__ratelimit(&rl))
+ pr_err("diag: In %s, error calling diag_device_write, err: %d\n",
__func__, err);
}
}
diff --git a/drivers/gpu/ion/Kconfig b/drivers/gpu/ion/Kconfig
index 5bb254b..39133b5 100644
--- a/drivers/gpu/ion/Kconfig
+++ b/drivers/gpu/ion/Kconfig
@@ -16,3 +16,12 @@
depends on ARCH_MSM && ION
help
Choose this option if you wish to use ion on an MSM target.
+
+config ION_LEAK_CHECK
+ bool "Check for leaked Ion buffers (debugging)"
+ depends on ION
+ help
+ Choose this option if you wish to enable checking for leaked
+ ion buffers at runtime. Choosing this option will also add a
+ debugfs node under the ion directory that can be used to
+ enable/disable the leak checking.
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index e97f652..0904f9fe 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -1024,10 +1024,91 @@
return client;
}
+/**
+ * ion_mark_dangling_buffers_locked() - Mark dangling buffers
+ * @dev: the ion device whose buffers will be searched
+ *
+ * Sets marked=1 for all known buffers associated with `dev' that no
+ * longer have a handle pointing to them. dev->lock should be held
+ * across a call to this function (and should only be unlocked after
+ * checking for marked buffers).
+ */
+static void ion_mark_dangling_buffers_locked(struct ion_device *dev)
+{
+ struct rb_node *n, *n2;
+ /* mark all buffers as 1 */
+ for (n = rb_first(&dev->buffers); n; n = rb_next(n)) {
+ struct ion_buffer *buf = rb_entry(n, struct ion_buffer,
+ node);
+
+ buf->marked = 1;
+ }
+
+ /* now see which buffers we can access */
+ for (n = rb_first(&dev->clients); n; n = rb_next(n)) {
+ struct ion_client *client = rb_entry(n, struct ion_client,
+ node);
+
+ mutex_lock(&client->lock);
+ for (n2 = rb_first(&client->handles); n2; n2 = rb_next(n2)) {
+ struct ion_handle *handle
+ = rb_entry(n2, struct ion_handle, node);
+
+ handle->buffer->marked = 0;
+
+ }
+ mutex_unlock(&client->lock);
+
+ }
+}
+
+#ifdef CONFIG_ION_LEAK_CHECK
+static u32 ion_debug_check_leaks_on_destroy;
+
+static int ion_check_for_and_print_leaks(struct ion_device *dev)
+{
+ struct rb_node *n;
+ int num_leaks = 0;
+
+ if (!ion_debug_check_leaks_on_destroy)
+ return 0;
+
+ /* check for leaked buffers (those that no longer have a
+ * handle pointing to them) */
+ ion_mark_dangling_buffers_locked(dev);
+
+ /* Anyone still marked as a 1 means a leaked handle somewhere */
+ for (n = rb_first(&dev->buffers); n; n = rb_next(n)) {
+ struct ion_buffer *buf = rb_entry(n, struct ion_buffer,
+ node);
+
+ if (buf->marked == 1) {
+ pr_info("Leaked ion buffer at %p\n", buf);
+ num_leaks++;
+ }
+ }
+ return num_leaks;
+}
+static void setup_ion_leak_check(struct dentry *debug_root)
+{
+ debugfs_create_bool("check_leaks_on_destroy", 0664, debug_root,
+ &ion_debug_check_leaks_on_destroy);
+}
+#else
+static int ion_check_for_and_print_leaks(struct ion_device *dev)
+{
+ return 0;
+}
+static void setup_ion_leak_check(struct dentry *debug_root)
+{
+}
+#endif
+
void ion_client_destroy(struct ion_client *client)
{
struct ion_device *dev = client->dev;
struct rb_node *n;
+ int num_leaks;
pr_debug("%s: %d\n", __func__, __LINE__);
while ((n = rb_first(&client->handles))) {
@@ -1040,8 +1121,21 @@
put_task_struct(client->task);
rb_erase(&client->node, &dev->clients);
debugfs_remove_recursive(client->debug_root);
+
+ num_leaks = ion_check_for_and_print_leaks(dev);
+
mutex_unlock(&dev->lock);
+ if (num_leaks) {
+ struct task_struct *current_task = current;
+ char current_task_name[TASK_COMM_LEN];
+ get_task_comm(current_task_name, current_task);
+ WARN(1, "%s: Detected %d leaked ion buffer%s.\n",
+ __func__, num_leaks, num_leaks == 1 ? "" : "s");
+ pr_info("task name at time of leak: %s, pid: %d\n",
+ current_task_name, current_task->pid);
+ }
+
kfree(client->name);
kfree(client);
}
@@ -1397,9 +1491,6 @@
case ION_IOC_CLEAN_INV_CACHES:
return client->dev->custom_ioctl(client,
ION_IOC_CLEAN_INV_CACHES, arg);
- case ION_IOC_GET_FLAGS:
- return client->dev->custom_ioctl(client,
- ION_IOC_GET_FLAGS, arg);
default:
return -ENOTTY;
}
@@ -1807,37 +1898,14 @@
{
struct ion_device *dev = s->private;
struct rb_node *n;
- struct rb_node *n2;
- /* mark all buffers as 1 */
seq_printf(s, "%16.s %16.s %16.s %16.s\n", "buffer", "heap", "size",
"ref cnt");
+
mutex_lock(&dev->lock);
- for (n = rb_first(&dev->buffers); n; n = rb_next(n)) {
- struct ion_buffer *buf = rb_entry(n, struct ion_buffer,
- node);
+ ion_mark_dangling_buffers_locked(dev);
- buf->marked = 1;
- }
-
- /* now see which buffers we can access */
- for (n = rb_first(&dev->clients); n; n = rb_next(n)) {
- struct ion_client *client = rb_entry(n, struct ion_client,
- node);
-
- mutex_lock(&client->lock);
- for (n2 = rb_first(&client->handles); n2; n2 = rb_next(n2)) {
- struct ion_handle *handle = rb_entry(n2,
- struct ion_handle, node);
-
- handle->buffer->marked = 0;
-
- }
- mutex_unlock(&client->lock);
-
- }
-
- /* And anyone still marked as a 1 means a leaked handle somewhere */
+ /* Anyone still marked as a 1 means a leaked handle somewhere */
for (n = rb_first(&dev->buffers); n; n = rb_next(n)) {
struct ion_buffer *buf = rb_entry(n, struct ion_buffer,
node);
@@ -1898,6 +1966,8 @@
idev->clients = RB_ROOT;
debugfs_create_file("check_leaked_fds", 0664, idev->debug_root, idev,
&debug_leak_fops);
+
+ setup_ion_leak_check(idev->debug_root);
return idev;
}
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index b660968..1ca457f 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -734,22 +734,6 @@
break;
}
- case ION_IOC_GET_FLAGS:
- {
- struct ion_flag_data data;
- int ret;
- if (copy_from_user(&data, (void __user *)arg,
- sizeof(struct ion_flag_data)))
- return -EFAULT;
-
- ret = ion_handle_get_flags(client, data.handle, &data.flags);
- if (ret < 0)
- return ret;
- if (copy_to_user((void __user *)arg, &data,
- sizeof(struct ion_flag_data)))
- return -EFAULT;
- break;
- }
default:
return -ENOTTY;
}
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index e4a9e30..2017c8d 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -391,10 +391,13 @@
struct qpnp_iadc_drv *iadc = qpnp_iadc;
uint32_t num = 0;
- num = iadc->adc->calib.offset_raw - iadc->adc->calib.offset_raw;
+ if ((iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw) == 0) {
+ pr_err("raw offset errors! raw_gain:0x%x and raw_offset:0x%x\n",
+ iadc->adc->calib.gain_raw, iadc->adc->calib.offset_raw);
+ return -EINVAL;
+ }
- iadc->adc->calib.offset_uv = (num * QPNP_ADC_GAIN_NV)/
- (iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw);
+ iadc->adc->calib.offset_uv = 0;
num = iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw;
@@ -437,6 +440,10 @@
}
rc = qpnp_convert_raw_offset_voltage();
+ if (rc < 0) {
+ pr_err("qpnp raw_voltage conversion failed\n");
+ goto fail;
+ }
rslt_msb = (raw_data & QPNP_RAW_CODE_16_BIT_MSB_MASK) >>
QPNP_BIT_SHIFT_8;
@@ -558,7 +565,7 @@
{
struct qpnp_iadc_drv *iadc = qpnp_iadc;
struct qpnp_vadc_result result_pmic_therm;
- int rc;
+ int rc = 0;
rc = qpnp_vadc_read(DIE_TEMP, &result_pmic_therm);
if (rc < 0)
@@ -572,7 +579,7 @@
pr_err("periodic IADC calibration failed\n");
}
- return 0;
+ return rc;
}
int32_t qpnp_iadc_read(enum qpnp_iadc_channels channel,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
index d714ffb..69d523c 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
@@ -77,8 +77,6 @@
void (*process_axi_irq) (struct vfe_device *vfe_dev,
uint32_t irq_status0, uint32_t irq_status1,
struct msm_isp_timestamp *ts);
- void (*process_error_irq) (struct vfe_device *vfe_dev,
- uint32_t irq_status0, uint32_t irq_status1);
void (*process_stats_irq) (struct vfe_device *vfe_dev,
uint32_t irq_status0, uint32_t irq_status1,
struct msm_isp_timestamp *ts);
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
index 8f00e80..b981653 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
@@ -17,11 +17,13 @@
#include "msm_isp32.h"
#include "msm_isp_util.h"
#include "msm_isp_axi_util.h"
+#include "msm_isp_stats_util.h"
#include "msm_isp.h"
#include "msm.h"
#include "msm_camera_io_util.h"
-#define VFE32_BURST_LEN 4
+#define VFE32_BURST_LEN 3
+#define VFE32_UB_SIZE 1024
#define VFE32_EQUAL_SLICE_UB 117
#define VFE32_WM_BASE(idx) (0x4C + 0x18 * idx)
#define VFE32_RDI_BASE(idx) (idx ? 0x734 + 0x4 * (idx - 1) : 0x06FC)
@@ -30,6 +32,13 @@
#define VFE32_PING_PONG_BASE(wm, ping_pong) \
(VFE32_WM_BASE(wm) + 0x4 * (1 + (~(ping_pong >> wm) & 0x1)))
+#define VFE32_NUM_STATS_TYPE 7
+#define VFE32_STATS_PING_PONG_OFFSET 7
+#define VFE32_STATS_BASE(idx) (0xF4 + 0xC * idx)
+#define VFE32_STATS_PING_PONG_BASE(idx, ping_pong) \
+ (VFE32_STATS_BASE(idx) + 0x4 * \
+ (~(ping_pong >> (idx + VFE32_STATS_PING_PONG_OFFSET)) & 0x1))
+
/*Temporary use fixed bus vectors in VFE */
static struct msm_bus_vectors msm_vfe32_init_vectors[] = {
{
@@ -177,17 +186,16 @@
ISP_DBG("%s: PIX0 frame id: %lu\n", __func__,
vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
msm_isp_sof_notify(vfe_dev, VFE_PIX_0, ts);
+ ISP_DBG("%s: SOF IRQ\n", __func__);
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].raw_stream_count > 0
+ && vfe_dev->axi_data.src_info[VFE_PIX_0].
+ pix_stream_count == 0) {
+ msm_isp_sof_notify(vfe_dev, VFE_PIX_0, ts);
+ msm_isp_update_framedrop_reg(vfe_dev);
+ }
}
}
-static void msm_vfe32_process_stats_irq(struct vfe_device *vfe_dev,
- uint32_t irq_status0, uint32_t irq_status1,
- struct msm_isp_timestamp *ts)
-{
- /* todo: add stats specific code */
- return;
-}
-
static void msm_vfe32_process_violation_status(struct vfe_device *vfe_dev)
{
uint32_t violation_status = vfe_dev->error_info.violation_status;
@@ -326,12 +334,22 @@
if (!(irq_status0 & 0x20) && !(irq_status1 & 0x1C000000))
return;
+ if (irq_status0 & BIT(5))
+ msm_isp_sof_notify(vfe_dev, VFE_PIX_0, ts);
+ if (irq_status1 & BIT(26))
+ msm_isp_sof_notify(vfe_dev, VFE_RAW_0, ts);
+ if (irq_status1 & BIT(27))
+ msm_isp_sof_notify(vfe_dev, VFE_RAW_1, ts);
+ if (irq_status1 & BIT(28))
+ msm_isp_sof_notify(vfe_dev, VFE_RAW_2, ts);
+
if (vfe_dev->axi_data.stream_update)
msm_isp_axi_stream_update(vfe_dev);
-
msm_isp_update_framedrop_reg(vfe_dev);
msm_isp_update_error_frame_count(vfe_dev);
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev);
return;
}
@@ -462,6 +480,46 @@
}
}
+static void msm_vfe32_cfg_io_format(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_req_cmd)
+{
+ int bpp, bpp_reg = 0;
+ uint32_t io_format_reg;
+ bpp = msm_isp_get_bit_per_pixel(stream_req_cmd->output_format);
+
+ switch (bpp) {
+ case 8:
+ bpp_reg = 0;
+ break;
+ case 10:
+ bpp_reg = 1 << 0;
+ break;
+ case 12:
+ bpp_reg = 1 << 1;
+ break;
+ }
+ io_format_reg = msm_camera_io_r(vfe_dev->vfe_base + 0x6F8);
+ switch (stream_req_cmd->stream_src) {
+ case CAMIF_RAW:
+ io_format_reg &= 0xFFFFCFFF;
+ io_format_reg |= bpp_reg << 12;
+ break;
+ case IDEAL_RAW:
+ io_format_reg &= 0xFFFFFFC8;
+ io_format_reg |= bpp_reg << 4;
+ break;
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER:
+ case RDI_INTF_0:
+ case RDI_INTF_1:
+ case RDI_INTF_2:
+ default:
+ pr_err("%s: Invalid stream source\n", __func__);
+ return;
+ }
+ msm_camera_io_w(io_format_reg, vfe_dev->vfe_base + 0x6F8);
+}
+
static void msm_vfe32_cfg_camif(struct vfe_device *vfe_dev,
struct msm_vfe_pix_cfg *pix_cfg)
{
@@ -481,10 +539,10 @@
camif_cfg->pixels_per_line,
vfe_dev->vfe_base + 0x1EC);
- msm_camera_io_w(ISP_SUB(first_pixel) << 16 | ISP_SUB(last_pixel),
+ msm_camera_io_w(first_pixel << 16 | last_pixel,
vfe_dev->vfe_base + 0x1F0);
- msm_camera_io_w(ISP_SUB(first_line) << 16 | ISP_SUB(last_line),
+ msm_camera_io_w(first_line << 16 | last_line,
vfe_dev->vfe_base + 0x1F4);
val = msm_camera_io_r(vfe_dev->vfe_base + 0x6FC);
@@ -518,6 +576,9 @@
} else if (update_state == DISABLE_CAMIF) {
msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x1E0);
vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
+ } else if (update_state == DISABLE_CAMIF_IMMEDIATELY) {
+ msm_camera_io_w_mb(0x2, vfe_dev->vfe_base + 0x1E0);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
}
}
@@ -723,7 +784,28 @@
static int msm_vfe32_get_stats_idx(enum msm_isp_stats_type stats_type)
{
- return 0;
+ switch (stats_type) {
+ case MSM_ISP_STATS_AEC:
+ case MSM_ISP_STATS_BG:
+ return 0;
+ case MSM_ISP_STATS_AF:
+ case MSM_ISP_STATS_BF:
+ return 1;
+ case MSM_ISP_STATS_AWB:
+ return 2;
+ case MSM_ISP_STATS_RS:
+ return 3;
+ case MSM_ISP_STATS_CS:
+ return 4;
+ case MSM_ISP_STATS_IHIST:
+ return 5;
+ case MSM_ISP_STATS_SKIN:
+ case MSM_ISP_STATS_BHIST:
+ return 6;
+ default:
+ pr_err("%s: Invalid stats type\n", __func__);
+ return -EINVAL;
+ }
}
static void msm_vfe32_stats_cfg_comp_mask(struct vfe_device *vfe_dev)
@@ -734,60 +816,120 @@
static void msm_vfe32_stats_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ uint32_t irq_mask;
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ irq_mask |= BIT(STATS_IDX(stream_info->stream_handle) + 13);
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
return;
}
static void msm_vfe32_stats_clear_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ uint32_t irq_mask;
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ irq_mask &= ~(BIT(STATS_IDX(stream_info->stream_handle) + 13));
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
return;
}
static void msm_vfe32_stats_cfg_wm_reg(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ /*Nothing to configure for VFE3.x*/
return;
}
static void msm_vfe32_stats_clear_wm_reg(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ /*Nothing to configure for VFE3.x*/
return;
}
static void msm_vfe32_stats_cfg_ub(struct vfe_device *vfe_dev)
{
+ int i;
+ uint32_t ub_offset = VFE32_UB_SIZE;
+ uint32_t ub_size[VFE32_NUM_STATS_TYPE] = {
+ 64, /*MSM_ISP_STATS_BG*/
+ 64, /*MSM_ISP_STATS_BF*/
+ 16, /*MSM_ISP_STATS_AWB*/
+ 8, /*MSM_ISP_STATS_RS*/
+ 16, /*MSM_ISP_STATS_CS*/
+ 16, /*MSM_ISP_STATS_IHIST*/
+ 16, /*MSM_ISP_STATS_BHIST*/
+ };
+
+ for (i = 0; i < VFE32_NUM_STATS_TYPE; i++) {
+ ub_offset -= ub_size[i];
+ msm_camera_io_w(ub_offset << 16 | (ub_size[i] - 1),
+ vfe_dev->vfe_base + VFE32_STATS_BASE(i) + 0x8);
+ }
return;
}
static void msm_vfe32_stats_enable_module(struct vfe_device *vfe_dev,
uint32_t stats_mask, uint8_t enable)
{
- return;
+ int i;
+ uint32_t module_cfg, module_cfg_mask = 0;
+
+ for (i = 0; i < VFE32_NUM_STATS_TYPE; i++) {
+ if ((stats_mask >> i) & 0x1) {
+ switch (i) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ module_cfg_mask |= 1 << (5 + i);
+ break;
+ case 5:
+ module_cfg_mask |= 1 << 16;
+ break;
+ case 6:
+ module_cfg_mask |= 1 << 19;
+ break;
+ default:
+ pr_err("%s: Invalid stats mask\n", __func__);
+ return;
+ }
+ }
+ }
+
+ module_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x10);
+ if (enable)
+ module_cfg |= module_cfg_mask;
+ else
+ module_cfg &= ~module_cfg_mask;
+ msm_camera_io_w(module_cfg, vfe_dev->vfe_base + 0x10);
}
static void msm_vfe32_stats_update_ping_pong_addr(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status,
unsigned long paddr)
{
- return;
+ int stats_idx = STATS_IDX(stream_info->stream_handle);
+ msm_camera_io_w(paddr, vfe_dev->vfe_base +
+ VFE32_STATS_PING_PONG_BASE(stats_idx, pingpong_status));
}
static uint32_t msm_vfe32_stats_get_wm_mask(uint32_t irq_status0,
uint32_t irq_status1)
{
- return 0;
+ return (irq_status0 >> 13) & 0x7F;
}
static uint32_t msm_vfe32_stats_get_comp_mask(uint32_t irq_status0,
uint32_t irq_status1)
{
- return 0;
+ return (irq_status0 >> 24) & 0x1;
}
static uint32_t msm_vfe32_stats_get_frame_id(struct vfe_device *vfe_dev)
{
- return 0;
+ return vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
}
static int msm_vfe32_get_platform_data(struct vfe_device *vfe_dev)
@@ -850,6 +992,18 @@
.num_rdi_master = 3,
};
+static struct msm_vfe_stats_hardware_info msm_vfe32_stats_hw_info = {
+ .stats_capability_mask =
+ 1 << MSM_ISP_STATS_AEC | 1 << MSM_ISP_STATS_BG |
+ 1 << MSM_ISP_STATS_AF | 1 << MSM_ISP_STATS_BF |
+ 1 << MSM_ISP_STATS_AWB | 1 << MSM_ISP_STATS_IHIST |
+ 1 << MSM_ISP_STATS_RS | 1 << MSM_ISP_STATS_CS |
+ 1 << MSM_ISP_STATS_SKIN | 1 << MSM_ISP_STATS_BHIST,
+ .stats_ping_pong_offset = VFE32_STATS_PING_PONG_OFFSET,
+ .num_stats_type = VFE32_NUM_STATS_TYPE,
+ .num_stats_comp_mask = 0,
+};
+
static struct v4l2_subdev_core_ops msm_vfe32_subdev_core_ops = {
.ioctl = msm_isp_ioctl,
.subscribe_event = msm_isp_subscribe_event,
@@ -875,11 +1029,12 @@
.process_halt_irq = msm_vfe32_process_halt_irq,
.process_reg_update = msm_vfe32_process_reg_update,
.process_axi_irq = msm_isp_process_axi_irq,
- .process_stats_irq = msm_vfe32_process_stats_irq,
+ .process_stats_irq = msm_isp_process_stats_irq,
},
.axi_ops = {
.reload_wm = msm_vfe32_axi_reload_wm,
.enable_wm = msm_vfe32_axi_enable_wm,
+ .cfg_io_format = msm_vfe32_cfg_io_format,
.cfg_comp_mask = msm_vfe32_axi_cfg_comp_mask,
.clear_comp_mask = msm_vfe32_axi_clear_comp_mask,
.cfg_wm_irq_mask = msm_vfe32_axi_cfg_wm_irq_mask,
@@ -930,6 +1085,7 @@
},
.dmi_reg_offset = 0x5A0,
.axi_hw_info = &msm_vfe32_axi_hw_info,
+ .stats_hw_info = &msm_vfe32_stats_hw_info,
.subdev_ops = &msm_vfe32_subdev_ops,
.subdev_internal_ops = &msm_vfe32_internal_ops,
};
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
index fa0bf18..f08644f 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
@@ -369,15 +369,10 @@
msm_isp_send_event(vfe_dev, ISP_EVENT_SOF, &sof_event);
}
-void msm_isp_calculate_framedrop(
- struct msm_vfe_axi_shared_data *axi_data,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+uint32_t msm_isp_get_framedrop_period(
+ enum msm_vfe_frame_skip_pattern frame_skip_pattern)
{
- struct msm_vfe_axi_stream *stream_info =
- &axi_data->stream_info[
- (stream_cfg_cmd->axi_stream_handle & 0xFF)];
- uint32_t framedrop_period = 1;
- switch (stream_cfg_cmd->frame_skip_pattern) {
+ switch (frame_skip_pattern) {
case NO_SKIP:
case EVERY_2FRAME:
case EVERY_3FRAME:
@@ -386,18 +381,28 @@
case EVERY_6FRAME:
case EVERY_7FRAME:
case EVERY_8FRAME:
- framedrop_period = stream_cfg_cmd->frame_skip_pattern + 1;
- break;
+ return frame_skip_pattern + 1;
case EVERY_16FRAME:
- framedrop_period = 16;
+ return 16;
break;
case EVERY_32FRAME:
- framedrop_period = 32;
+ return 32;
break;
default:
- framedrop_period = 1;
- break;
+ return 1;
}
+ return 1;
+}
+
+void msm_isp_calculate_framedrop(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ uint32_t framedrop_period = msm_isp_get_framedrop_period(
+ stream_cfg_cmd->frame_skip_pattern);
stream_info->framedrop_pattern = 0x1;
stream_info->framedrop_period = framedrop_period - 1;
@@ -911,8 +916,16 @@
stream_info->bufq_handle,
MSM_ISP_BUFFER_FLUSH_DIVERTED);
break;
- case UPDATE_STREAM_FRAMEDROP_PATTERN:
+ case UPDATE_STREAM_FRAMEDROP_PATTERN: {
+ uint32_t framedrop_period =
+ msm_isp_get_framedrop_period(update_cmd->skip_pattern);
+ stream_info->runtime_init_frame_drop = 0;
+ stream_info->framedrop_pattern = 0x1;
+ stream_info->framedrop_period = framedrop_period - 1;
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_framedrop(vfe_dev, stream_info);
break;
+ }
default:
pr_err("%s: Invalid update type\n", __func__);
return -EINVAL;
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
index 7d0f9cb..691edc3 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -94,6 +94,13 @@
if (data > 0x1) {
unsigned long jiffes = msecs_to_jiffies(500);
long lrc = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ispif->auto_complete_lock, flags);
+ ispif->wait_timeout = 0;
+ init_completion(&ispif->reset_complete);
+ spin_unlock_irqrestore(&ispif->auto_complete_lock, flags);
+
if (params->vfe_intf == VFE0)
msm_camera_io_w(data, ispif->base + ISPIF_RST_CMD_ADDR);
else
@@ -104,6 +111,11 @@
if (lrc < 0 || !lrc) {
pr_err("%s: wait timeout ret = %ld\n", __func__, lrc);
rc = -EIO;
+
+ spin_lock_irqsave(&ispif->auto_complete_lock, flags);
+ ispif->wait_timeout = 1;
+ spin_unlock_irqrestore(
+ &ispif->auto_complete_lock, flags);
}
}
return rc;
@@ -114,6 +126,12 @@
int rc = 0;
long lrc = 0;
unsigned long jiffes = msecs_to_jiffies(500);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ispif->auto_complete_lock, flags);
+ ispif->wait_timeout = 0;
+ init_completion(&ispif->reset_complete);
+ spin_unlock_irqrestore(&ispif->auto_complete_lock, flags);
BUG_ON(!ispif);
@@ -125,14 +143,17 @@
msm_camera_io_w_mb(ISPIF_RST_CMD_1_MASK, ispif->base +
ISPIF_RST_CMD_1_ADDR);
- CDBG("%s: Sending reset\n", __func__);
lrc = wait_for_completion_interruptible_timeout(
&ispif->reset_complete, jiffes);
+
if (lrc < 0 || !lrc) {
pr_err("%s: wait timeout ret = %ld\n", __func__, lrc);
rc = -EIO;
+
+ spin_lock_irqsave(&ispif->auto_complete_lock, flags);
+ ispif->wait_timeout = 1;
+ spin_unlock_irqrestore(&ispif->auto_complete_lock, flags);
}
- CDBG("%s: reset returned\n", __func__);
return rc;
}
@@ -571,8 +592,14 @@
ispif->base + ISPIF_IRQ_CLEAR_2_ADDR);
if (out[VFE0].ispifIrqStatus0 & ISPIF_IRQ_STATUS_MASK) {
- if (out[VFE0].ispifIrqStatus0 & RESET_DONE_IRQ)
- complete(&ispif->reset_complete);
+ if (out[VFE0].ispifIrqStatus0 & RESET_DONE_IRQ) {
+ unsigned long flags;
+ spin_lock_irqsave(&ispif->auto_complete_lock, flags);
+ if (ispif->wait_timeout == 0)
+ complete(&ispif->reset_complete);
+ spin_unlock_irqrestore(
+ &ispif->auto_complete_lock, flags);
+ }
if (out[VFE0].ispifIrqStatus0 & PIX_INTF_0_OVERFLOW_IRQ)
pr_err("%s: VFE0 pix0 overflow.\n", __func__);
@@ -709,8 +736,6 @@
goto error_irq;
}
- init_completion(&ispif->reset_complete);
-
rc = msm_ispif_reset(ispif);
if (rc == 0) {
ispif->ispif_state = ISPIF_POWER_UP;
@@ -830,13 +855,8 @@
struct ispif_device *ispif = v4l2_get_subdevdata(sd);
mutex_lock(&ispif->mutex);
- if (ispif->open_cnt > 0) {
- CDBG("%s: dev already open\n", __func__);
- goto end;
- }
/* mem remap is done in init when the clock is on */
ispif->open_cnt++;
-end:
mutex_unlock(&ispif->mutex);
return 0;
}
@@ -940,7 +960,8 @@
ispif->pdev = pdev;
ispif->ispif_state = ISPIF_POWER_DOWN;
ispif->open_cnt = 0;
-
+ spin_lock_init(&ispif->auto_complete_lock);
+ ispif->wait_timeout = 0;
return 0;
error:
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h
index c4418c1..f8c3cce 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h
@@ -48,6 +48,8 @@
struct mutex mutex;
uint8_t start_ack_pending;
struct completion reset_complete;
+ spinlock_t auto_complete_lock;
+ uint8_t wait_timeout;
uint32_t csid_version;
int enb_dump_reg;
uint32_t open_cnt;
diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
index 9af6674..77dc6f5 100644
--- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
@@ -35,7 +35,9 @@
}
new_entry->session_id = buf_info->session_id;
new_entry->stream_id = buf_info->stream_id;
+ mutex_lock(&buf_mngr_dev->buf_q_lock);
list_add_tail(&new_entry->entry, &buf_mngr_dev->buf_qhead);
+ mutex_unlock(&buf_mngr_dev->buf_q_lock);
buf_info->index = new_entry->vb2_buf->v4l2_buf.index;
return 0;
}
@@ -55,7 +57,9 @@
(bufs->vb2_buf,
buf_info->session_id,
buf_info->stream_id);
+ mutex_lock(&buf_mngr_dev->buf_q_lock);
list_del_init(&bufs->entry);
+ mutex_unlock(&buf_mngr_dev->buf_q_lock);
kfree(bufs);
break;
}
@@ -76,7 +80,9 @@
(bufs->vb2_buf->v4l2_buf.index == buf_info->index)) {
ret = buf_mngr_dev->vb2_ops.put_buf(bufs->vb2_buf,
buf_info->session_id, buf_info->stream_id);
+ mutex_lock(&buf_mngr_dev->buf_q_lock);
list_del_init(&bufs->entry);
+ mutex_unlock(&buf_mngr_dev->buf_q_lock);
kfree(bufs);
break;
}
@@ -156,12 +162,14 @@
&msm_buf_mngr_dev->vb2_ops);
INIT_LIST_HEAD(&msm_buf_mngr_dev->buf_qhead);
+ mutex_init(&msm_buf_mngr_dev->buf_q_lock);
end:
return rc;
}
static void __exit msm_buf_mngr_exit(void)
{
+ mutex_destroy(&msm_buf_mngr_dev->buf_q_lock);
kfree(msm_buf_mngr_dev);
}
diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h
index 7e588cc..a2b3a7e 100644
--- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h
+++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h
@@ -33,6 +33,7 @@
struct msm_buf_mngr_device {
struct list_head buf_qhead;
+ struct mutex buf_q_lock;
struct msm_sd_subdev subdev;
struct msm_sd_req_vb2_q vb2_ops;
};
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
index 88d1bf9..9d89a7e 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
@@ -847,7 +847,7 @@
mpq_dmx_info.devices = NULL;
mpq_dmx_info.ion_client = NULL;
- mpq_sdmx_check_app_loaded();
+ mpq_dmx_info.secure_demux_app_loaded = 0;
/*
* TODO: the following should be set based on the decoder:
@@ -4610,6 +4610,16 @@
int mpq_sdmx_is_loaded(void)
{
- return mpq_bypass_sdmx ? 0 : mpq_dmx_info.secure_demux_app_loaded;
+ static int sdmx_load_checked;
+
+ if (mpq_bypass_sdmx)
+ return 0;
+
+ if (!sdmx_load_checked) {
+ mpq_sdmx_check_app_loaded();
+ sdmx_load_checked = 1;
+ }
+
+ return mpq_dmx_info.secure_demux_app_loaded;
}
EXPORT_SYMBOL(mpq_sdmx_is_loaded);
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index 5d360bb..181b2b6 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -497,6 +497,14 @@
b->m.planes[i].reserved[0],
b->m.planes[i].reserved[1],
b->m.planes[i].length);
+ rc = msm_smem_cache_operations(v4l2_inst->mem_client,
+ binfo->handle[i], SMEM_CACHE_CLEAN);
+ if (rc)
+ dprintk(VIDC_WARN,
+ "CACHE Clean failed: %d, %d, %d\n",
+ b->m.planes[i].reserved[0],
+ b->m.planes[i].reserved[1],
+ b->m.planes[i].length);
}
b->m.planes[i].m.userptr = binfo->device_addr[i];
}
diff --git a/drivers/media/platform/msm/wfd/mdp-5-subdev.c b/drivers/media/platform/msm/wfd/mdp-5-subdev.c
index 5b49498..218bbe5 100644
--- a/drivers/media/platform/msm/wfd/mdp-5-subdev.c
+++ b/drivers/media/platform/msm/wfd/mdp-5-subdev.c
@@ -211,7 +211,7 @@
return -EINVAL;
}
- domain = msm_fb_get_iommu_domain();
+ domain = msm_fb_get_iommu_domain(inst->mdp, MDP_IOMMU_DOMAIN_NS);
rc = ion_map_iommu(mmap->ion_client, mregion->ion_handle,
domain, 0, SZ_4K, 0,
(unsigned long *)&mregion->paddr,
@@ -235,7 +235,7 @@
inst = mmap->cookie;
mregion = mmap->mregion;
- domain = msm_fb_get_iommu_domain();
+ domain = msm_fb_get_iommu_domain(inst->mdp, MDP_IOMMU_DOMAIN_NS);
ion_unmap_iommu(mmap->ion_client,
mregion->ion_handle,
domain, 0);
diff --git a/drivers/net/ethernet/msm/Kconfig b/drivers/net/ethernet/msm/Kconfig
index 3fced2d..e15f4a9 100644
--- a/drivers/net/ethernet/msm/Kconfig
+++ b/drivers/net/ethernet/msm/Kconfig
@@ -50,3 +50,9 @@
This driver supports Ethernet in the FSM9xxx.
To compile this driver as a module, choose M here: the
module will be called qfec.
+
+config ECM_IPA
+ tristate "STD ECM LAN Driver support"
+ depends on IPA
+ help
+ Allows LAN between Apps and tethered HOST on STD ECM
diff --git a/drivers/net/ethernet/msm/Makefile b/drivers/net/ethernet/msm/Makefile
index 7d9d4c6..e152ec7 100644
--- a/drivers/net/ethernet/msm/Makefile
+++ b/drivers/net/ethernet/msm/Makefile
@@ -7,3 +7,4 @@
obj-$(CONFIG_MSM_RMNET_BAM) += msm_rmnet_bam.o
obj-$(CONFIG_MSM_RMNET_SMUX) += msm_rmnet_smux.o
obj-$(CONFIG_QFEC) += qfec.o
+obj-$(CONFIG_ECM_IPA) += ecm_ipa.o
diff --git a/drivers/net/ethernet/msm/ecm_ipa.c b/drivers/net/ethernet/msm/ecm_ipa.c
new file mode 100644
index 0000000..605fd84
--- /dev/null
+++ b/drivers/net/ethernet/msm/ecm_ipa.c
@@ -0,0 +1,1105 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <mach/ecm_ipa.h>
+
+#define DRIVER_NAME "ecm_ipa"
+#define DRIVER_VERSION "19-Feb-2013"
+#define ECM_IPA_IPV4_HDR_NAME "ecm_eth_ipv4"
+#define ECM_IPA_IPV6_HDR_NAME "ecm_eth_ipv6"
+#define IPA_TO_USB_CLIENT IPA_CLIENT_USB_CONS
+#define INACTIVITY_MSEC_DELAY 100
+#define ECM_IPA_ERROR(fmt, args...) \
+ pr_err(DRIVER_NAME "@%s@%d@ctx:%s: "\
+ fmt, __func__, __LINE__, current->comm, ## args)
+#ifdef ECM_IPA_DEBUG_ON
+#define ECM_IPA_DEBUG(fmt, args...) \
+ pr_err(DRIVER_NAME "@%s@%d@ctx:%s: "\
+ fmt, __func__, __LINE__, current->comm, ## args)
+#else /* ECM_IPA_DEBUG_ON */
+#define ECM_IPA_DEBUG(fmt, args...)
+#endif /* ECM_IPA_DEBUG_ON */
+
+#define NULL_CHECK(ptr) \
+ do { \
+ if (!(ptr)) { \
+ ECM_IPA_ERROR("null pointer #ptr\n"); \
+ return -EINVAL; \
+ } \
+ } \
+ while (0)
+
+#define ECM_IPA_LOG_ENTRY() ECM_IPA_DEBUG("begin\n")
+#define ECM_IPA_LOG_EXIT() ECM_IPA_DEBUG("end\n")
+
+/**
+ * struct ecm_ipa_dev - main driver context parameters
+ * @ack_spinlock: protect last sent skb
+ * @last_out_skb: last sent skb saved until Tx notify is received from IPA
+ * @net: network interface struct implemented by this driver
+ * @folder: debugfs folder for various debuging switches
+ * @tx_enable: flag that enable/disable Tx path to continue to IPA
+ * @rx_enable: flag that enable/disable Rx path to continue to IPA
+ * @rm_enable: flag that enable/disable Resource manager request prior to Tx
+ * @dma_enable: flag that allow on-the-fly DMA mode for IPA
+ * @tx_file: saved debugfs entry to allow cleanup
+ * @rx_file: saved debugfs entry to allow cleanup
+ * @rm_file: saved debugfs entry to allow cleanup
+ * @dma_file: saved debugfs entry to allow cleanup
+ * @eth_ipv4_hdr_hdl: saved handle for ipv4 header-insertion table
+ * @eth_ipv6_hdr_hdl: saved handle for ipv6 header-insertion table
+ * @usb_to_ipa_hdl: save handle for IPA pipe operations
+ * @ipa_to_usb_hdl: save handle for IPA pipe operations
+ */
+struct ecm_ipa_dev {
+ spinlock_t ack_spinlock;
+ struct sk_buff *last_out_skb;
+ struct net_device *net;
+ bool tx_enable;
+ bool rx_enable;
+ bool rm_enable;
+ bool dma_enable;
+ struct dentry *folder;
+ struct dentry *tx_file;
+ struct dentry *rx_file;
+ struct dentry *rm_file;
+ struct dentry *dma_file;
+ uint32_t eth_ipv4_hdr_hdl;
+ uint32_t eth_ipv6_hdr_hdl;
+ u32 usb_to_ipa_hdl;
+ u32 ipa_to_usb_hdl;
+};
+
+/**
+ * struct ecm_ipa_ctx - saved pointer for the std ecm network device
+ * which allow ecm_ipa to be a singleton
+ */
+static struct ecm_ipa_dev *ecm_ipa_ctx;
+
+static int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl);
+static void sk_buff_print(struct sk_buff *skb);
+static int ecm_ipa_set_device_ethernet_addr(
+ u8 *dev_ethaddr, u8 device_ethaddr[]);
+static void ecm_ipa_packet_receive_notify(void *priv,
+ enum ipa_dp_evt_type evt,
+ unsigned long data);
+static void ecm_ipa_tx_complete_notify(void *priv,
+ enum ipa_dp_evt_type evt,
+ unsigned long data);
+static int ecm_ipa_ep_registers_dma_cfg(u32 usb_to_ipa_hdl);
+static int ecm_ipa_open(struct net_device *net);
+static int ecm_ipa_stop(struct net_device *net);
+static netdev_tx_t ecm_ipa_start_xmit(struct sk_buff *skb,
+ struct net_device *net);
+static void ecm_ipa_rm_notify(void *user_data, enum ipa_rm_event event,
+ unsigned long data);
+static int ecm_ipa_create_rm_resource(struct ecm_ipa_dev *dev);
+static void ecm_ipa_destory_rm_resource(void);
+static bool rx_filter(struct sk_buff *skb);
+static bool tx_filter(struct sk_buff *skb);
+static bool rm_enabled(struct ecm_ipa_dev *dev);
+
+static int ecm_ipa_rules_cfg(struct ecm_ipa_dev *dev,
+ const void *dst_mac, const void *src_mac);
+static int ecm_ipa_register_tx(struct ecm_ipa_dev *dev);
+static void ecm_ipa_deregister_tx(struct ecm_ipa_dev *dev);
+static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *dev);
+static void ecm_ipa_debugfs_destroy(struct ecm_ipa_dev *dev);
+static int ecm_ipa_debugfs_tx_open(struct inode *inode, struct file *file);
+static int ecm_ipa_debugfs_rx_open(struct inode *inode, struct file *file);
+static int ecm_ipa_debugfs_rm_open(struct inode *inode, struct file *file);
+static int ecm_ipa_debugfs_dma_open(struct inode *inode, struct file *file);
+static ssize_t ecm_ipa_debugfs_enable_read(struct file *file,
+ char __user *ubuf, size_t count, loff_t *ppos);
+static ssize_t ecm_ipa_debugfs_enable_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos);
+static ssize_t ecm_ipa_debugfs_enable_write_dma(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos);
+static void eth_get_drvinfo(struct net_device *net,
+ struct ethtool_drvinfo *drv_info);
+
+static const struct net_device_ops ecm_ipa_netdev_ops = {
+ .ndo_open = ecm_ipa_open,
+ .ndo_stop = ecm_ipa_stop,
+ .ndo_start_xmit = ecm_ipa_start_xmit,
+ .ndo_set_mac_address = eth_mac_addr,
+};
+static const struct ethtool_ops ops = {
+ .get_drvinfo = eth_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+};
+const struct file_operations ecm_ipa_debugfs_tx_ops = {
+ .open = ecm_ipa_debugfs_tx_open,
+ .read = ecm_ipa_debugfs_enable_read,
+ .write = ecm_ipa_debugfs_enable_write,
+};
+const struct file_operations ecm_ipa_debugfs_rx_ops = {
+ .open = ecm_ipa_debugfs_rx_open,
+ .read = ecm_ipa_debugfs_enable_read,
+ .write = ecm_ipa_debugfs_enable_write,
+};
+const struct file_operations ecm_ipa_debugfs_rm_ops = {
+ .open = ecm_ipa_debugfs_rm_open,
+ .read = ecm_ipa_debugfs_enable_read,
+ .write = ecm_ipa_debugfs_enable_write,
+};
+const struct file_operations ecm_ipa_debugfs_dma_ops = {
+ .open = ecm_ipa_debugfs_dma_open,
+ .read = ecm_ipa_debugfs_enable_read,
+ .write = ecm_ipa_debugfs_enable_write_dma,
+};
+
+/**
+ * ecm_ipa_init() - initializes internal data structures
+ * @ecm_ipa_rx_dp_notify: supplied callback to be called by the IPA
+ * driver upon data packets received from USB pipe into IPA core.
+ * @ecm_ipa_rt_dp_notify: supplied callback to be called by the IPA
+ * driver upon exception packets sent from IPA pipe into USB core.
+ * @priv: should be passed later on to ecm_ipa_configure, hold the network
+ * structure allocated for STD ECM interface.
+ *
+ * Shall be called prior to pipe connection.
+ * The out parameters (the callbacks) shall be supplied to ipa_connect.
+ * Detailed description:
+ * - set the callbacks to be used by the caller upon ipa_connect
+ * - allocate the network device
+ * - set the priv argument with a reference to the network device
+ *
+ * Returns negative errno, or zero on success
+ */
+int ecm_ipa_init(ecm_ipa_callback *ecm_ipa_rx_dp_notify,
+ ecm_ipa_callback *ecm_ipa_tx_dp_notify,
+ void **priv)
+{
+ int ret = 0;
+ struct net_device *net;
+ struct ecm_ipa_dev *dev;
+ ECM_IPA_LOG_ENTRY();
+ ECM_IPA_DEBUG("%s version %s\n", DRIVER_NAME, DRIVER_VERSION);
+ NULL_CHECK(ecm_ipa_rx_dp_notify);
+ NULL_CHECK(ecm_ipa_tx_dp_notify);
+ NULL_CHECK(priv);
+ net = alloc_etherdev(sizeof(struct ecm_ipa_dev));
+ if (!net) {
+ ret = -ENOMEM;
+ ECM_IPA_ERROR("fail to allocate etherdev\n");
+ goto fail_alloc_etherdev;
+ }
+ ECM_IPA_DEBUG("etherdev was successfully allocated\n");
+ dev = netdev_priv(net);
+ memset(dev, 0, sizeof(*dev));
+ dev->tx_enable = true;
+ dev->rx_enable = true;
+ spin_lock_init(&dev->ack_spinlock);
+ dev->net = net;
+ ecm_ipa_ctx = dev;
+ *priv = (void *)dev;
+ snprintf(net->name, sizeof(net->name), "%s%%d", "ecm");
+ net->netdev_ops = &ecm_ipa_netdev_ops;
+ ECM_IPA_DEBUG("internal data structures were intialized\n");
+ ret = ecm_ipa_debugfs_init(dev);
+ if (ret)
+ goto fail_debugfs;
+ ECM_IPA_DEBUG("debugfs entries were created\n");
+ *ecm_ipa_rx_dp_notify = ecm_ipa_packet_receive_notify;
+ *ecm_ipa_tx_dp_notify = ecm_ipa_tx_complete_notify;
+ ECM_IPA_LOG_EXIT();
+ return 0;
+fail_debugfs:
+ free_netdev(net);
+fail_alloc_etherdev:
+ return ret;
+}
+EXPORT_SYMBOL(ecm_ipa_init);
+
+/**
+ * ecm_ipa_rules_cfg() - set header insertion and register Tx/Rx properties
+ * Headers will be commited to HW
+ * @dev: main driver context parameters
+ * @dst_mac: destination MAC address
+ * @src_mac: source MAC address
+ *
+ * Returns negative errno, or zero on success
+ */
+static int ecm_ipa_rules_cfg(struct ecm_ipa_dev *dev,
+ const void *dst_mac, const void *src_mac)
+{
+ struct ipa_ioc_add_hdr *hdrs;
+ struct ipa_hdr_add *ipv4_hdr;
+ struct ipa_hdr_add *ipv6_hdr;
+ struct ethhdr *eth_ipv4;
+ struct ethhdr *eth_ipv6;
+ int result = 0;
+
+ ECM_IPA_LOG_ENTRY();
+ hdrs = kzalloc(sizeof(*hdrs) + sizeof(*ipv4_hdr) + sizeof(*ipv6_hdr),
+ GFP_KERNEL);
+ if (!hdrs) {
+ result = -ENOMEM;
+ goto out;
+ }
+ ipv4_hdr = &hdrs->hdr[0];
+ eth_ipv4 = (struct ethhdr *)ipv4_hdr->hdr;
+ ipv6_hdr = &hdrs->hdr[1];
+ eth_ipv6 = (struct ethhdr *)ipv6_hdr->hdr;
+ strlcpy(ipv4_hdr->name, ECM_IPA_IPV4_HDR_NAME, IPA_RESOURCE_NAME_MAX);
+ memcpy(eth_ipv4->h_dest, dst_mac, ETH_ALEN);
+ memcpy(eth_ipv4->h_source, src_mac, ETH_ALEN);
+ eth_ipv4->h_proto = ETH_P_IP;
+ ipv4_hdr->hdr_len = ETH_HLEN;
+ ipv4_hdr->is_partial = 0;
+ strlcpy(ipv6_hdr->name, ECM_IPA_IPV6_HDR_NAME, IPA_RESOURCE_NAME_MAX);
+ memcpy(eth_ipv6->h_dest, dst_mac, ETH_ALEN);
+ memcpy(eth_ipv6->h_source, src_mac, ETH_ALEN);
+ eth_ipv6->h_proto = ETH_P_IPV6;
+ ipv6_hdr->hdr_len = ETH_HLEN;
+ ipv6_hdr->is_partial = 0;
+ hdrs->commit = 1;
+ hdrs->num_hdrs = 2;
+ result = ipa_add_hdr(hdrs);
+ if (result) {
+ ECM_IPA_ERROR("Fail on Header-Insertion(%d)\n", result);
+ goto out_free_mem;
+ }
+ if (ipv4_hdr->status) {
+ ECM_IPA_ERROR("Fail on Header-Insertion ipv4(%d)\n",
+ ipv4_hdr->status);
+ result = ipv4_hdr->status;
+ goto out_free_mem;
+ }
+ if (ipv6_hdr->status) {
+ ECM_IPA_ERROR("Fail on Header-Insertion ipv6(%d)\n",
+ ipv6_hdr->status);
+ result = ipv6_hdr->status;
+ goto out_free_mem;
+ }
+ dev->eth_ipv4_hdr_hdl = ipv4_hdr->hdr_hdl;
+ dev->eth_ipv6_hdr_hdl = ipv6_hdr->hdr_hdl;
+ ECM_IPA_LOG_EXIT();
+out_free_mem:
+ kfree(hdrs);
+out:
+ return result;
+}
+
+static void ecm_ipa_rules_destroy(struct ecm_ipa_dev *dev)
+{
+ struct ipa_ioc_del_hdr *del_hdr;
+ struct ipa_hdr_del *ipv4;
+ struct ipa_hdr_del *ipv6;
+ int result;
+ del_hdr = kzalloc(sizeof(*del_hdr) + sizeof(*ipv4) +
+ sizeof(*ipv6), GFP_KERNEL);
+ if (!del_hdr)
+ return;
+ del_hdr->commit = 1;
+ del_hdr->num_hdls = 2;
+ ipv4 = &del_hdr->hdl[0];
+ ipv4->hdl = dev->eth_ipv4_hdr_hdl;
+ ipv6 = &del_hdr->hdl[1];
+ ipv6->hdl = dev->eth_ipv6_hdr_hdl;
+ result = ipa_del_hdr(del_hdr);
+ if (result || ipv4->status || ipv6->status)
+ ECM_IPA_ERROR("ipa_del_hdr failed");
+}
+
+static int ecm_ipa_register_tx(struct ecm_ipa_dev *dev)
+{
+ struct ipa_tx_intf tx_properties = {0};
+ struct ipa_ioc_tx_intf_prop properties[2] = { {0}, {0} };
+ struct ipa_ioc_tx_intf_prop *ipv4_property;
+ struct ipa_ioc_tx_intf_prop *ipv6_property;
+ int result = 0;
+ ECM_IPA_LOG_ENTRY();
+ tx_properties.prop = properties;
+ ipv4_property = &tx_properties.prop[0];
+ ipv4_property->ip = IPA_IP_v4;
+ ipv4_property->dst_pipe = IPA_TO_USB_CLIENT;
+ strlcpy(ipv4_property->hdr_name, ECM_IPA_IPV4_HDR_NAME,
+ IPA_RESOURCE_NAME_MAX);
+ ipv6_property = &tx_properties.prop[1];
+ ipv6_property->ip = IPA_IP_v6;
+ ipv6_property->dst_pipe = IPA_TO_USB_CLIENT;
+ strlcpy(ipv6_property->hdr_name, ECM_IPA_IPV6_HDR_NAME,
+ IPA_RESOURCE_NAME_MAX);
+ tx_properties.num_props = 2;
+ result = ipa_register_intf(dev->net->name, &tx_properties, NULL);
+ if (result)
+ ECM_IPA_ERROR("fail on Tx_prop registration\n");
+ ECM_IPA_LOG_EXIT();
+ return result;
+}
+
+static void ecm_ipa_deregister_tx(struct ecm_ipa_dev *dev)
+{
+ int result;
+ ECM_IPA_LOG_ENTRY();
+ result = ipa_deregister_intf(dev->net->name);
+ if (result)
+ ECM_IPA_DEBUG("Fail on Tx prop deregister\n");
+ ECM_IPA_LOG_EXIT();
+ return;
+}
+
+/**
+ * ecm_ipa_configure() - make IPA core end-point specific configuration
+ * @usb_to_ipa_hdl: handle of usb_to_ipa end-point for IPA driver
+ * @ipa_to_usb_hdl: handle of ipa_to_usb end-point for IPA driver
+ * @host_ethaddr: host Ethernet address in network order
+ * @device_ethaddr: device Ethernet address in network order
+ *
+ * Configure the usb_to_ipa and ipa_to_usb end-point registers
+ * - USB->IPA end-point: disable de-aggregation, enable link layer
+ * header removal (Ethernet removal), source NATing and default routing.
+ * - IPA->USB end-point: disable aggregation, add link layer header (Ethernet)
+ * - allocate Ethernet device
+ * - register to Linux network stack
+ *
+ * Returns negative errno, or zero on success
+ */
+int ecm_ipa_configure(u8 host_ethaddr[], u8 device_ethaddr[],
+ void *priv)
+{
+ struct ecm_ipa_dev *dev = priv;
+ struct net_device *net;
+ int result;
+ ECM_IPA_LOG_ENTRY();
+ NULL_CHECK(host_ethaddr);
+ NULL_CHECK(host_ethaddr);
+ NULL_CHECK(dev);
+ net = dev->net;
+ NULL_CHECK(net);
+ ECM_IPA_DEBUG("host_ethaddr=%pM device_ethaddr=%pM\n",
+ host_ethaddr, device_ethaddr);
+ result = ecm_ipa_create_rm_resource(dev);
+ if (result) {
+ ECM_IPA_ERROR("fail on RM create\n");
+ return -EINVAL;
+ }
+ ECM_IPA_DEBUG("RM resource was created\n");
+ netif_carrier_off(dev->net);
+ result = ecm_ipa_set_device_ethernet_addr(net->dev_addr,
+ device_ethaddr);
+ if (result) {
+ ECM_IPA_ERROR("set device MAC failed\n");
+ goto fail_set_device_ethernet;
+ }
+ result = ecm_ipa_rules_cfg(dev, host_ethaddr, device_ethaddr);
+ if (result) {
+ ECM_IPA_ERROR("fail on ipa rules set\n");
+ goto fail_set_device_ethernet;
+ }
+ ECM_IPA_DEBUG("Ethernet header insertion was set\n");
+ result = ecm_ipa_register_tx(dev);
+ if (result) {
+ ECM_IPA_ERROR("fail on properties set\n");
+ goto fail_register_tx;
+ }
+ ECM_IPA_DEBUG("ECM Tx properties were registered\n");
+ result = register_netdev(net);
+ if (result) {
+ ECM_IPA_ERROR("register_netdev failed: %d\n", result);
+ goto fail_register_netdev;
+ }
+ ECM_IPA_DEBUG("register_netdev succeeded\n");
+ ECM_IPA_LOG_EXIT();
+ return 0;
+fail_register_netdev:
+ ecm_ipa_deregister_tx(dev);
+fail_register_tx:
+fail_set_device_ethernet:
+ ecm_ipa_rules_destroy(dev);
+ ecm_ipa_destory_rm_resource();
+ free_netdev(net);
+ return result;
+}
+EXPORT_SYMBOL(ecm_ipa_configure);
+
+int ecm_ipa_connect(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl,
+ void *priv)
+{
+ struct ecm_ipa_dev *dev = priv;
+ ECM_IPA_LOG_ENTRY();
+ NULL_CHECK(priv);
+ ECM_IPA_DEBUG("usb_to_ipa_hdl = %d, ipa_to_usb_hdl = %d\n",
+ usb_to_ipa_hdl, ipa_to_usb_hdl);
+ if (!usb_to_ipa_hdl || usb_to_ipa_hdl >= IPA_CLIENT_MAX) {
+ ECM_IPA_ERROR("usb_to_ipa_hdl(%d) is not a valid ipa handle\n",
+ usb_to_ipa_hdl);
+ return -EINVAL;
+ }
+ if (!ipa_to_usb_hdl || ipa_to_usb_hdl >= IPA_CLIENT_MAX) {
+ ECM_IPA_ERROR("ipa_to_usb_hdl(%d) is not a valid ipa handle\n",
+ ipa_to_usb_hdl);
+ return -EINVAL;
+ }
+ dev->ipa_to_usb_hdl = ipa_to_usb_hdl;
+ dev->usb_to_ipa_hdl = usb_to_ipa_hdl;
+ ecm_ipa_ep_registers_cfg(usb_to_ipa_hdl, ipa_to_usb_hdl);
+ netif_carrier_on(dev->net);
+ if (!netif_carrier_ok(dev->net)) {
+ ECM_IPA_ERROR("netif_carrier_ok error\n");
+ return -EBUSY;
+ }
+ ECM_IPA_LOG_EXIT();
+ return 0;
+}
+EXPORT_SYMBOL(ecm_ipa_connect);
+
+int ecm_ipa_disconnect(void *priv)
+{
+ struct ecm_ipa_dev *dev = priv;
+ ECM_IPA_LOG_ENTRY();
+ NULL_CHECK(dev);
+ netif_carrier_off(dev->net);
+ ECM_IPA_LOG_EXIT();
+ return 0;
+}
+EXPORT_SYMBOL(ecm_ipa_disconnect);
+
+
+static void ecm_ipa_rm_notify(void *user_data, enum ipa_rm_event event,
+ unsigned long data)
+{
+ struct ecm_ipa_dev *dev = user_data;
+ ECM_IPA_LOG_ENTRY();
+ if (event == IPA_RM_RESOURCE_GRANTED &&
+ netif_queue_stopped(dev->net)) {
+ ECM_IPA_DEBUG("Resource Granted - waking queue\n");
+ netif_wake_queue(dev->net);
+ } else {
+ ECM_IPA_DEBUG("Resource released\n");
+ }
+ ECM_IPA_LOG_EXIT();
+}
+
+static int ecm_ipa_create_rm_resource(struct ecm_ipa_dev *dev)
+{
+ struct ipa_rm_create_params create_params = {0};
+ int result;
+ ECM_IPA_LOG_ENTRY();
+ create_params.name = IPA_RM_RESOURCE_STD_ECM_PROD;
+ create_params.reg_params.user_data = dev;
+ create_params.reg_params.notify_cb = ecm_ipa_rm_notify;
+ result = ipa_rm_create_resource(&create_params);
+ if (result) {
+ ECM_IPA_ERROR("Fail on ipa_rm_create_resource\n");
+ goto fail_rm_create;
+ }
+ ECM_IPA_DEBUG("rm client was created");
+
+ result = ipa_rm_inactivity_timer_init(IPA_RM_RESOURCE_STD_ECM_PROD,
+ INACTIVITY_MSEC_DELAY);
+ if (result) {
+ ECM_IPA_ERROR("Fail on ipa_rm_inactivity_timer_init\n");
+ goto fail_it;
+ }
+ ECM_IPA_DEBUG("rm_it client was created");
+ ECM_IPA_LOG_EXIT();
+ return 0;
+fail_it:
+fail_rm_create:
+ return result;
+}
+
+static void ecm_ipa_destory_rm_resource(void)
+{
+ ECM_IPA_LOG_ENTRY();
+ ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_STD_ECM_PROD);
+ ECM_IPA_LOG_EXIT();
+}
+
+static bool rx_filter(struct sk_buff *skb)
+{
+ struct ecm_ipa_dev *dev = netdev_priv(skb->dev);
+ return !dev->rx_enable;
+}
+
+static bool tx_filter(struct sk_buff *skb)
+{
+ struct ecm_ipa_dev *dev = netdev_priv(skb->dev);
+ return !dev->tx_enable;
+}
+
+static bool rm_enabled(struct ecm_ipa_dev *dev)
+{
+ return dev->rm_enable;
+}
+
+static int ecm_ipa_open(struct net_device *net)
+{
+ ECM_IPA_LOG_ENTRY();
+ netif_start_queue(net);
+ ECM_IPA_LOG_EXIT();
+ return 0;
+}
+
+static int ecm_ipa_stop(struct net_device *net)
+{
+ ECM_IPA_LOG_ENTRY();
+ ECM_IPA_DEBUG("stopping net device\n");
+ netif_stop_queue(net);
+ ECM_IPA_LOG_EXIT();
+ return 0;
+}
+
+/**
+ * ecm_ipa_cleanup() - destroys all
+ * ecm information
+ * @priv: main driver context parameters
+ *
+ */
+void ecm_ipa_cleanup(void *priv)
+{
+ struct ecm_ipa_dev *dev = priv;
+ ECM_IPA_LOG_ENTRY();
+ if (!dev) {
+ ECM_IPA_ERROR("dev NULL pointer\n");
+ return;
+ }
+ if (rm_enabled(dev)) {
+ ecm_ipa_destory_rm_resource();
+ ecm_ipa_debugfs_destroy(dev);
+ }
+ if (!dev->net) {
+ unregister_netdev(dev->net);
+ free_netdev(dev->net);
+ }
+ ECM_IPA_DEBUG("cleanup done\n");
+ ecm_ipa_ctx = NULL;
+ ECM_IPA_LOG_EXIT();
+ return ;
+}
+EXPORT_SYMBOL(ecm_ipa_cleanup);
+
+static int resource_request(struct ecm_ipa_dev *dev)
+{
+ int result = 0;
+ ECM_IPA_LOG_ENTRY();
+ if (!rm_enabled(dev))
+ goto out;
+ result = ipa_rm_inactivity_timer_request_resource(
+ IPA_RM_RESOURCE_STD_ECM_PROD);
+out:
+ ECM_IPA_LOG_EXIT();
+ return result;
+}
+
+static void resource_release(struct ecm_ipa_dev *dev)
+{
+ ECM_IPA_LOG_ENTRY();
+ if (!rm_enabled(dev))
+ goto out;
+ ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_STD_ECM_PROD);
+out:
+ ECM_IPA_LOG_EXIT();
+}
+
+/**
+ * ecm_ipa_start_xmit() - send data from APPs to USB core via IPA core
+ * @skb: packet received from Linux stack
+ * @net: the network device being used to send this packet
+ *
+ * Several conditions needed in order to send the packet to IPA:
+ * - we are in a valid state were the queue is not stopped
+ * - Filter Tx switch is turned off
+ * - The resources required for actual Tx are all up
+ *
+ */
+static netdev_tx_t ecm_ipa_start_xmit(struct sk_buff *skb,
+ struct net_device *net)
+{
+ int ret;
+ netdev_tx_t status = NETDEV_TX_BUSY;
+ struct ecm_ipa_dev *dev = netdev_priv(net);
+ unsigned long flags;
+ ECM_IPA_LOG_ENTRY();
+ if (unlikely(netif_queue_stopped(net))) {
+ ECM_IPA_ERROR("interface queue is stopped\n");
+ goto out;
+ }
+ ECM_IPA_DEBUG("send (proto=0x%04x)\n", ntohs(skb->protocol));
+ if (unlikely(tx_filter(skb))) {
+ dev_kfree_skb_any(skb);
+ ECM_IPA_ERROR("packet got filtered out on Tx path\n");
+ status = NETDEV_TX_OK;
+ goto out;
+ }
+ ret = resource_request(dev);
+ if (ret) {
+ ECM_IPA_DEBUG("Waiting to resource\n");
+ netif_stop_queue(net);
+ goto resource_busy;
+ }
+ ECM_IPA_DEBUG("taking ack_lock\n");
+ spin_lock_irqsave(&dev->ack_spinlock, flags);
+ ECM_IPA_DEBUG("ack_lock taken\n");
+ if (dev->last_out_skb) {
+ ECM_IPA_DEBUG("No Tx-ack received for previous packet\n");
+ ECM_IPA_DEBUG("releasing ack_lock\n");
+ spin_unlock_irqrestore(&dev->ack_spinlock, flags);
+ ECM_IPA_DEBUG("ack_lock released\n");
+ netif_stop_queue(net);
+ status = -NETDEV_TX_BUSY;
+ goto out;
+ } else {
+ dev->last_out_skb = skb;
+ }
+ ECM_IPA_DEBUG("releasing ack_lock\n");
+ spin_unlock_irqrestore(&dev->ack_spinlock, flags);
+ ECM_IPA_DEBUG("ack_lock released\n");
+ sk_buff_print(skb);
+ ECM_IPA_DEBUG("ipa_tx_dp is called (dst_client=%d)\n",
+ IPA_TO_USB_CLIENT);
+ ret = ipa_tx_dp(IPA_TO_USB_CLIENT, skb, NULL);
+ if (ret) {
+ ECM_IPA_ERROR("ipa transmit failed (%d)\n", ret);
+ goto fail_tx_packet;
+ }
+ net->stats.tx_packets++;
+ net->stats.tx_bytes += skb->len;
+ ECM_IPA_LOG_EXIT();
+ status = NETDEV_TX_OK;
+ goto out;
+fail_tx_packet:
+out:
+ resource_release(dev);
+resource_busy:
+ ECM_IPA_LOG_EXIT();
+ return status;
+}
+
+/**
+ * ecm_ipa_packet_receive_notify() - Rx notify
+ *
+ * @priv: ecm driver context
+ * @evt: event type
+ * @data: data provided with event
+ *
+ * IPA will pass a packet with skb->data pointing to Ethernet packet frame
+ */
+void ecm_ipa_packet_receive_notify(void *priv,
+ enum ipa_dp_evt_type evt,
+ unsigned long data)
+{
+ struct sk_buff *skb = (struct sk_buff *)data;
+ struct ecm_ipa_dev *dev = priv;
+ int result;
+ ECM_IPA_LOG_ENTRY();
+ if (evt != IPA_RECEIVE) {
+ ECM_IPA_ERROR("A none IPA_RECEIVE event in ecm_ipa_receive\n");
+ return;
+ }
+ ECM_IPA_DEBUG("receive\n");
+ sk_buff_print(skb);
+ skb->dev = dev->net;
+ skb->protocol = eth_type_trans(skb, dev->net);
+ if (rx_filter(skb)) {
+ ECM_IPA_ERROR("packet got filtered out on Rx path\n");
+ dev_kfree_skb_any(skb);
+ return;
+ }
+ ECM_IPA_DEBUG("kernel stack Rx is called\n");
+ result = netif_rx(skb);
+ if (result)
+ ECM_IPA_ERROR("fail on netif_rx\n");
+ dev->net->stats.rx_packets++;
+ dev->net->stats.rx_bytes += skb->len;
+ ECM_IPA_LOG_EXIT();
+ return;
+}
+
+/**
+ * ecm_ipa_tx_complete_notify() - Rx notify
+ *
+ * @priv: ecm driver context
+ * @evt: event type
+ * @data: data provided with event
+ *
+ * Check that the packet is the one we sent and release it
+ * This function will be called in defered context in IPA wq.
+ */
+void ecm_ipa_tx_complete_notify(void *priv,
+ enum ipa_dp_evt_type evt,
+ unsigned long data)
+{
+ struct sk_buff *skb = (struct sk_buff *)data;
+ struct ecm_ipa_dev *dev = priv;
+ unsigned long flags;
+ ECM_IPA_LOG_ENTRY();
+
+ if (!dev) {
+ ECM_IPA_ERROR("dev is NULL pointer\n");
+ return;
+ }
+ if (evt != IPA_WRITE_DONE) {
+ ECM_IPA_ERROR("unsupported event on Tx callback\n");
+ return;
+ }
+ ECM_IPA_DEBUG("taking ack_lock\n");
+ spin_lock_irqsave(&dev->ack_spinlock, flags);
+ ECM_IPA_DEBUG("ack_lock taken\n");
+ if (skb != dev->last_out_skb)
+ ECM_IPA_ERROR("ACKed/Sent not the same(FIFO expected)\n");
+ dev->last_out_skb = NULL;
+ ECM_IPA_DEBUG("releasing ack_lock\n");
+ spin_unlock_irqrestore(&dev->ack_spinlock, flags);
+ ECM_IPA_DEBUG("ack_lock released\n");
+ if (netif_queue_stopped(dev->net)) {
+ ECM_IPA_DEBUG("waking up queue\n");
+ netif_wake_queue(dev->net);
+ }
+ dev_kfree_skb_any(skb);
+ ECM_IPA_LOG_EXIT();
+ return;
+}
+
+static int ecm_ipa_debugfs_tx_open(struct inode *inode, struct file *file)
+{
+ struct ecm_ipa_dev *dev = inode->i_private;
+ ECM_IPA_LOG_ENTRY();
+ file->private_data = &(dev->tx_enable);
+ ECM_IPA_LOG_ENTRY();
+ return 0;
+}
+
+static int ecm_ipa_debugfs_rx_open(struct inode *inode, struct file *file)
+{
+ struct ecm_ipa_dev *dev = inode->i_private;
+ ECM_IPA_LOG_ENTRY();
+ file->private_data = &(dev->rx_enable);
+ ECM_IPA_LOG_EXIT();
+ return 0;
+}
+
+static int ecm_ipa_debugfs_rm_open(struct inode *inode, struct file *file)
+{
+ struct ecm_ipa_dev *dev = inode->i_private;
+ ECM_IPA_LOG_ENTRY();
+ file->private_data = &(dev->rm_enable);
+ ECM_IPA_LOG_EXIT();
+ return 0;
+}
+
+static ssize_t ecm_ipa_debugfs_enable_write_dma(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct ecm_ipa_dev *dev = file->private_data;
+ int result;
+ ECM_IPA_LOG_ENTRY();
+ file->private_data = &dev->dma_enable;
+ result = ecm_ipa_debugfs_enable_write(file, buf, count, ppos);
+ if (dev->dma_enable)
+ ecm_ipa_ep_registers_dma_cfg(dev->usb_to_ipa_hdl);
+ else
+ ecm_ipa_ep_registers_cfg(dev->usb_to_ipa_hdl,
+ dev->usb_to_ipa_hdl);
+ ECM_IPA_LOG_EXIT();
+ return result;
+}
+
+static int ecm_ipa_debugfs_dma_open(struct inode *inode, struct file *file)
+{
+ struct ecm_ipa_dev *dev = inode->i_private;
+ ECM_IPA_LOG_ENTRY();
+ file->private_data = dev;
+ ECM_IPA_LOG_EXIT();
+ return 0;
+}
+
+static ssize_t ecm_ipa_debugfs_enable_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ unsigned long missing;
+ char input;
+ bool *enable = file->private_data;
+ if (count != sizeof(input) + 1) {
+ ECM_IPA_ERROR("wrong input length(%zd)\n", count);
+ return -EINVAL;
+ }
+ if (!buf) {
+ ECM_IPA_ERROR("Bad argument\n");
+ return -EINVAL;
+ }
+ missing = copy_from_user(&input, buf, 1);
+ if (missing)
+ return -EFAULT;
+ ECM_IPA_DEBUG("input received %c\n", input);
+ *enable = input - '0';
+ ECM_IPA_DEBUG("value was set to %d\n", *enable);
+ return count;
+}
+
+static ssize_t ecm_ipa_debugfs_enable_read(struct file *file,
+ char __user *ubuf, size_t count, loff_t *ppos)
+{
+ int nbytes;
+ int size = 0;
+ int ret;
+ loff_t pos;
+ u8 enable_str[sizeof(char)*3] = {0};
+ bool *enable = file->private_data;
+ pos = *ppos;
+ nbytes = scnprintf(enable_str, sizeof(enable_str), "%d\n", *enable);
+ ret = simple_read_from_buffer(ubuf, count, ppos, enable_str, nbytes);
+ if (ret < 0) {
+ ECM_IPA_ERROR("simple_read_from_buffer problem");
+ return ret;
+ }
+ size += ret;
+ count -= nbytes;
+ *ppos = pos + size;
+ return size;
+}
+
+static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *dev)
+{
+ const mode_t flags = S_IRUSR | S_IRGRP | S_IROTH |
+ S_IWUSR | S_IWGRP | S_IWOTH;
+ int ret = -EINVAL;
+ ECM_IPA_LOG_ENTRY();
+ if (!dev)
+ return -EINVAL;
+ dev->folder = debugfs_create_dir("ecm_ipa", NULL);
+ if (!dev->folder) {
+ ECM_IPA_ERROR("could not create debugfs folder entry\n");
+ ret = -EFAULT;
+ goto fail_folder;
+ }
+ dev->tx_file = debugfs_create_file("tx_enable", flags, dev->folder, dev,
+ &ecm_ipa_debugfs_tx_ops);
+ if (!dev->tx_file) {
+ ECM_IPA_ERROR("could not create debugfs tx file\n");
+ ret = -EFAULT;
+ goto fail_file;
+ }
+ dev->rx_file = debugfs_create_file("rx_enable", flags, dev->folder, dev,
+ &ecm_ipa_debugfs_rx_ops);
+ if (!dev->rx_file) {
+ ECM_IPA_ERROR("could not create debugfs rx file\n");
+ ret = -EFAULT;
+ goto fail_file;
+ }
+ dev->rm_file = debugfs_create_file("rm_enable", flags, dev->folder, dev,
+ &ecm_ipa_debugfs_rm_ops);
+ if (!dev->rm_file) {
+ ECM_IPA_ERROR("could not create debugfs rm file\n");
+ ret = -EFAULT;
+ goto fail_file;
+ }
+ dev->dma_file = debugfs_create_file("dma_enable", flags, dev->folder,
+ dev, &ecm_ipa_debugfs_dma_ops);
+ if (!dev->dma_file) {
+ ECM_IPA_ERROR("could not create debugfs dma file\n");
+ ret = -EFAULT;
+ goto fail_file;
+ }
+ ECM_IPA_LOG_EXIT();
+ return 0;
+fail_file:
+ debugfs_remove_recursive(dev->folder);
+fail_folder:
+ return ret;
+}
+
+static void ecm_ipa_debugfs_destroy(struct ecm_ipa_dev *dev)
+{
+ debugfs_remove_recursive(dev->folder);
+}
+
+static void eth_get_drvinfo(struct net_device *net,
+ struct ethtool_drvinfo *drv_info)
+{
+ ECM_IPA_LOG_ENTRY();
+ strlcpy(drv_info->driver, DRIVER_NAME, sizeof(drv_info->driver));
+ strlcpy(drv_info->version, DRIVER_VERSION, sizeof(drv_info->version));
+ ECM_IPA_LOG_EXIT();
+}
+
+
+/**
+ * ecm_ipa_ep_cfg() - configure the USB endpoints for ECM
+ *
+ *usb_to_ipa_hdl: handle received from ipa_connect
+ *ipa_to_usb_hdl: handle received from ipa_connect
+ *
+ * USB to IPA pipe:
+ * - No de-aggregation
+ * - Remove Ethernet header
+ * - SRC NAT
+ * - Default routing(0)
+ * IPA to USB Pipe:
+ * - No aggregation
+ * - Add Ethernet header
+ */
+int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl)
+{
+ int result = 0;
+ struct ipa_ep_cfg usb_to_ipa_ep_cfg;
+ struct ipa_ep_cfg ipa_to_usb_ep_cfg;
+ ECM_IPA_LOG_ENTRY();
+ memset(&usb_to_ipa_ep_cfg, 0 , sizeof(struct ipa_ep_cfg));
+ usb_to_ipa_ep_cfg.aggr.aggr_en = IPA_BYPASS_AGGR;
+ usb_to_ipa_ep_cfg.hdr.hdr_len = ETH_HLEN;
+ usb_to_ipa_ep_cfg.nat.nat_en = IPA_SRC_NAT;
+ usb_to_ipa_ep_cfg.route.rt_tbl_hdl = 0;
+ usb_to_ipa_ep_cfg.mode.dst = IPA_CLIENT_A5_LAN_WAN_CONS;
+ usb_to_ipa_ep_cfg.mode.mode = IPA_BASIC;
+ result = ipa_cfg_ep(usb_to_ipa_hdl, &usb_to_ipa_ep_cfg);
+ if (result) {
+ ECM_IPA_ERROR("failed to configure USB to IPA point\n");
+ goto out;
+ }
+ memset(&ipa_to_usb_ep_cfg, 0 , sizeof(struct ipa_ep_cfg));
+ ipa_to_usb_ep_cfg.aggr.aggr_en = IPA_BYPASS_AGGR;
+ ipa_to_usb_ep_cfg.hdr.hdr_len = ETH_HLEN;
+ ipa_to_usb_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
+ result = ipa_cfg_ep(ipa_to_usb_hdl, &ipa_to_usb_ep_cfg);
+ if (result) {
+ ECM_IPA_ERROR("failed to configure IPA to USB end-point\n");
+ goto out;
+ }
+ ECM_IPA_DEBUG("end-point registers successfully configured\n");
+out:
+ ECM_IPA_LOG_EXIT();
+ return result;
+}
+
+/**
+ * ecm_ipa_ep_registers_dma_cfg() - configure the USB endpoints for ECM
+ * DMA
+ * @usb_to_ipa_hdl: handle received from ipa_connect
+ *
+ * This function will override the previous configuration
+ * which is needed for cores that does not support blocks logic
+ * Note that client handles are the actual pipe index
+ */
+int ecm_ipa_ep_registers_dma_cfg(u32 usb_to_ipa_hdl)
+{
+ int result = 0;
+ struct ipa_ep_cfg_mode cfg_mode;
+ u32 apps_to_ipa_hdl = 2;
+ ECM_IPA_LOG_ENTRY();
+ /* Apps to IPA - override the configuration made by IPA driver
+ * in order to allow data path on older platforms*/
+ memset(&cfg_mode, 0 , sizeof(cfg_mode));
+ cfg_mode.mode = IPA_DMA;
+ cfg_mode.dst = IPA_CLIENT_USB_CONS;
+ result = ipa_cfg_ep_mode(apps_to_ipa_hdl, &cfg_mode);
+ if (result) {
+ ECM_IPA_ERROR("failed to configure Apps to IPA\n");
+ goto out;
+ }
+ memset(&cfg_mode, 0 , sizeof(cfg_mode));
+ cfg_mode.mode = IPA_DMA;
+ cfg_mode.dst = IPA_CLIENT_A5_LAN_WAN_CONS;
+ result = ipa_cfg_ep_mode(usb_to_ipa_hdl, &cfg_mode);
+ if (result) {
+ ECM_IPA_ERROR("failed to configure USB to IPA\n");
+ goto out;
+ }
+ ECM_IPA_DEBUG("end-point registers successfully configured\n");
+out:
+ ECM_IPA_LOG_EXIT();
+ return result;
+}
+
+static void ecm_ipa_dump_buff(u8 *buff, u32 byte_size)
+{
+ int i;
+ ECM_IPA_DEBUG("ofst(hex), addr(hex), data(hex), value(char):\n");
+ for (i = 0 ; i < byte_size; i += 4) {
+ ECM_IPA_DEBUG("%2x %p %02x %02x %02x %02x | %c %c %c %c\n",
+ i, &buff[i],
+ buff[i], buff[i+1], buff[i+2], buff[i+3],
+ buff[i], buff[i+1], buff[i+2], buff[i+3]);
+ }
+}
+
+/**
+ * sk_buff_print() - detailed sk_buff printouts
+ * @skb: the socket buff
+ */
+void sk_buff_print(struct sk_buff *skb)
+{
+ ECM_IPA_DEBUG("called by: %s\n", current->comm);
+ ECM_IPA_DEBUG("skb->next=0x%p, skb->prev=0x%p, skb->sk=0x%p\n",
+ skb->next, skb->prev, skb->sk);
+ ECM_IPA_DEBUG("skb->len=0x%x, skb->data_len=0x%x protocol=0x%04x\n",
+ skb->len, skb->data_len, skb->protocol);
+ ECM_IPA_DEBUG("skb->mac_len=0x%x, skb->hdr_len=0x%x, skb->csum=%x\n",
+ skb->mac_len, skb->hdr_len, skb->csum);
+
+ ECM_IPA_DEBUG("mac_header = 0x%p\n", skb_mac_header(skb));
+ ECM_IPA_DEBUG("network_header = 0x%p\n", skb_network_header(skb));
+ ECM_IPA_DEBUG("transport_header=0x%p\n", skb_transport_header(skb));
+
+ ECM_IPA_DEBUG("skb->head=0x%p\n", skb->head);
+ ECM_IPA_DEBUG("skb->data=0x%p\n", skb->data);
+ ECM_IPA_DEBUG("tail=0x%p\n", skb_tail_pointer(skb));
+ ECM_IPA_DEBUG("end =0x%p\n", skb_end_pointer(skb));
+ ECM_IPA_DEBUG("skb->truesize=0x%x (buffer size)\n",
+ skb->truesize);
+ ecm_ipa_dump_buff(skb->data, skb->len);
+}
+
+/**
+ * ecm_ipa_set_device_ethernet_addr() - set device etherenet address
+ * @dev_ethaddr: device etherenet address
+ *
+ * Returns 0 for success, negative otherwise
+ */
+int ecm_ipa_set_device_ethernet_addr(u8 *dev_ethaddr, u8 device_ethaddr[])
+{
+ if (!is_valid_ether_addr(device_ethaddr))
+ return -EINVAL;
+ memcpy(dev_ethaddr, device_ethaddr, ETH_ALEN);
+ ECM_IPA_DEBUG("device ethernet address: %pM\n", dev_ethaddr);
+ return 0;
+}
+
+/**
+ * ecm_ipa_init_module() - module initialization
+ *
+ */
+static int ecm_ipa_init_module(void)
+{
+ ECM_IPA_LOG_ENTRY();
+ ECM_IPA_LOG_EXIT();
+ return 0;
+}
+
+/**
+ * ecm_ipa_cleanup_module() - module cleanup
+ *
+ */
+static void ecm_ipa_cleanup_module(void)
+{
+ ECM_IPA_LOG_ENTRY();
+ ECM_IPA_LOG_EXIT();
+ return;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ECM IPA network interface");
+
+late_initcall(ecm_ipa_init_module);
+module_exit(ecm_ipa_cleanup_module);
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
index f4dff66..76e3175 100644
--- a/drivers/platform/msm/Kconfig
+++ b/drivers/platform/msm/Kconfig
@@ -101,4 +101,14 @@
This driver gets the Q6 out of power collapsed state and
exposes ioctl control to read avtimer tick.
+config SSM
+ tristate "Qualcomm Secure Service Module"
+ depends on QSEECOM
+ depends on MSM_SMD
+ help
+ Provides an interface for OEM driver to communicate with Trustzone
+ and modem for key exchange and mode change.
+ This driver uses Secure Channel Manager interface for trustzone
+ communication and communicates with modem over SMD channel.
+
endmenu
diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile
index a679fb9..289ece9 100644
--- a/drivers/platform/msm/Makefile
+++ b/drivers/platform/msm/Makefile
@@ -10,3 +10,4 @@
obj-$(CONFIG_QPNP_VIBRATOR) += qpnp-vibrator.o
obj-$(CONFIG_QPNP_CLKDIV) += qpnp-clkdiv.o
obj-$(CONFIG_MSM_AVTIMER) += avtimer.o
+obj-$(CONFIG_SSM) += ssm.o
diff --git a/drivers/platform/msm/ipa/Makefile b/drivers/platform/msm/ipa/Makefile
index c541eb7..a25c799 100644
--- a/drivers/platform/msm/ipa/Makefile
+++ b/drivers/platform/msm/ipa/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_IPA) += ipat.o
ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \
- ipa_utils.o ipa_nat.o rmnet_bridge.o a2_service.o ipa_bridge.o ipa_intf.o
+ ipa_utils.o ipa_nat.o rmnet_bridge.o a2_service.o ipa_bridge.o ipa_intf.o \
+ ipa_rm.o ipa_rm_dependency_graph.o ipa_rm_peers_list.o ipa_rm_resource.o ipa_rm_inactivity_timer.o
diff --git a/drivers/platform/msm/ipa/ipa.c b/drivers/platform/msm/ipa/ipa.c
index 7690b21..ccf5f96 100644
--- a/drivers/platform/msm/ipa/ipa.c
+++ b/drivers/platform/msm/ipa/ipa.c
@@ -26,6 +26,7 @@
#include <mach/msm_bus.h>
#include <mach/msm_bus_board.h>
#include "ipa_i.h"
+#include "ipa_rm_i.h"
#define IPA_SUMMING_THRESHOLD (0x10)
#define IPA_PIPE_MEM_START_OFST (0x0)
@@ -1528,6 +1529,7 @@
* - Create empty routing table in system memory(no committing)
* - Initialize pipes memory pool with ipa_pipe_mem_init for supported platforms
* - Create a char-device for IPA
+* - Initialize IPA RM (resource manager)
*/
static int ipa_init(const struct ipa_plat_drv_res *resource_p)
{
@@ -1870,10 +1872,20 @@
if (ipa_ctx->ipa_hw_mode == IPA_HW_MODE_NORMAL)
ipa_disable_clks();
+ /* Initialize IPA RM (resource manager) */
+ result = ipa_rm_initialize();
+ if (result) {
+ IPAERR(":cdev_add err=%d\n", -result);
+ result = -ENODEV;
+ goto fail_ipa_rm_init;
+ }
+
IPADBG(":IPA driver init OK.\n");
return 0;
+fail_ipa_rm_init:
+ cdev_del(&ipa_ctx->cdev);
fail_cdev_add:
device_destroy(ipa_ctx->class, ipa_ctx->dev_num);
fail_device_create:
diff --git a/drivers/platform/msm/ipa/ipa_rm.c b/drivers/platform/msm/ipa/ipa_rm.c
new file mode 100644
index 0000000..99b19cc
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_rm.c
@@ -0,0 +1,374 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <mach/ipa.h>
+#include "ipa_i.h"
+#include "ipa_rm_dependency_graph.h"
+#include "ipa_rm_i.h"
+#include "ipa_rm_resource.h"
+
+struct ipa_rm_context_type {
+ struct ipa_rm_dep_graph *dep_graph;
+ struct workqueue_struct *ipa_rm_wq;
+};
+static struct ipa_rm_context_type *ipa_rm_ctx;
+
+/**
+ * ipa_rm_create_resource() - create resource
+ * @create_params: [in] parameters needed
+ * for resource initialization
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * This function is called by IPA RM client to initialize client's resources.
+ * This API should be called before any other IPA RM API
+ * on given resource name.
+ *
+ */
+int ipa_rm_create_resource(struct ipa_rm_create_params *create_params)
+{
+ struct ipa_rm_resource *resource;
+ int result;
+
+ if (!create_params) {
+ result = -EINVAL;
+ goto bail;
+ }
+ if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
+ create_params->name,
+ &resource) == 0) {
+ result = -EPERM;
+ goto bail;
+ }
+ result = ipa_rm_resource_create(create_params,
+ &resource);
+ if (result)
+ goto bail;
+ result = ipa_rm_dep_graph_add(ipa_rm_ctx->dep_graph, resource);
+ if (result)
+ ipa_rm_resource_delete(resource);
+bail:
+ return result;
+}
+EXPORT_SYMBOL(ipa_rm_create_resource);
+
+/**
+ * ipa_rm_add_dependency() - create dependency
+ * between 2 resources
+ * @resource_name: name of dependent resource
+ * @depends_on_name: name of its dependency
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * Side effects: IPA_RM_RESORCE_GRANTED could be generated
+ * in case client registered with IPA RM
+ */
+int ipa_rm_add_dependency(enum ipa_rm_resource_name resource_name,
+ enum ipa_rm_resource_name depends_on_name)
+{
+ return ipa_rm_dep_graph_add_dependency(
+ ipa_rm_ctx->dep_graph,
+ resource_name,
+ depends_on_name);
+}
+EXPORT_SYMBOL(ipa_rm_add_dependency);
+
+/**
+ * ipa_rm_delete_dependency() - create dependency
+ * between 2 resources
+ * @resource_name: name of dependent resource
+ * @depends_on_name: name of its dependency
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * Side effects: IPA_RM_RESORCE_GRANTED could be generated
+ * in case client registered with IPA RM
+ */
+int ipa_rm_delete_dependency(enum ipa_rm_resource_name resource_name,
+ enum ipa_rm_resource_name depends_on_name)
+{
+ return ipa_rm_dep_graph_delete_dependency(
+ ipa_rm_ctx->dep_graph,
+ resource_name,
+ depends_on_name);
+}
+EXPORT_SYMBOL(ipa_rm_delete_dependency);
+
+/**
+ * ipa_rm_request_resource() - request resource
+ * @resource_name: [in] name of the requested resource
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * All registered callbacks are called with IPA_RM_RESOURCE_GRANTED
+ * on successful completion of this operation.
+ */
+int ipa_rm_request_resource(enum ipa_rm_resource_name resource_name)
+{
+ struct ipa_rm_resource *resource;
+ int result;
+ IPADBG("IPA RM ::ipa_rm_request_resource ENTER\n");
+
+ if (!IPA_RM_RESORCE_IS_PROD(resource_name)) {
+ result = -EINVAL;
+ goto bail;
+ }
+ if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
+ resource_name,
+ &resource) != 0) {
+ result = -EPERM;
+ goto bail;
+ }
+ result = ipa_rm_resource_producer_request(
+ (struct ipa_rm_resource_prod *)resource);
+
+bail:
+ IPADBG("IPA RM ::ipa_rm_request_resource EXIT [%d]\n", result);
+
+ return result;
+}
+EXPORT_SYMBOL(ipa_rm_request_resource);
+
+/**
+ * ipa_rm_release_resource() - release resource
+ * @resource_name: [in] name of the requested resource
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * All registered callbacks are called with IPA_RM_RESOURCE_RELEASED
+ * on successful completion of this operation.
+ */
+int ipa_rm_release_resource(enum ipa_rm_resource_name resource_name)
+{
+ struct ipa_rm_resource *resource;
+ int result;
+ IPADBG("IPA RM ::ipa_rm_release_resource ENTER\n");
+
+ if (!IPA_RM_RESORCE_IS_PROD(resource_name)) {
+ result = -EINVAL;
+ goto bail;
+ }
+ if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
+ resource_name,
+ &resource) != 0) {
+ result = -EPERM;
+ goto bail;
+ }
+ result = ipa_rm_resource_producer_release(
+ (struct ipa_rm_resource_prod *)resource);
+
+bail:
+ IPADBG("IPA RM ::ipa_rm_release_resource EXIT [%d]\n", result);
+ return result;
+}
+EXPORT_SYMBOL(ipa_rm_release_resource);
+
+/**
+ * ipa_rm_register() - register for event
+ * @resource_name: resource name
+ * @reg_params: [in] registration parameters
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * Registration parameters provided here should be the same
+ * as provided later in ipa_rm_deregister() call.
+ */
+int ipa_rm_register(enum ipa_rm_resource_name resource_name,
+ struct ipa_rm_register_params *reg_params)
+{
+ int result;
+ struct ipa_rm_resource *resource;
+ if (!IPA_RM_RESORCE_IS_PROD(resource_name)) {
+ result = -EINVAL;
+ goto bail;
+ }
+ if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
+ resource_name,
+ &resource) != 0) {
+ result = -EPERM;
+ goto bail;
+ }
+ result = ipa_rm_resource_producer_register(
+ (struct ipa_rm_resource_prod *)resource,
+ reg_params);
+bail:
+ return result;
+}
+EXPORT_SYMBOL(ipa_rm_register);
+
+/**
+ * ipa_rm_deregister() - cancel the registration
+ * @resource_name: resource name
+ * @reg_params: [in] registration parameters
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * Registration parameters provided here should be the same
+ * as provided in ipa_rm_register() call.
+ */
+int ipa_rm_deregister(enum ipa_rm_resource_name resource_name,
+ struct ipa_rm_register_params *reg_params)
+{
+ int result;
+ struct ipa_rm_resource *resource;
+ if (!IPA_RM_RESORCE_IS_PROD(resource_name)) {
+ result = -EINVAL;
+ goto bail;
+ }
+ if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
+ resource_name,
+ &resource) != 0) {
+ result = -EPERM;
+ goto bail;
+ }
+ result = ipa_rm_resource_producer_deregister(
+ (struct ipa_rm_resource_prod *)resource,
+ reg_params);
+bail:
+ return result;
+}
+EXPORT_SYMBOL(ipa_rm_deregister);
+
+/**
+ * ipa_rm_notify_completion() -
+ * consumer driver notification for
+ * request_resource / release_resource operations
+ * completion
+ * @event: notified event
+ * @resource_name: resource name
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_rm_notify_completion(enum ipa_rm_event event,
+ enum ipa_rm_resource_name resource_name)
+{
+ int result;
+ if (!IPA_RM_RESORCE_IS_CONS(resource_name)) {
+ result = -EINVAL;
+ goto bail;
+ }
+ ipa_rm_wq_send_cmd(IPA_RM_WQ_RESOURCE_CB,
+ resource_name,
+ event);
+ result = 0;
+bail:
+ return result;
+}
+EXPORT_SYMBOL(ipa_rm_notify_completion);
+
+static void ipa_rm_wq_handler(struct work_struct *work)
+{
+ struct ipa_rm_resource *resource;
+ struct ipa_rm_wq_work_type *ipa_rm_work =
+ container_of(work,
+ struct ipa_rm_wq_work_type,
+ work);
+ switch (ipa_rm_work->wq_cmd) {
+ case IPA_RM_WQ_NOTIFY_PROD:
+ if (!IPA_RM_RESORCE_IS_PROD(ipa_rm_work->resource_name))
+ return;
+ if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
+ ipa_rm_work->resource_name,
+ &resource) != 0)
+ return;
+ ipa_rm_resource_producer_notify_clients(
+ (struct ipa_rm_resource_prod *)resource,
+ ipa_rm_work->event);
+
+ break;
+ case IPA_RM_WQ_NOTIFY_CONS:
+ break;
+ case IPA_RM_WQ_RESOURCE_CB:
+ if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
+ ipa_rm_work->resource_name,
+ &resource) != 0)
+ return;
+ ipa_rm_resource_consumer_handle_cb(
+ (struct ipa_rm_resource_cons *)resource,
+ ipa_rm_work->event);
+ break;
+ default:
+ break;
+ }
+
+ kfree((void *) work);
+}
+
+/**
+ * ipa_rm_wq_send_cmd() - send a command for deferred work
+ * @wq_cmd: command that should be executed
+ * @resource_name: resource on which command should be executed
+ *
+ * Returns: 0 on success, negative otherwise
+ */
+int ipa_rm_wq_send_cmd(enum ipa_rm_wq_cmd wq_cmd,
+ enum ipa_rm_resource_name resource_name,
+ enum ipa_rm_event event)
+{
+ int result = -ENOMEM;
+ struct ipa_rm_wq_work_type *work = kzalloc(sizeof(*work), GFP_KERNEL);
+ if (work) {
+ INIT_WORK((struct work_struct *)work, ipa_rm_wq_handler);
+ work->wq_cmd = wq_cmd;
+ work->resource_name = resource_name;
+ work->event = event;
+ result = queue_work(ipa_rm_ctx->ipa_rm_wq,
+ (struct work_struct *)work);
+ }
+ return result;
+}
+
+/**
+ * ipa_rm_initialize() - initialize IPA RM component
+ *
+ * Returns: 0 on success, negative otherwise
+ */
+int ipa_rm_initialize(void)
+{
+ int result;
+
+ ipa_rm_ctx = kzalloc(sizeof(*ipa_rm_ctx), GFP_KERNEL);
+ if (!ipa_rm_ctx) {
+ result = -ENOMEM;
+ goto bail;
+ }
+ ipa_rm_ctx->ipa_rm_wq = create_singlethread_workqueue("ipa_rm_wq");
+ if (!ipa_rm_ctx->ipa_rm_wq) {
+ result = -ENOMEM;
+ goto create_wq_fail;
+ }
+ result = ipa_rm_dep_graph_create(&(ipa_rm_ctx->dep_graph));
+ if (result)
+ goto graph_alloc_fail;
+ IPADBG("IPA RM ipa_rm_initialize SUCCESS\n");
+ return 0;
+
+graph_alloc_fail:
+ destroy_workqueue(ipa_rm_ctx->ipa_rm_wq);
+create_wq_fail:
+ kfree(ipa_rm_ctx);
+bail:
+ return result;
+}
+
+/**
+ * ipa_rm_exit() - free all IPA RM resources
+ */
+void ipa_rm_exit(void)
+{
+ ipa_rm_dep_graph_delete(ipa_rm_ctx->dep_graph);
+ destroy_workqueue(ipa_rm_ctx->ipa_rm_wq);
+ kfree(ipa_rm_ctx);
+ ipa_rm_ctx = NULL;
+}
diff --git a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c
new file mode 100644
index 0000000..6afab42
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c
@@ -0,0 +1,208 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include "ipa_rm_dependency_graph.h"
+#include "ipa_rm_i.h"
+
+static int ipa_rm_dep_get_index(enum ipa_rm_resource_name resource_name)
+{
+ int resource_index = IPA_RM_INDEX_INVALID;
+ if (IPA_RM_RESORCE_IS_PROD(resource_name))
+ resource_index = ipa_rm_prod_index(resource_name);
+ else if (IPA_RM_RESORCE_IS_CONS(resource_name))
+ resource_index = ipa_rm_cons_index(resource_name);
+
+ return resource_index;
+}
+
+/**
+ * ipa_rm_dep_graph_create() - creates graph
+ * @dep_graph: [out] created dependency graph
+ *
+ * Returns: dependency graph on success, NULL on failure
+ */
+int ipa_rm_dep_graph_create(struct ipa_rm_dep_graph **dep_graph)
+{
+ int result = 0;
+ *dep_graph = kzalloc(sizeof(**dep_graph), GFP_KERNEL);
+ if (!*dep_graph) {
+ result = -ENOMEM;
+ goto bail;
+ }
+ rwlock_init(&((*dep_graph)->lock));
+bail:
+ return result;
+}
+
+/**
+ * ipa_rm_dep_graph_delete() - destroyes the graph
+ * @graph: [in] dependency graph
+ *
+ * Frees all resources.
+ */
+void ipa_rm_dep_graph_delete(struct ipa_rm_dep_graph *graph)
+{
+ int resource_index;
+ if (!graph)
+ return;
+ write_lock(&graph->lock);
+ for (resource_index = 0;
+ resource_index < IPA_RM_RESOURCE_MAX;
+ resource_index++)
+ kfree(graph->resource_table[resource_index]);
+ write_unlock(&graph->lock);
+ memset(graph->resource_table, 0, sizeof(graph->resource_table));
+}
+
+/**
+ * ipa_rm_dep_graph_get_resource() - provides a resource by name
+ * @graph: [in] dependency graph
+ * @name: [in] name of the resource
+ * @resource: [out] resource in case of success
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_rm_dep_graph_get_resource(
+ struct ipa_rm_dep_graph *graph,
+ enum ipa_rm_resource_name resource_name,
+ struct ipa_rm_resource **resource)
+{
+ int result;
+ int resource_index;
+ if (!graph) {
+ result = -EINVAL;
+ goto bail;
+ }
+ resource_index = ipa_rm_dep_get_index(resource_name);
+ if (resource_index == IPA_RM_INDEX_INVALID) {
+ result = -EINVAL;
+ goto bail;
+ }
+ read_lock(&graph->lock);
+ *resource = graph->resource_table[resource_index];
+ read_unlock(&graph->lock);
+ if (!*resource) {
+ result = -EINVAL;
+ goto bail;
+ }
+ result = 0;
+bail:
+ return result;
+}
+
+/**
+ * ipa_rm_dep_graph_add() - adds resource to graph
+ * @graph: [in] dependency graph
+ * @resource: [in] resource to add
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_rm_dep_graph_add(struct ipa_rm_dep_graph *graph,
+ struct ipa_rm_resource *resource)
+{
+ int result = 0;
+ int resource_index;
+ if (!graph || !resource) {
+ result = -EINVAL;
+ goto bail;
+ }
+ resource_index = ipa_rm_dep_get_index(resource->name);
+ if (resource_index == IPA_RM_INDEX_INVALID) {
+ result = -EINVAL;
+ goto bail;
+ }
+ write_lock(&graph->lock);
+ graph->resource_table[resource_index] = resource;
+ write_unlock(&graph->lock);
+bail:
+ return result;
+}
+
+/**
+ * ipa_rm_dep_graph_add_dependency() - adds dependency between
+ * two nodes in graph
+ * @graph: [in] dependency graph
+ * @resource_name: [in] resource to add
+ * @depends_on_name: [in] resource to add
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_rm_dep_graph_add_dependency(struct ipa_rm_dep_graph *graph,
+ enum ipa_rm_resource_name resource_name,
+ enum ipa_rm_resource_name depends_on_name)
+{
+ struct ipa_rm_resource *dependant = NULL;
+ struct ipa_rm_resource *dependency = NULL;
+ int result;
+ if (!graph ||
+ !IPA_RM_RESORCE_IS_PROD(resource_name) ||
+ !IPA_RM_RESORCE_IS_CONS(depends_on_name)) {
+ result = -EINVAL;
+ goto bail;
+ }
+ if (ipa_rm_dep_graph_get_resource(graph,
+ resource_name,
+ &dependant)) {
+ result = -EINVAL;
+ goto bail;
+ }
+ if (ipa_rm_dep_graph_get_resource(graph,
+ depends_on_name,
+ &dependency)) {
+ result = -EINVAL;
+ goto bail;
+ }
+ result = ipa_rm_resource_add_dependency(dependant, dependency);
+bail:
+ return result;
+}
+
+/**
+ * ipa_rm_dep_graph_delete_dependency() - deleted dependency between
+ * two nodes in graph
+ * @graph: [in] dependency graph
+ * @resource_name: [in] resource to delete
+ * @depends_on_name: [in] resource to delete
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ */
+int ipa_rm_dep_graph_delete_dependency(struct ipa_rm_dep_graph *graph,
+ enum ipa_rm_resource_name resource_name,
+ enum ipa_rm_resource_name depends_on_name)
+{
+ struct ipa_rm_resource *dependant = NULL;
+ struct ipa_rm_resource *dependency = NULL;
+ int result;
+ if (!graph ||
+ !IPA_RM_RESORCE_IS_PROD(resource_name) ||
+ !IPA_RM_RESORCE_IS_CONS(depends_on_name)) {
+ result = -EINVAL;
+ goto bail;
+ }
+ if (ipa_rm_dep_graph_get_resource(graph,
+ resource_name,
+ &dependant)) {
+ result = -EINVAL;
+ goto bail;
+ }
+ if (ipa_rm_dep_graph_get_resource(graph,
+ depends_on_name,
+ &dependency)) {
+ result = -EINVAL;
+ goto bail;
+ }
+ result = ipa_rm_resource_delete_dependency(dependant, dependency);
+bail:
+ return result;
+}
diff --git a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.h b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.h
new file mode 100644
index 0000000..19d9461
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef _IPA_RM_DEPENDENCY_GRAPH_H_
+#define _IPA_RM_DEPENDENCY_GRAPH_H_
+
+#include <linux/list.h>
+#include <mach/ipa.h>
+#include "ipa_rm_resource.h"
+
+struct ipa_rm_dep_graph {
+ struct ipa_rm_resource *resource_table[IPA_RM_RESOURCE_MAX];
+ rwlock_t lock;
+};
+
+int ipa_rm_dep_graph_get_resource(
+ struct ipa_rm_dep_graph *graph,
+ enum ipa_rm_resource_name name,
+ struct ipa_rm_resource **resource);
+
+int ipa_rm_dep_graph_create(struct ipa_rm_dep_graph **dep_graph);
+
+void ipa_rm_dep_graph_delete(struct ipa_rm_dep_graph *graph);
+
+int ipa_rm_dep_graph_add(struct ipa_rm_dep_graph *graph,
+ struct ipa_rm_resource *resource);
+
+int ipa_rm_dep_graph_add_dependency(struct ipa_rm_dep_graph *graph,
+ enum ipa_rm_resource_name resource_name,
+ enum ipa_rm_resource_name depends_on_name);
+
+int ipa_rm_dep_graph_delete_dependency(struct ipa_rm_dep_graph *graph,
+ enum ipa_rm_resource_name resource_name,
+ enum ipa_rm_resource_name depends_on_name);
+
+#endif /* _IPA_RM_DEPENDENCY_GRAPH_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_rm_i.h b/drivers/platform/msm/ipa/ipa_rm_i.h
new file mode 100644
index 0000000..141a442
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_rm_i.h
@@ -0,0 +1,64 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef _IPA_RM_I_H_
+#define _IPA_RM_I_H_
+
+#include <linux/workqueue.h>
+#include <mach/ipa.h>
+
+#define IPA_RM_RESOURCE_CONS_MAX \
+ (IPA_RM_RESOURCE_MAX - IPA_RM_RESOURCE_PROD_MAX)
+#define IPA_RM_RESORCE_IS_PROD(x) \
+ (x >= IPA_RM_RESOURCE_PROD && x < IPA_RM_RESOURCE_PROD_MAX)
+#define IPA_RM_RESORCE_IS_CONS(x) \
+ (x >= IPA_RM_RESOURCE_PROD_MAX && x < IPA_RM_RESOURCE_MAX)
+#define IPA_RM_INDEX_INVALID (-1)
+
+int ipa_rm_prod_index(enum ipa_rm_resource_name resource_name);
+int ipa_rm_cons_index(enum ipa_rm_resource_name resource_name);
+
+/**
+ * enum ipa_rm_wq_cmd - workqueue commands
+ */
+enum ipa_rm_wq_cmd {
+ IPA_RM_WQ_NOTIFY_PROD,
+ IPA_RM_WQ_NOTIFY_CONS,
+ IPA_RM_WQ_RESOURCE_CB
+};
+
+/**
+ * struct ipa_rm_wq_work_type - IPA RM worqueue specific
+ * work type
+ * @work: work struct
+ * @wq_cmd: command that should be processed in workqueue context
+ * @resource_name: name of the resource on which this work
+ * should be done
+ * @dep_graph: data structure to search for resource if exists
+ * @event: event to notify
+ */
+struct ipa_rm_wq_work_type {
+ struct work_struct work;
+ enum ipa_rm_wq_cmd wq_cmd;
+ enum ipa_rm_resource_name resource_name;
+ enum ipa_rm_event event;
+};
+
+int ipa_rm_wq_send_cmd(enum ipa_rm_wq_cmd wq_cmd,
+ enum ipa_rm_resource_name resource_name,
+ enum ipa_rm_event event);
+
+int ipa_rm_initialize(void);
+
+void ipa_rm_exit(void);
+
+#endif /* _IPA_RM_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c b/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c
new file mode 100644
index 0000000..2a3b8d3
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c
@@ -0,0 +1,249 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/unistd.h>
+#include <linux/workqueue.h>
+#include <mach/ipa.h>
+#include "ipa_i.h"
+
+/**
+ * struct ipa_rm_it_private - IPA RM Inactivity Timer private
+ * data
+ * @initied: indicates if instance was initialized
+ * @lock - spinlock for mutual exclusion
+ * @resource_name - resource name
+ * @work: delayed work object for running delayed releas
+ * function
+ * @release_in_prog: boolean flag indicates if release resource
+ * is scheduled for happen in the future.
+ * @jiffies: number of jiffies for timeout
+ *
+ * WWAN private - holds all relevant info about WWAN driver
+ */
+struct ipa_rm_it_private {
+ bool initied;
+ enum ipa_rm_resource_name resource_name;
+ spinlock_t lock;
+ struct delayed_work work;
+ bool release_in_prog;
+ unsigned long jiffies;
+};
+
+static struct ipa_rm_it_private ipa_rm_it_handles[IPA_RM_RESOURCE_MAX];
+
+/**
+ * ipa_rm_inactivity_timer_func() - called when timer expired in
+ * the context of the shared workqueue. Checks internally is
+ * release_in_prog flag is set and calls to
+ * ipa_rm_release_resource(). release_in_prog is cleared when
+ * calling to ipa_rm_inactivity_timer_request_resource(). In
+ * this situation this function shall not call to
+ * ipa_rm_release_resource() since the resource needs to remain
+ * up
+ *
+ * @work: work object provided by the work queue
+ *
+ * Return codes:
+ * None
+ */
+static void ipa_rm_inactivity_timer_func(struct work_struct *work)
+{
+
+ struct ipa_rm_it_private *me = container_of(to_delayed_work(work),
+ struct ipa_rm_it_private,
+ work);
+ unsigned long flags;
+
+ IPADBG("%s: timer expired for resource %d!\n", __func__,
+ me->resource_name);
+
+ /* check that release still need to be performed */
+ spin_lock_irqsave(
+ &ipa_rm_it_handles[me->resource_name].lock, flags);
+ if (ipa_rm_it_handles[me->resource_name].release_in_prog) {
+ IPADBG("%s: calling release_resource on resource %d!\n",
+ __func__, me->resource_name);
+ ipa_rm_release_resource(me->resource_name);
+ ipa_rm_it_handles[me->resource_name].release_in_prog = false;
+ }
+ spin_unlock_irqrestore(
+ &ipa_rm_it_handles[me->resource_name].lock, flags);
+}
+
+/**
+* ipa_rm_inactivity_timer_init() - Init function for IPA RM
+* inactivity timer. This function shall be called prior calling
+* any other API of IPA RM inactivity timer.
+*
+* @resource_name: Resource name. @see ipa_rm.h
+* @msecs: time in miliseccond, that IPA RM inactivity timer
+* shall wait prior calling to ipa_rm_release_resource().
+*
+* Return codes:
+* 0: success
+* -EINVAL: invalid parameters
+*/
+int ipa_rm_inactivity_timer_init(enum ipa_rm_resource_name resource_name,
+ unsigned long msecs)
+{
+ IPADBG("%s: resource %d\n", __func__, resource_name);
+
+ if (resource_name < 0 ||
+ resource_name >= IPA_RM_RESOURCE_MAX) {
+ IPAERR("%s: Invalid parameter\n", __func__);
+ return -EINVAL;
+ }
+
+ if (ipa_rm_it_handles[resource_name].initied) {
+ IPAERR("%s: resource %d already inited\n",
+ __func__, resource_name);
+ return -EINVAL;
+ }
+
+ spin_lock_init(&ipa_rm_it_handles[resource_name].lock);
+ ipa_rm_it_handles[resource_name].resource_name = resource_name;
+ ipa_rm_it_handles[resource_name].jiffies = msecs_to_jiffies(msecs);
+ ipa_rm_it_handles[resource_name].release_in_prog = false;
+
+ INIT_DELAYED_WORK(&ipa_rm_it_handles[resource_name].work,
+ ipa_rm_inactivity_timer_func);
+ ipa_rm_it_handles[resource_name].initied = 1;
+
+ return 0;
+}
+
+/**
+* ipa_rm_inactivity_timer_destroy() - De-Init function for IPA
+* RM inactivity timer.
+*
+* @resource_name: Resource name. @see ipa_rm.h
+*
+* Return codes:
+* 0: success
+* -EINVAL: invalid parameters
+*/
+int ipa_rm_inactivity_timer_destroy(enum ipa_rm_resource_name resource_name)
+{
+ IPADBG("%s: resource %d\n", __func__, resource_name);
+
+ if (resource_name < 0 ||
+ resource_name >= IPA_RM_RESOURCE_MAX) {
+ IPAERR("%s: Invalid parameter\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!ipa_rm_it_handles[resource_name].initied) {
+ IPAERR("%s: resource %d already inited\n",
+ __func__, resource_name);
+ return -EINVAL;
+ }
+
+ memset(&ipa_rm_it_handles[resource_name], 0,
+ sizeof(struct ipa_rm_it_private));
+
+ return 0;
+}
+
+/**
+* ipa_rm_inactivity_timer_request_resource() - Same as
+* ipa_rm_request_resource(), with a difference that calling to
+* this function will also cancel the inactivity timer, if
+* ipa_rm_inactivity_timer_release_resource() was called earlier.
+*
+* @resource_name: Resource name. @see ipa_rm.h
+*
+* Return codes:
+* 0: success
+* -EINVAL: invalid parameters
+*/
+int ipa_rm_inactivity_timer_request_resource(
+ enum ipa_rm_resource_name resource_name)
+{
+ int ret;
+ unsigned long flags;
+ IPADBG("%s: resource %d\n", __func__, resource_name);
+
+ if (resource_name < 0 ||
+ resource_name >= IPA_RM_RESOURCE_MAX) {
+ IPAERR("%s: Invalid parameter\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!ipa_rm_it_handles[resource_name].initied) {
+ IPAERR("%s: Not initialized\n", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ipa_rm_it_handles[resource_name].lock, flags);
+ cancel_delayed_work(&ipa_rm_it_handles[resource_name].work);
+ ipa_rm_it_handles[resource_name].release_in_prog = false;
+ spin_unlock_irqrestore(&ipa_rm_it_handles[resource_name].lock, flags);
+ ret = ipa_rm_request_resource(resource_name);
+ IPADBG("%s: resource %d: returning %d\n", __func__, resource_name, ret);
+ return ret;
+}
+
+/**
+* ipa_rm_inactivity_timer_release_resource() - Sets the
+* inactivity timer to the timeout set by
+* ipa_rm_inactivity_timer_init(). When the timeout expires, IPA
+* RM inactivity timer will call to ipa_rm_release_resource().
+* If a call to ipa_rm_inactivity_timer_request_resource() was
+* made BEFORE the timout has expired, rge timer will be
+* cancelled.
+*
+* @resource_name: Resource name. @see ipa_rm.h
+*
+* Return codes:
+* 0: success
+* -EINVAL: invalid parameters
+*/
+int ipa_rm_inactivity_timer_release_resource(
+ enum ipa_rm_resource_name resource_name)
+{
+ unsigned long flags;
+ IPADBG("%s: resource %d\n", __func__, resource_name);
+
+ if (resource_name < 0 ||
+ resource_name >= IPA_RM_RESOURCE_MAX) {
+ IPAERR("%s: Invalid parameter\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!ipa_rm_it_handles[resource_name].initied) {
+ IPAERR("%s: Not initialized\n", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ipa_rm_it_handles[resource_name].lock, flags);
+ if (ipa_rm_it_handles[resource_name].release_in_prog) {
+ IPADBG("%s: Timer already set, not scheduling again %d\n",
+ __func__, resource_name);
+ spin_unlock_irqrestore(
+ &ipa_rm_it_handles[resource_name].lock, flags);
+ return 0;
+ }
+ ipa_rm_it_handles[resource_name].release_in_prog = true;
+ spin_unlock_irqrestore(&ipa_rm_it_handles[resource_name].lock, flags);
+
+ IPADBG("%s: setting delayed work\n", __func__);
+ schedule_delayed_work(&ipa_rm_it_handles[resource_name].work,
+ ipa_rm_it_handles[resource_name].jiffies);
+
+ return 0;
+}
+
diff --git a/drivers/platform/msm/ipa/ipa_rm_peers_list.c b/drivers/platform/msm/ipa/ipa_rm_peers_list.c
new file mode 100644
index 0000000..55f8239
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_rm_peers_list.c
@@ -0,0 +1,247 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include "ipa_i.h"
+#include "ipa_rm_i.h"
+#include "ipa_rm_resource.h"
+
+/**
+ * ipa_rm_peers_list_get_resource_index() - resource name to index
+ * of this resource in corresponding peers list
+ * @resource_name: [in] resource name
+ *
+ * Returns: resource index mapping, IPA_RM_INDEX_INVALID
+ * in case provided resource name isn't contained in enum
+ * ipa_rm_resource_name.
+ *
+ */
+static int ipa_rm_peers_list_get_resource_index(
+ enum ipa_rm_resource_name resource_name)
+{
+ int resource_index = IPA_RM_INDEX_INVALID;
+ if (IPA_RM_RESORCE_IS_PROD(resource_name))
+ resource_index = ipa_rm_prod_index(resource_name);
+ else if (IPA_RM_RESORCE_IS_CONS(resource_name)) {
+ resource_index = ipa_rm_cons_index(resource_name);
+ if (resource_index != IPA_RM_INDEX_INVALID)
+ resource_index =
+ resource_index - IPA_RM_RESOURCE_PROD_MAX;
+ }
+
+ return resource_index;
+}
+
+static bool ipa_rm_peers_list_check_index(int index,
+ struct ipa_rm_peers_list *peers_list)
+{
+ return !(index > peers_list->max_peers || index < 0);
+}
+
+/**
+ * ipa_rm_peers_list_create() - creates the peers list
+ *
+ * @max_peers: maximum number of peers in new list
+ * @peers_list: [out] newly created peers list
+ *
+ * Returns: 0 in case of SUCCESS, negative otherwise
+ */
+int ipa_rm_peers_list_create(int max_peers,
+ struct ipa_rm_peers_list **peers_list)
+{
+ int result;
+ *peers_list = kzalloc(sizeof(**peers_list), GFP_KERNEL);
+ if (!*peers_list) {
+ result = -ENOMEM;
+ goto bail;
+ }
+ rwlock_init(&(*peers_list)->peers_lock);
+ (*peers_list)->max_peers = max_peers;
+ (*peers_list)->peers = kzalloc((*peers_list)->max_peers *
+ sizeof(struct ipa_rm_resource *), GFP_KERNEL);
+ if (!((*peers_list)->peers)) {
+ result = -ENOMEM;
+ goto list_alloc_fail;
+ }
+ return 0;
+
+list_alloc_fail:
+ kfree(*peers_list);
+bail:
+ return result;
+}
+
+/**
+ * ipa_rm_peers_list_delete() - deletes the peers list
+ *
+ * @peers_list: peers list
+ *
+ */
+void ipa_rm_peers_list_delete(struct ipa_rm_peers_list *peers_list)
+{
+ if (peers_list) {
+ kfree(peers_list->peers);
+ kfree(peers_list);
+ }
+}
+
+/**
+ * ipa_rm_peers_list_remove_peer() - removes peer from the list
+ *
+ * @peers_list: peers list
+ * @resource_name: name of the resource to remove
+ *
+ */
+void ipa_rm_peers_list_remove_peer(
+ struct ipa_rm_peers_list *peers_list,
+ enum ipa_rm_resource_name resource_name)
+{
+ if (!peers_list)
+ return;
+ write_lock(&peers_list->peers_lock);
+ peers_list->peers[ipa_rm_peers_list_get_resource_index(
+ resource_name)] = NULL;
+ peers_list->peers_count--;
+ write_unlock(&peers_list->peers_lock);
+}
+
+/**
+ * ipa_rm_peers_list_add_peer() - adds peer to the list
+ *
+ * @peers_list: peers list
+ * @resource: resource to add
+ *
+ */
+void ipa_rm_peers_list_add_peer(
+ struct ipa_rm_peers_list *peers_list,
+ struct ipa_rm_resource *resource)
+{
+ if (!peers_list || !resource)
+ return;
+ read_lock(&peers_list->peers_lock);
+ peers_list->peers[ipa_rm_peers_list_get_resource_index(
+ resource->name)] =
+ resource;
+ peers_list->peers_count++;
+ read_unlock(&peers_list->peers_lock);
+}
+
+/**
+ * ipa_rm_peers_list_is_empty() - checks
+ * if resource peers list is empty
+ *
+ * @peers_list: peers list
+ *
+ * Returns: true if the list is empty, false otherwise
+ */
+bool ipa_rm_peers_list_is_empty(struct ipa_rm_peers_list *peers_list)
+{
+ bool result = true;
+ if (!peers_list)
+ goto bail;
+ read_lock(&peers_list->peers_lock);
+ if (peers_list->peers_count > 0)
+ result = false;
+ read_unlock(&peers_list->peers_lock);
+bail:
+ return result;
+}
+
+/**
+ * ipa_rm_peers_list_has_last_peer() - checks
+ * if resource peers list has exactly one peer
+ *
+ * @peers_list: peers list
+ *
+ * Returns: true if the list has exactly one peer, false otherwise
+ */
+bool ipa_rm_peers_list_has_last_peer(
+ struct ipa_rm_peers_list *peers_list)
+{
+ bool result = true;
+ if (!peers_list)
+ goto bail;
+ read_lock(&peers_list->peers_lock);
+ if (peers_list->peers_count == 1)
+ result = false;
+ read_unlock(&peers_list->peers_lock);
+bail:
+ return result;
+}
+
+/**
+ * ipa_rm_peers_list_check_dependency() - check dependency
+ * between 2 peer lists
+ * @resource_peers: first peers list
+ * @resource_name: first peers list resource name
+ * @depends_on_peers: second peers list
+ * @depends_on_name: second peers list resource name
+ *
+ * Returns: true if there is dependency, false otherwise
+ *
+ */
+bool ipa_rm_peers_list_check_dependency(
+ struct ipa_rm_peers_list *resource_peers,
+ enum ipa_rm_resource_name resource_name,
+ struct ipa_rm_peers_list *depends_on_peers,
+ enum ipa_rm_resource_name depends_on_name)
+{
+ bool result = false;
+ if (!resource_peers || !depends_on_peers)
+ return result;
+ read_lock(&resource_peers->peers_lock);
+ if (resource_peers->peers[ipa_rm_peers_list_get_resource_index(
+ depends_on_name)] != NULL)
+ result = true;
+ read_unlock(&resource_peers->peers_lock);
+
+ read_lock(&depends_on_peers->peers_lock);
+ if (depends_on_peers->peers[ipa_rm_peers_list_get_resource_index(
+ resource_name)] != NULL)
+ result = true;
+ read_unlock(&depends_on_peers->peers_lock);
+
+ return result;
+}
+
+/**
+ * ipa_rm_peers_list_get_resource() - get resource by
+ * resource index
+ * @resource_index: resource index
+ * @resource_peers: peers list
+ *
+ * Returns: the resource if found, NULL otherwise
+ */
+struct ipa_rm_resource *ipa_rm_peers_list_get_resource(int resource_index,
+ struct ipa_rm_peers_list *resource_peers)
+{
+ struct ipa_rm_resource *result = NULL;
+ if (!ipa_rm_peers_list_check_index(resource_index, resource_peers))
+ goto bail;
+ read_lock(&resource_peers->peers_lock);
+ result = resource_peers->peers[resource_index];
+ read_unlock(&resource_peers->peers_lock);
+bail:
+ return result;
+}
+
+/**
+ * ipa_rm_peers_list_get_size() - get peers list sise
+ *
+ * @peers_list: peers list
+ *
+ * Returns: the size of the peers list
+ */
+int ipa_rm_peers_list_get_size(struct ipa_rm_peers_list *peers_list)
+{
+ return peers_list->max_peers;
+}
diff --git a/drivers/platform/msm/ipa/ipa_rm_peers_list.h b/drivers/platform/msm/ipa/ipa_rm_peers_list.h
new file mode 100644
index 0000000..f8fd1ca
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_rm_peers_list.h
@@ -0,0 +1,55 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef _IPA_RM_PEERS_LIST_H_
+#define _IPA_RM_PEERS_LIST_H_
+
+#include "ipa_rm_resource.h"
+
+/**
+ * struct ipa_rm_peers_list - IPA RM resource peers list
+ * @peers: the list of references to resources dependent on this resource
+ * in case of producer or list of dependencies in case of consumer
+ * @max_peers: maximum number of peers for this resource
+ * @peers_count: actual number of peers for this resource
+ * @peers_lock: RW lock for peers container
+ */
+struct ipa_rm_peers_list {
+ struct ipa_rm_resource **peers;
+ int max_peers;
+ int peers_count;
+ rwlock_t peers_lock;
+};
+
+int ipa_rm_peers_list_create(int max_peers,
+ struct ipa_rm_peers_list **peers_list);
+void ipa_rm_peers_list_delete(struct ipa_rm_peers_list *peers_list);
+void ipa_rm_peers_list_remove_peer(
+ struct ipa_rm_peers_list *peers_list,
+ enum ipa_rm_resource_name resource_name);
+void ipa_rm_peers_list_add_peer(
+ struct ipa_rm_peers_list *peers_list,
+ struct ipa_rm_resource *resource);
+bool ipa_rm_peers_list_check_dependency(
+ struct ipa_rm_peers_list *resource_peers,
+ enum ipa_rm_resource_name resource_name,
+ struct ipa_rm_peers_list *depends_on_peers,
+ enum ipa_rm_resource_name depends_on_name);
+struct ipa_rm_resource *ipa_rm_peers_list_get_resource(int resource_index,
+ struct ipa_rm_peers_list *peers_list);
+int ipa_rm_peers_list_get_size(struct ipa_rm_peers_list *peers_list);
+bool ipa_rm_peers_list_is_empty(struct ipa_rm_peers_list *peers_list);
+bool ipa_rm_peers_list_has_last_peer(
+ struct ipa_rm_peers_list *peers_list);
+
+
+#endif /* _IPA_RM_PEERS_LIST_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_rm_resource.c b/drivers/platform/msm/ipa/ipa_rm_resource.c
new file mode 100644
index 0000000..3ba8e84
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_rm_resource.c
@@ -0,0 +1,809 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include "ipa_i.h"
+#include "ipa_rm_resource.h"
+#include "ipa_rm_i.h"
+
+/**
+ * ipa_rm_dep_prod_index() - producer name to producer index mapping
+ * @resource_name: [in] resource name (should be of producer)
+ *
+ * Returns: resource index mapping, IPA_RM_INDEX_INVALID
+ * in case provided resource name isn't contained
+ * in enum ipa_rm_resource_name or is not of producers.
+ *
+ */
+int ipa_rm_prod_index(enum ipa_rm_resource_name resource_name)
+{
+ int result = resource_name;
+ switch (resource_name) {
+ case IPA_RM_RESOURCE_BRIDGE_PROD:
+ case IPA_RM_RESOURCE_A2_PROD:
+ case IPA_RM_RESOURCE_USB_PROD:
+ case IPA_RM_RESOURCE_HSIC_PROD:
+ case IPA_RM_RESOURCE_STD_ECM_PROD:
+ case IPA_RM_RESOURCE_WWAN_0_PROD:
+ case IPA_RM_RESOURCE_WWAN_1_PROD:
+ case IPA_RM_RESOURCE_WWAN_2_PROD:
+ case IPA_RM_RESOURCE_WWAN_3_PROD:
+ case IPA_RM_RESOURCE_WWAN_4_PROD:
+ case IPA_RM_RESOURCE_WWAN_5_PROD:
+ case IPA_RM_RESOURCE_WWAN_6_PROD:
+ case IPA_RM_RESOURCE_WWAN_7_PROD:
+ case IPA_RM_RESOURCE_WLAN_PROD:
+ break;
+ default:
+ result = IPA_RM_INDEX_INVALID;
+ break;
+ }
+ return result;
+}
+
+/**
+ * ipa_rm_cons_index() - consumer name to consumer index mapping
+ * @resource_name: [in] resource name (should be of consumer)
+ *
+ * Returns: resource index mapping, IPA_RM_INDEX_INVALID
+ * in case provided resource name isn't contained
+ * in enum ipa_rm_resource_name or is not of consumers.
+ *
+ */
+int ipa_rm_cons_index(enum ipa_rm_resource_name resource_name)
+{
+ int result = resource_name;
+ switch (resource_name) {
+ case IPA_RM_RESOURCE_A2_CONS:
+ case IPA_RM_RESOURCE_USB_CONS:
+ case IPA_RM_RESOURCE_HSIC_CONS:
+ break;
+ default:
+ result = IPA_RM_INDEX_INVALID;
+ break;
+ }
+ return result;
+}
+
+static int ipa_rm_resource_consumer_request(
+ struct ipa_rm_resource_cons *consumer)
+{
+ int result = 0;
+ int driver_result;
+ unsigned long flags;
+ IPADBG("IPA RM ::ipa_rm_resource_consumer_request ENTER\n");
+ spin_lock_irqsave(&consumer->resource.state_lock, flags);
+ switch (consumer->resource.state) {
+ case IPA_RM_RELEASED:
+ case IPA_RM_RELEASE_IN_PROGRESS:
+ {
+ enum ipa_rm_resource_state prev_state =
+ consumer->resource.state;
+ consumer->resource.state = IPA_RM_REQUEST_IN_PROGRESS;
+ spin_unlock_irqrestore(&consumer->resource.state_lock, flags);
+ driver_result = consumer->request_resource();
+ spin_lock_irqsave(&consumer->resource.state_lock, flags);
+ if (driver_result == 0)
+ consumer->resource.state = IPA_RM_GRANTED;
+ else if (driver_result != -EINPROGRESS) {
+ consumer->resource.state = prev_state;
+ result = driver_result;
+ goto bail;
+ }
+ result = driver_result;
+ break;
+ }
+ case IPA_RM_GRANTED:
+ break;
+ case IPA_RM_REQUEST_IN_PROGRESS:
+ result = -EINPROGRESS;
+ break;
+ default:
+ result = -EPERM;
+ goto bail;
+ }
+ consumer->usage_count++;
+bail:
+ spin_unlock_irqrestore(&consumer->resource.state_lock, flags);
+ IPADBG("IPA RM ::ipa_rm_resource_consumer_request EXIT [%d]\n", result);
+ return result;
+}
+
+static int ipa_rm_resource_consumer_release(
+ struct ipa_rm_resource_cons *consumer)
+{
+ int result = 0;
+ int driver_result;
+ unsigned long flags;
+ enum ipa_rm_resource_state save_state;
+ IPADBG("IPA RM ::ipa_rm_resource_consumer_release ENTER\n");
+ spin_lock_irqsave(&consumer->resource.state_lock, flags);
+ switch (consumer->resource.state) {
+ case IPA_RM_RELEASED:
+ break;
+ case IPA_RM_GRANTED:
+ case IPA_RM_REQUEST_IN_PROGRESS:
+ if (consumer->usage_count > 0)
+ consumer->usage_count--;
+ if (consumer->usage_count == 0) {
+ save_state = consumer->resource.state;
+ consumer->resource.state = IPA_RM_RELEASE_IN_PROGRESS;
+ spin_unlock_irqrestore(&consumer->resource.state_lock,
+ flags);
+ driver_result = consumer->release_resource();
+ spin_lock_irqsave(&consumer->resource.state_lock,
+ flags);
+ if (driver_result == 0)
+ consumer->resource.state = IPA_RM_RELEASED;
+ else if (driver_result != -EINPROGRESS)
+ consumer->resource.state = save_state;
+ result = driver_result;
+ }
+ break;
+ case IPA_RM_RELEASE_IN_PROGRESS:
+ if (consumer->usage_count > 0)
+ consumer->usage_count--;
+ result = -EINPROGRESS;
+ break;
+ default:
+ result = -EPERM;
+ goto bail;
+ }
+bail:
+ spin_unlock_irqrestore(&consumer->resource.state_lock, flags);
+ IPADBG("IPA RM ::ipa_rm_resource_consumer_release EXIT [%d]\n", result);
+ return result;
+}
+
+/**
+ * ipa_rm_resource_producer_notify_clients() - notify
+ * all registered clients of given producer
+ * @producer: producer
+ * @event: event to notify
+ */
+void ipa_rm_resource_producer_notify_clients(
+ struct ipa_rm_resource_prod *producer,
+ enum ipa_rm_event event)
+{
+ struct ipa_rm_notification_info *reg_info, *reg_info_cloned;
+ struct list_head *pos, *q;
+ LIST_HEAD(cloned_list);
+ read_lock(&producer->event_listeners_lock);
+ list_for_each(pos, &(producer->event_listeners)) {
+ reg_info = list_entry(pos,
+ struct ipa_rm_notification_info,
+ link);
+ reg_info_cloned = kzalloc(sizeof(*reg_info_cloned), GFP_ATOMIC);
+ if (!reg_info_cloned)
+ goto clone_list_failed;
+ reg_info_cloned->reg_params.notify_cb =
+ reg_info->reg_params.notify_cb;
+ reg_info_cloned->reg_params.user_data =
+ reg_info->reg_params.user_data;
+ list_add(®_info_cloned->link, &cloned_list);
+ }
+ read_unlock(&producer->event_listeners_lock);
+ list_for_each_safe(pos, q, &cloned_list) {
+ reg_info = list_entry(pos,
+ struct ipa_rm_notification_info,
+ link);
+ reg_info->reg_params.notify_cb(
+ reg_info->reg_params.user_data,
+ event,
+ 0);
+ list_del(pos);
+ kfree(reg_info);
+ }
+ return;
+clone_list_failed:
+ read_unlock(&producer->event_listeners_lock);
+}
+
+static int ipa_rm_resource_producer_create(struct ipa_rm_resource **resource,
+ struct ipa_rm_resource_prod **producer,
+ struct ipa_rm_create_params *create_params,
+ int *max_peers)
+{
+ int result = 0;
+ *producer = kzalloc(sizeof(**producer), GFP_KERNEL);
+ if (*producer == NULL) {
+ result = -ENOMEM;
+ goto bail;
+ }
+ rwlock_init(&(*producer)->event_listeners_lock);
+ INIT_LIST_HEAD(&((*producer)->event_listeners));
+ result = ipa_rm_resource_producer_register(*producer,
+ &(create_params->reg_params));
+ if (result)
+ goto register_fail;
+ (*resource) = (struct ipa_rm_resource *) (*producer);
+ (*resource)->type = IPA_RM_PRODUCER;
+ *max_peers = IPA_RM_RESOURCE_CONS_MAX;
+ goto bail;
+register_fail:
+ kfree(*producer);
+bail:
+ return result;
+}
+
+static void ipa_rm_resource_producer_delete(
+ struct ipa_rm_resource_prod *producer)
+{
+ struct ipa_rm_notification_info *reg_info;
+ struct list_head *pos, *q;
+ write_lock(&producer->event_listeners_lock);
+ list_for_each_safe(pos, q, &(producer->event_listeners)) {
+ reg_info = list_entry(pos,
+ struct ipa_rm_notification_info,
+ link);
+ list_del(pos);
+ kfree(reg_info);
+ }
+ write_unlock(&producer->event_listeners_lock);
+}
+
+static int ipa_rm_resource_consumer_create(struct ipa_rm_resource **resource,
+ struct ipa_rm_resource_cons **consumer,
+ struct ipa_rm_create_params *create_params,
+ int *max_peers)
+{
+ int result = 0;
+ *consumer = kzalloc(sizeof(**consumer), GFP_KERNEL);
+ if (*consumer == NULL) {
+ result = -ENOMEM;
+ goto bail;
+ }
+ (*consumer)->request_resource = create_params->request_resource;
+ (*consumer)->release_resource = create_params->release_resource;
+ (*resource) = (struct ipa_rm_resource *) (*consumer);
+ (*resource)->type = IPA_RM_CONSUMER;
+ *max_peers = IPA_RM_RESOURCE_PROD_MAX;
+bail:
+ return result;
+}
+
+/**
+ * ipa_rm_resource_create() - creates resource
+ * @create_params: [in] parameters needed
+ * for resource initialization with IPA RM
+ * @resource: [out] created resource
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_rm_resource_create(
+ struct ipa_rm_create_params *create_params,
+ struct ipa_rm_resource **resource)
+{
+ struct ipa_rm_resource_cons *consumer;
+ struct ipa_rm_resource_prod *producer;
+ int max_peers;
+ int result = 0;
+
+ if (!create_params) {
+ result = -EINVAL;
+ goto bail;
+ }
+ if (IPA_RM_RESORCE_IS_PROD(create_params->name)) {
+ result = ipa_rm_resource_producer_create(resource,
+ &producer,
+ create_params,
+ &max_peers);
+ if (result)
+ goto bail;
+ } else if (IPA_RM_RESORCE_IS_CONS(create_params->name)) {
+ result = ipa_rm_resource_consumer_create(resource,
+ &consumer,
+ create_params,
+ &max_peers);
+ if (result)
+ goto bail;
+ } else {
+ result = -EPERM;
+ goto bail;
+ }
+ result = ipa_rm_peers_list_create(max_peers,
+ &((*resource)->peers_list));
+ if (result)
+ goto peers_alloc_fail;
+ (*resource)->name = create_params->name;
+ (*resource)->state = IPA_RM_RELEASED;
+ spin_lock_init(&((*resource)->state_lock));
+ goto bail;
+peers_alloc_fail:
+ ipa_rm_resource_delete(*resource);
+bail:
+ return result;
+}
+
+/**
+ * ipa_rm_resource_delete() - deletes resource
+ * @resource: [in] resource
+ * for resource initialization with IPA RM
+ */
+void ipa_rm_resource_delete(struct ipa_rm_resource *resource)
+{
+ if (!resource)
+ return;
+ if (resource->peers_list)
+ ipa_rm_peers_list_delete(resource->peers_list);
+ if (resource->type == IPA_RM_PRODUCER) {
+ ipa_rm_resource_producer_delete(
+ (struct ipa_rm_resource_prod *) resource);
+ kfree((struct ipa_rm_resource_prod *) resource);
+ } else
+ kfree((struct ipa_rm_resource_cons *) resource);
+}
+
+/**
+ * ipa_rm_resource_register() - register resource
+ * @resource: [in] resource
+ * @reg_params: [in] registration parameters
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * Producer resource is expected for this call.
+ *
+ */
+int ipa_rm_resource_producer_register(struct ipa_rm_resource_prod *producer,
+ struct ipa_rm_register_params *reg_params)
+{
+ int result = 0;
+ struct ipa_rm_notification_info *reg_info;
+ struct list_head *pos;
+ if (!producer || !reg_params) {
+ result = -EPERM;
+ goto bail;
+ }
+ read_lock(&producer->event_listeners_lock);
+ list_for_each(pos, &(producer->event_listeners)) {
+ reg_info = list_entry(pos,
+ struct ipa_rm_notification_info,
+ link);
+ if (reg_info->reg_params.notify_cb ==
+ reg_params->notify_cb) {
+ result = -EPERM;
+ read_unlock(&producer->event_listeners_lock);
+ goto bail;
+ }
+
+ }
+ read_unlock(&producer->event_listeners_lock);
+ reg_info = kzalloc(sizeof(*reg_info), GFP_KERNEL);
+ if (reg_info == NULL) {
+ result = -ENOMEM;
+ goto bail;
+ }
+ reg_info->reg_params.user_data = reg_params->user_data;
+ reg_info->reg_params.notify_cb = reg_params->notify_cb;
+ INIT_LIST_HEAD(®_info->link);
+ write_lock(&producer->event_listeners_lock);
+ list_add(®_info->link, &producer->event_listeners);
+ write_unlock(&producer->event_listeners_lock);
+bail:
+ return result;
+}
+
+/**
+ * ipa_rm_resource_deregister() - register resource
+ * @resource: [in] resource
+ * @reg_params: [in] registration parameters
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * Producer resource is expected for this call.
+ * This function deleted only single instance of
+ * registration info.
+ *
+ */
+int ipa_rm_resource_producer_deregister(struct ipa_rm_resource_prod *producer,
+ struct ipa_rm_register_params *reg_params)
+{
+ int result = -EINVAL;
+ struct ipa_rm_notification_info *reg_info;
+ struct list_head *pos, *q;
+ if (!producer || !reg_params)
+ return -EINVAL;
+ write_lock(&producer->event_listeners_lock);
+ list_for_each_safe(pos, q, &(producer->event_listeners)) {
+ reg_info = list_entry(pos,
+ struct ipa_rm_notification_info,
+ link);
+ if (reg_info->reg_params.notify_cb ==
+ reg_params->notify_cb) {
+ list_del(pos);
+ kfree(reg_info);
+ result = 0;
+ goto bail;
+ }
+
+ }
+bail:
+ write_unlock(&producer->event_listeners_lock);
+ return result;
+}
+
+/**
+ * ipa_rm_resource_add_dependency() - add dependency between two
+ * given resources
+ * @resource: [in] resource resource
+ * @depends_on: [in] depends_on resource
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_rm_resource_add_dependency(struct ipa_rm_resource *resource,
+ struct ipa_rm_resource *depends_on)
+{
+ int result = 0;
+ unsigned long flags;
+ int consumer_result;
+ if (!resource || !depends_on)
+ return -EINVAL;
+ if (ipa_rm_peers_list_check_dependency(resource->peers_list,
+ resource->name,
+ depends_on->peers_list,
+ depends_on->name))
+ return -EINVAL;
+ ipa_rm_peers_list_add_peer(resource->peers_list, depends_on);
+ ipa_rm_peers_list_add_peer(depends_on->peers_list, resource);
+ spin_lock_irqsave(&resource->state_lock, flags);
+ switch (resource->state) {
+ case IPA_RM_RELEASED:
+ case IPA_RM_RELEASE_IN_PROGRESS:
+ break;
+ case IPA_RM_GRANTED:
+ case IPA_RM_REQUEST_IN_PROGRESS:
+ {
+ enum ipa_rm_resource_state prev_state = resource->state;
+ resource->state = IPA_RM_REQUEST_IN_PROGRESS;
+ ((struct ipa_rm_resource_prod *)
+ resource)->pending_request++;
+ spin_unlock_irqrestore(&resource->state_lock, flags);
+ consumer_result = ipa_rm_resource_consumer_request(
+ (struct ipa_rm_resource_cons *)depends_on);
+ spin_lock_irqsave(&resource->state_lock, flags);
+ if (consumer_result != -EINPROGRESS)
+ resource->state = prev_state;
+ ((struct ipa_rm_resource_prod *)
+ resource)->pending_request--;
+ result = consumer_result;
+ break;
+ }
+ default:
+ result = -EPERM;
+ goto bail;
+ }
+bail:
+ spin_unlock_irqrestore(&resource->state_lock, flags);
+ IPADBG("IPA RM ipa_rm_resource_add_dependency name[%d]count[%d]EXIT\n",
+ resource->name, resource->peers_list->peers_count);
+ IPADBG("IPA RM ipa_rm_resource_add_dependency name[%d]count[%d]EXIT\n",
+ depends_on->name, depends_on->peers_list->peers_count);
+ return result;
+}
+
+/**
+ * ipa_rm_resource_delete_dependency() - add dependency between two
+ * given resources
+ * @resource: [in] resource resource
+ * @depends_on: [in] depends_on resource
+ *
+ * Returns: 0 on success, negative on failure
+ * EINPROGRESS is returned in case this is the last dependency
+ * of given resource and IPA RM client should receive the RELEASED cb
+ */
+int ipa_rm_resource_delete_dependency(struct ipa_rm_resource *resource,
+ struct ipa_rm_resource *depends_on)
+{
+ int result = 0;
+ unsigned long flags;
+ if (!resource || !depends_on)
+ return -EINVAL;
+ if (ipa_rm_peers_list_check_dependency(resource->peers_list,
+ resource->name,
+ depends_on->peers_list,
+ depends_on->name))
+ return -EINVAL;
+ spin_lock_irqsave(&resource->state_lock, flags);
+ switch (resource->state) {
+ case IPA_RM_RELEASED:
+ case IPA_RM_GRANTED:
+ break;
+ case IPA_RM_RELEASE_IN_PROGRESS:
+ if (((struct ipa_rm_resource_prod *)
+ resource)->pending_release > 0)
+ ((struct ipa_rm_resource_prod *)
+ resource)->pending_release--;
+ break;
+ case IPA_RM_REQUEST_IN_PROGRESS:
+ if (((struct ipa_rm_resource_prod *)
+ resource)->pending_request > 0)
+ ((struct ipa_rm_resource_prod *)
+ resource)->pending_request--;
+ break;
+ default:
+ result = -EINVAL;
+ spin_unlock_irqrestore(&resource->state_lock, flags);
+ goto bail;
+ }
+ spin_unlock_irqrestore(&resource->state_lock, flags);
+ (void) ipa_rm_resource_consumer_release(
+ (struct ipa_rm_resource_cons *)depends_on);
+ if (ipa_rm_peers_list_has_last_peer(resource->peers_list)) {
+ (void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
+ resource->name,
+ IPA_RM_RESOURCE_RELEASED);
+ result = -EINPROGRESS;
+ }
+ ipa_rm_peers_list_remove_peer(resource->peers_list,
+ depends_on->name);
+ ipa_rm_peers_list_remove_peer(depends_on->peers_list,
+ resource->name);
+bail:
+ return result;
+}
+
+/**
+ * ipa_rm_resource_producer_request() - producer resource request
+ * @producer: [in] producer
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_rm_resource_producer_request(struct ipa_rm_resource_prod *producer)
+{
+ int peers_index;
+ int result = 0;
+ unsigned long flags;
+ struct ipa_rm_resource *consumer;
+ int consumer_result;
+ IPADBG("IPA RM ::ipa_rm_resource_producer_request [%d] ENTER\n",
+ producer->resource.name);
+ if (ipa_rm_peers_list_is_empty(producer->resource.peers_list)) {
+ spin_lock_irqsave(&producer->resource.state_lock, flags);
+ producer->resource.state = IPA_RM_GRANTED;
+ spin_unlock_irqrestore(&producer->resource.state_lock, flags);
+ return 0;
+ }
+ spin_lock_irqsave(&producer->resource.state_lock, flags);
+ IPADBG("IPA RM ::ipa_rm_resource_producer_request state [%d]\n",
+ producer->resource.state);
+ switch (producer->resource.state) {
+ case IPA_RM_RELEASED:
+ case IPA_RM_RELEASE_IN_PROGRESS:
+ producer->resource.state = IPA_RM_REQUEST_IN_PROGRESS;
+ break;
+ case IPA_RM_GRANTED:
+ goto unlock_and_bail;
+ case IPA_RM_REQUEST_IN_PROGRESS:
+ result = -EINPROGRESS;
+ goto unlock_and_bail;
+ default:
+ result = -EINVAL;
+ goto unlock_and_bail;
+ }
+ producer->pending_request = 0;
+ spin_unlock_irqrestore(&producer->resource.state_lock, flags);
+ for (peers_index = 0;
+ peers_index < ipa_rm_peers_list_get_size(
+ producer->resource.peers_list);
+ peers_index++) {
+ consumer = ipa_rm_peers_list_get_resource(peers_index,
+ producer->resource.peers_list);
+ if (consumer) {
+ spin_lock_irqsave(
+ &producer->resource.state_lock, flags);
+ producer->pending_request++;
+ spin_unlock_irqrestore(
+ &producer->resource.state_lock, flags);
+ consumer_result = ipa_rm_resource_consumer_request(
+ (struct ipa_rm_resource_cons *)consumer);
+ if (consumer_result == -EINPROGRESS) {
+ result = -EINPROGRESS;
+ } else {
+ spin_lock_irqsave(
+ &producer->resource.state_lock, flags);
+ producer->pending_request--;
+ spin_unlock_irqrestore(
+ &producer->resource.state_lock, flags);
+ if (consumer_result != 0) {
+ result = consumer_result;
+ goto bail;
+ }
+ }
+ }
+ }
+ spin_lock_irqsave(&producer->resource.state_lock, flags);
+ if (producer->pending_request == 0)
+ producer->resource.state = IPA_RM_GRANTED;
+ spin_unlock_irqrestore(&producer->resource.state_lock, flags);
+ return result;
+unlock_and_bail:
+ spin_unlock_irqrestore(&producer->resource.state_lock, flags);
+bail:
+ IPADBG("IPA RM ::ipa_rm_resource_producer_request EXIT[%d]\n", result);
+ return result;
+}
+
+/**
+ * ipa_rm_resource_producer_release() - producer resource release
+ * producer: [in] producer resource
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ */
+int ipa_rm_resource_producer_release(struct ipa_rm_resource_prod *producer)
+{
+ int peers_index;
+ int result = 0;
+ unsigned long flags;
+ struct ipa_rm_resource *consumer;
+ int consumer_result;
+ IPADBG("IPA RM ::ipa_rm_resource_producer_release ENTER\n");
+ if (ipa_rm_peers_list_is_empty(producer->resource.peers_list)) {
+ spin_lock_irqsave(&producer->resource.state_lock, flags);
+ producer->resource.state = IPA_RM_RELEASED;
+ spin_unlock_irqrestore(&producer->resource.state_lock, flags);
+ return 0;
+ }
+ spin_lock_irqsave(&producer->resource.state_lock, flags);
+ switch (producer->resource.state) {
+ case IPA_RM_RELEASED:
+ goto bail;
+ case IPA_RM_GRANTED:
+ case IPA_RM_REQUEST_IN_PROGRESS:
+ producer->resource.state = IPA_RM_RELEASE_IN_PROGRESS;
+ break;
+ case IPA_RM_RELEASE_IN_PROGRESS:
+ result = -EINPROGRESS;
+ goto bail;
+ default:
+ result = -EPERM;
+ goto bail;
+ }
+ producer->pending_release = 0;
+ spin_unlock_irqrestore(&producer->resource.state_lock, flags);
+ for (peers_index = 0;
+ peers_index < ipa_rm_peers_list_get_size(
+ producer->resource.peers_list);
+ peers_index++) {
+ consumer = ipa_rm_peers_list_get_resource(peers_index,
+ producer->resource.peers_list);
+ if (consumer) {
+ spin_lock_irqsave(
+ &producer->resource.state_lock, flags);
+ producer->pending_release++;
+ spin_unlock_irqrestore(
+ &producer->resource.state_lock, flags);
+ consumer_result = ipa_rm_resource_consumer_release(
+ (struct ipa_rm_resource_cons *)consumer);
+ if (consumer_result == -EINPROGRESS) {
+ result = -EINPROGRESS;
+ } else {
+ spin_lock_irqsave(
+ &producer->resource.state_lock, flags);
+ producer->pending_release--;
+ spin_unlock_irqrestore(
+ &producer->resource.state_lock, flags);
+ }
+ }
+ }
+ spin_lock_irqsave(&producer->resource.state_lock, flags);
+ if (producer->pending_release == 0)
+ producer->resource.state = IPA_RM_RELEASED;
+ spin_unlock_irqrestore(&producer->resource.state_lock, flags);
+ return result;
+bail:
+ spin_unlock_irqrestore(&producer->resource.state_lock, flags);
+ IPADBG("IPA RM ::ipa_rm_resource_producer_release EXIT[%d]\n", result);
+ return result;
+}
+
+static void ipa_rm_resource_producer_handle_cb(
+ struct ipa_rm_resource_prod *producer,
+ enum ipa_rm_event event)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&producer->resource.state_lock, flags);
+ switch (producer->resource.state) {
+ case IPA_RM_REQUEST_IN_PROGRESS:
+ if (event != IPA_RM_RESOURCE_GRANTED)
+ goto unlock_and_bail;
+ if (producer->pending_request > 0) {
+ producer->pending_request--;
+ if (producer->pending_request == 0) {
+ producer->resource.state =
+ IPA_RM_GRANTED;
+ spin_unlock_irqrestore(
+ &producer->resource.state_lock, flags);
+ ipa_rm_resource_producer_notify_clients(
+ producer,
+ IPA_RM_RESOURCE_GRANTED);
+ goto bail;
+ }
+ }
+ break;
+ case IPA_RM_RELEASE_IN_PROGRESS:
+ if (event != IPA_RM_RESOURCE_RELEASED)
+ goto unlock_and_bail;
+ if (producer->pending_release > 0) {
+ producer->pending_release--;
+ if (producer->pending_release == 0) {
+ producer->resource.state =
+ IPA_RM_RELEASED;
+ spin_unlock_irqrestore(
+ &producer->resource.state_lock, flags);
+ ipa_rm_resource_producer_notify_clients(
+ producer,
+ IPA_RM_RESOURCE_RELEASED);
+ goto bail;
+ }
+ }
+ break;
+ case IPA_RM_GRANTED:
+ case IPA_RM_RELEASED:
+ default:
+ goto unlock_and_bail;
+ }
+unlock_and_bail:
+ spin_unlock_irqrestore(&producer->resource.state_lock, flags);
+bail:
+ return;
+}
+
+/**
+ * ipa_rm_resource_consumer_handle_cb() - propagates resource
+ * notification to all dependent producers
+ * @consumer: [in] notifying resource
+ *
+ */
+void ipa_rm_resource_consumer_handle_cb(struct ipa_rm_resource_cons *consumer,
+ enum ipa_rm_event event)
+{
+ int peers_index;
+ struct ipa_rm_resource *producer;
+ unsigned long flags;
+ if (!consumer)
+ return;
+ spin_lock_irqsave(&consumer->resource.state_lock, flags);
+ switch (consumer->resource.state) {
+ case IPA_RM_REQUEST_IN_PROGRESS:
+ if (event == IPA_RM_RESOURCE_RELEASED)
+ goto bail;
+ consumer->resource.state = IPA_RM_GRANTED;
+ break;
+ case IPA_RM_RELEASE_IN_PROGRESS:
+ if (event == IPA_RM_RESOURCE_GRANTED)
+ goto bail;
+ consumer->resource.state = IPA_RM_RELEASED;
+ break;
+ case IPA_RM_GRANTED:
+ case IPA_RM_RELEASED:
+ default:
+ goto bail;
+ }
+ spin_unlock_irqrestore(&consumer->resource.state_lock, flags);
+ for (peers_index = 0;
+ peers_index < ipa_rm_peers_list_get_size(
+ consumer->resource.peers_list);
+ peers_index++) {
+ producer = ipa_rm_peers_list_get_resource(peers_index,
+ consumer->resource.peers_list);
+ if (producer)
+ ipa_rm_resource_producer_handle_cb(
+ (struct ipa_rm_resource_prod *)
+ producer,
+ event);
+ }
+ return;
+bail:
+ spin_unlock_irqrestore(&consumer->resource.state_lock, flags);
+ return;
+}
diff --git a/drivers/platform/msm/ipa/ipa_rm_resource.h b/drivers/platform/msm/ipa/ipa_rm_resource.h
new file mode 100644
index 0000000..b9c2e91
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_rm_resource.h
@@ -0,0 +1,127 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef _IPA_RM_RESOURCE_H_
+#define _IPA_RM_RESOURCE_H_
+
+#include <linux/list.h>
+#include <mach/ipa.h>
+#include "ipa_rm_peers_list.h"
+
+/**
+ * enum ipa_rm_resource_state - resource state
+ */
+enum ipa_rm_resource_state {
+ IPA_RM_RELEASED,
+ IPA_RM_REQUEST_IN_PROGRESS,
+ IPA_RM_GRANTED,
+ IPA_RM_RELEASE_IN_PROGRESS
+};
+
+/**
+ * enum ipa_rm_resource_type - IPA resource manager resource type
+ */
+enum ipa_rm_resource_type {
+ IPA_RM_PRODUCER,
+ IPA_RM_CONSUMER
+};
+
+/**
+ * struct ipa_rm_notification_info - notification information
+ * of IPA RM client
+ * @reg_params: registration parameters
+ * @link: link to the list of all registered clients information
+ */
+struct ipa_rm_notification_info {
+ struct ipa_rm_register_params reg_params;
+ struct list_head link;
+};
+
+/**
+ * struct ipa_rm_resource - IPA RM resource
+ * @name: name identifying resource
+ * @state: state of the resource
+ * @state_lock: lock for all resource state related variables
+ * @peers_list: list of the peers of the resource
+ */
+struct ipa_rm_resource {
+ enum ipa_rm_resource_name name;
+ enum ipa_rm_resource_type type;
+ enum ipa_rm_resource_state state;
+ spinlock_t state_lock;
+ struct ipa_rm_peers_list *peers_list;
+};
+
+/**
+ * struct ipa_rm_resource_cons - IPA RM consumer
+ * @resource: resource
+ * @usage_count: number of producers in GRANTED / REQUESTED state
+ * using this consumer
+ * @request_resource: function which should be called to request resource
+ * from resource manager
+ * @release_resource: function which should be called to release resource
+ * from resource manager
+ * Add new fields after @resource only.
+ */
+struct ipa_rm_resource_cons {
+ struct ipa_rm_resource resource;
+ int usage_count;
+ int (*request_resource)(void);
+ int (*release_resource)(void);
+};
+
+/**
+ * struct ipa_rm_resource_prod - IPA RM producer
+ * @resource: resource
+ * @event_listeners: clients registered with this producer
+ * for notifications in resource state
+ * @event_listeners_lock: RW lock protecting the event listeners list
+ * Add new fields after @resource only.
+ */
+struct ipa_rm_resource_prod {
+ struct ipa_rm_resource resource;
+ struct list_head event_listeners;
+ rwlock_t event_listeners_lock;
+ int pending_request;
+ int pending_release;
+};
+
+int ipa_rm_resource_create(
+ struct ipa_rm_create_params *create_params,
+ struct ipa_rm_resource **resource);
+
+void ipa_rm_resource_delete(struct ipa_rm_resource *resource);
+
+int ipa_rm_resource_producer_register(struct ipa_rm_resource_prod *producer,
+ struct ipa_rm_register_params *reg_params);
+
+int ipa_rm_resource_producer_deregister(struct ipa_rm_resource_prod *producer,
+ struct ipa_rm_register_params *reg_params);
+
+int ipa_rm_resource_add_dependency(struct ipa_rm_resource *resource,
+ struct ipa_rm_resource *depends_on);
+
+int ipa_rm_resource_delete_dependency(struct ipa_rm_resource *resource,
+ struct ipa_rm_resource *depends_on);
+
+int ipa_rm_resource_producer_request(struct ipa_rm_resource_prod *producer);
+
+int ipa_rm_resource_producer_release(struct ipa_rm_resource_prod *producer);
+
+void ipa_rm_resource_consumer_handle_cb(struct ipa_rm_resource_cons *consumer,
+ enum ipa_rm_event event);
+
+void ipa_rm_resource_producer_notify_clients(
+ struct ipa_rm_resource_prod *producer,
+ enum ipa_rm_event event);
+
+#endif /* _IPA_RM_RESOURCE_H_ */
diff --git a/drivers/platform/msm/ssm.c b/drivers/platform/msm/ssm.c
new file mode 100644
index 0000000..c57bb91
--- /dev/null
+++ b/drivers/platform/msm/ssm.c
@@ -0,0 +1,931 @@
+/* Copyright (c) 2013, 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.
+ */
+/*
+ * Qualcomm Secure Service Module(SSM) driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/of.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/ion.h>
+#include <linux/types.h>
+#include <linux/firmware.h>
+#include <linux/elf.h>
+#include <linux/platform_device.h>
+#include <linux/msm_ion.h>
+#include <linux/platform_data/qcom_ssm.h>
+#include <mach/scm.h>
+#include <mach/msm_smd.h>
+
+#include "ssm.h"
+
+/* Macros */
+#define SSM_DEV_NAME "ssm"
+#define MPSS_SUBSYS 0
+#define SSM_INFO_CMD_ID 1
+#define QSEOS_CHECK_VERSION_CMD 0x00001803
+
+#define MAX_APP_NAME_SIZE 32
+#define SSM_MSG_LEN (104 + 4) /* bytes + pad */
+#define SSM_MSG_FIELD_LEN 11
+#define SSM_HEADER_LEN (SSM_MSG_FIELD_LEN * 4)
+#define ATOM_MSG_LEN (SSM_HEADER_LEN + SSM_MSG_LEN)
+#define FIRMWARE_NAME "ssmapp"
+#define TZAPP_NAME "SsmApp"
+#define CHANNEL_NAME "SSM_RTR"
+
+#define ALIGN_BUFFER(size) ((size + 4095) & ~4095)
+
+/* SSM driver structure.*/
+struct ssm_driver {
+ int32_t app_id;
+ int32_t app_status;
+ int32_t update_status;
+ int32_t atom_replay;
+ int32_t mtoa_replay;
+ uint32_t buff_len;
+ unsigned char *channel_name;
+ unsigned char *smd_buffer;
+ struct ion_client *ssm_ion_client;
+ struct ion_handle *ssm_ion_handle;
+ struct tzapp_get_mode_info_rsp *resp;
+ struct device *dev;
+ smd_channel_t *ch;
+ ion_phys_addr_t buff_phys;
+ ion_virt_addr_t buff_virt;
+ dev_t ssm_device_no;
+ struct work_struct ipc_work;
+ struct mutex mutex;
+ bool key_status;
+ bool ready;
+};
+
+static struct ssm_driver *ssm_drv;
+
+static unsigned int getint(char *buff, unsigned long *res)
+{
+ char value[SSM_MSG_FIELD_LEN];
+
+ memcpy(value, buff, SSM_MSG_FIELD_LEN);
+ value[SSM_MSG_FIELD_LEN - 1] = '\0';
+
+ return kstrtoul(skip_spaces(value), 10, res);
+}
+
+/*
+ * Send packet to modem over SMD channel.
+ */
+static int update_modem(enum ssm_ipc_req ipc_req, struct ssm_driver *ssm,
+ int length, char *data)
+{
+ unsigned int packet_len = SSM_HEADER_LEN + length + 1;
+ int rc = 0;
+
+ ssm->atom_replay += 1;
+ snprintf(ssm->smd_buffer, SSM_HEADER_LEN + 1, "%10u|%10u|%10u|%10u|"
+ , packet_len, ssm->atom_replay, ipc_req, length);
+ memcpy(ssm->smd_buffer + SSM_HEADER_LEN, data, length);
+
+ ssm->smd_buffer[packet_len - 1] = '|';
+
+ if (smd_write_avail(ssm->ch) < packet_len) {
+ dev_err(ssm->dev, "Not enough space dropping request\n");
+ rc = -ENOSPC;
+ }
+
+ rc = smd_write(ssm->ch, ssm->smd_buffer, packet_len);
+ if (rc < packet_len) {
+ dev_err(ssm->dev, "smd_write failed for %d\n", ipc_req);
+ rc = -EIO;
+ }
+
+ return rc;
+}
+
+/*
+ * Header Format
+ * Each member of header is of 10 byte (ASCII).
+ * Each entry is separated by '|' delimiter.
+ * |<-10 bytes->|<-10 bytes->|<-10 bytes->|<-10 bytes->|<-10 bytes->|
+ * |-----------------------------------------------------------------
+ * | length | replay no. | request | msg_len | message |
+ * |-----------------------------------------------------------------
+ *
+ */
+static int decode_header(char *buffer, int length,
+ struct ssm_common_msg *pkt)
+{
+ int rc;
+
+ rc = getint(buffer, &pkt->pktlen);
+ if (rc < 0)
+ return -EINVAL;
+
+ buffer += SSM_MSG_FIELD_LEN;
+ rc = getint(buffer, &pkt->replaynum);
+ if (rc < 0)
+ return -EINVAL;
+
+ buffer += SSM_MSG_FIELD_LEN;
+ rc = getint(buffer, (unsigned long *)&pkt->ipc_req);
+ if (rc < 0)
+ return -EINVAL;
+
+ buffer += SSM_MSG_FIELD_LEN;
+ rc = getint(buffer, &pkt->msg_len);
+ if ((rc < 0) || (pkt->msg_len > SSM_MSG_LEN))
+ return -EINVAL;
+
+ pkt->msg = buffer + SSM_MSG_FIELD_LEN;
+
+ dev_dbg(ssm_drv->dev, "len %lu rep %lu req %d msg_len %lu\n",
+ pkt->pktlen, pkt->replaynum, pkt->ipc_req,
+ pkt->msg_len);
+ return 0;
+}
+
+/*
+ * Decode address for storing the decryption key.
+ * Only for Key Exchange
+ * Message Format
+ * |Length@Address|
+ */
+static int decode_message(char *msg, unsigned int len, unsigned long *length,
+ unsigned long *address)
+{
+ int i = 0, rc = 0;
+ char *buff;
+
+ buff = kzalloc(len, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+ while (i < len) {
+ if (msg[i] == '@')
+ break;
+ i++;
+ }
+ if ((i < len) && (msg[i] == '@')) {
+ memcpy(buff, msg, i);
+ buff[i] = '\0';
+ rc = kstrtoul(skip_spaces(buff), 10, length);
+ if (rc || (length <= 0)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ memcpy(buff, &msg[i + 1], len - (i + 1));
+ buff[len - i] = '\0';
+ rc = kstrtoul(skip_spaces(buff), 10, address);
+ } else
+ rc = -EINVAL;
+
+exit:
+ kfree(buff);
+ return rc;
+}
+
+static void process_message(int cmd, char *msg, int len,
+ struct ssm_driver *ssm)
+{
+ int rc;
+ unsigned long key_len = 0, key_add = 0, val;
+ struct ssm_keyexchg_req req;
+
+ switch (cmd) {
+ case SSM_MTOA_KEY_EXCHANGE:
+ if (len < 3) {
+ dev_err(ssm->dev, "Invalid message\n");
+ break;
+ }
+
+ if (ssm->key_status) {
+ dev_err(ssm->dev, "Key exchange already done\n");
+ break;
+ }
+
+ rc = decode_message(msg, len, &key_len, &key_add);
+ if (rc) {
+ rc = update_modem(SSM_ATOM_KEY_STATUS, ssm,
+ 1, "1");
+ break;
+ }
+
+ /*
+ * We are doing key-exchange part here as it is very
+ * specific for this case. For all other tz
+ * communication we have generic function.
+ */
+ req.ssid = MPSS_SUBSYS;
+ req.address = (void *)key_add;
+ req.length = key_len;
+ req.status = (uint32_t *)ssm->buff_phys;
+
+ *(unsigned int *)ssm->buff_virt = -1;
+ rc = scm_call(KEY_EXCHANGE, 0x1, &req,
+ sizeof(struct ssm_keyexchg_req), NULL, 0);
+ if (rc) {
+ dev_err(ssm->dev, "Call for key exchg failed %d", rc);
+ rc = update_modem(SSM_ATOM_KEY_STATUS, ssm,
+ 1, "1");
+ } else {
+ /* Success encode packet and update modem */
+ rc = update_modem(SSM_ATOM_KEY_STATUS, ssm,
+ 1, "0");
+ ssm->key_status = true;
+ }
+ break;
+
+ case SSM_MTOA_MODE_UPDATE_STATUS:
+ msg[len] = '\0';
+ rc = kstrtoul(skip_spaces(msg), 10, &val);
+ if (val) {
+ dev_err(ssm->dev, "Modem mode update failed\n");
+ ssm->update_status = FAILED;
+ } else
+ ssm->update_status = SUCCESS;
+
+ dev_dbg(ssm->dev, "Modem mode update status %lu\n", val);
+ break;
+
+ default:
+ dev_dbg(ssm->dev, "Invalid message\n");
+ break;
+ };
+}
+
+/*
+ * Work function to handle and process packets coming from modem.
+ */
+static void ssm_app_modem_work_fn(struct work_struct *work)
+{
+ int sz, rc;
+ struct ssm_common_msg pkt;
+ struct ssm_driver *ssm;
+
+ ssm = container_of(work, struct ssm_driver, ipc_work);
+
+ mutex_lock(&ssm->mutex);
+ sz = smd_cur_packet_size(ssm->ch);
+ if ((sz <= 0) || (sz > ATOM_MSG_LEN)) {
+ dev_dbg(ssm_drv->dev, "Garbled message size\n");
+ goto unlock;
+ }
+
+ if (smd_read_avail(ssm->ch) < sz) {
+ dev_err(ssm_drv->dev, "SMD error data in channel\n");
+ goto unlock;
+ }
+
+ if (sz < SSM_HEADER_LEN) {
+ dev_err(ssm_drv->dev, "Invalid packet\n");
+ goto unlock;
+ }
+
+ if (smd_read(ssm->ch, ssm->smd_buffer, sz) != sz) {
+ dev_err(ssm_drv->dev, "Incomplete data\n");
+ goto unlock;
+ }
+
+ rc = decode_header(ssm->smd_buffer, sz, &pkt);
+ if (rc < 0) {
+ dev_err(ssm_drv->dev, "Corrupted header\n");
+ goto unlock;
+ }
+
+ /* Check validity of message */
+ if (ssm->mtoa_replay >= (int)pkt.replaynum) {
+ dev_err(ssm_drv->dev, "Replay attack...\n");
+ goto unlock;
+ }
+
+ if (pkt.msg[pkt.msg_len] != '|') {
+ dev_err(ssm_drv->dev, "Garbled message\n");
+ goto unlock;
+ }
+
+ ssm->mtoa_replay = pkt.replaynum;
+ process_message(pkt.ipc_req, pkt.msg, pkt.msg_len, ssm);
+
+unlock:
+ mutex_unlock(&ssm->mutex);
+}
+
+/*
+ * MODEM-APPS smd channel callback function.
+ */
+static void modem_request(void *ctxt, unsigned event)
+{
+ struct ssm_driver *ssm;
+
+ ssm = (struct ssm_driver *)ctxt;
+
+ switch (event) {
+ case SMD_EVENT_OPEN:
+ case SMD_EVENT_CLOSE:
+ dev_info(ssm->dev, "Port %s\n",
+ (event == SMD_EVENT_OPEN) ? "opened" : "closed");
+ break;
+ case SMD_EVENT_DATA:
+ if (smd_read_avail(ssm->ch) > 0)
+ schedule_work(&ssm->ipc_work);
+ break;
+ };
+}
+
+/*
+ * Communication interface between ssm driver and TZ.
+ */
+static int tz_scm_call(struct ssm_driver *ssm, void *tz_req, int tz_req_len,
+ void **tz_resp, int tz_resp_len)
+{
+ int rc;
+ struct common_req req;
+ struct common_resp resp;
+
+ memcpy((void *)ssm->buff_virt, tz_req, tz_req_len);
+
+ req.cmd_id = CLIENT_SEND_DATA_COMMAND;
+ req.app_id = ssm->app_id;
+ req.req_ptr = (void *)ssm->buff_phys;
+ req.req_len = tz_req_len;
+ req.resp_ptr = (void *)(ssm->buff_phys + tz_req_len);
+ req.resp_len = tz_resp_len;
+
+ rc = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &req,
+ sizeof(req), (void *)&resp, sizeof(resp));
+ if (rc) {
+ dev_err(ssm->dev, "SCM call failed for data command\n");
+ return rc;
+ }
+
+ if (resp.result != RESULT_SUCCESS) {
+ dev_err(ssm->dev, "Data command response failure %d\n",
+ resp.result);
+ return -EINVAL;
+ }
+
+ *tz_resp = (void *)(ssm->buff_virt + tz_req_len);
+
+ return rc;
+}
+
+/*
+ * Load SSM application in TZ and start application:
+ * 1. Check if SSM application is already loaded.
+ * 2. Load SSM application firmware.
+ * 3. Start SSM application in TZ.
+ */
+static int ssm_load_app(struct ssm_driver *ssm)
+{
+ unsigned char name[MAX_APP_NAME_SIZE], *pos;
+ int rc, i, fw_count;
+ uint32_t buff_len, size = 0, ion_len;
+ struct check_app_req app_req;
+ struct scm_resp app_resp;
+ struct load_app app_img_info;
+ const struct firmware **fw, *fw_mdt;
+ const struct elf32_hdr *ehdr;
+ const struct elf32_phdr *phdr;
+ struct ion_handle *ion_handle;
+ ion_phys_addr_t buff_phys;
+ ion_virt_addr_t buff_virt;
+
+ /* Check if TZ app already loaded */
+ app_req.cmd_id = APP_LOOKUP_COMMAND;
+ memcpy(app_req.app_name, TZAPP_NAME, MAX_APP_NAME_SIZE);
+
+ rc = scm_call(SCM_SVC_TZSCHEDULER, 1, &app_req,
+ sizeof(struct check_app_req),
+ &app_resp, sizeof(app_resp));
+ if (rc) {
+ dev_err(ssm->dev, "SCM call failed for LOOKUP COMMAND\n");
+ return -EINVAL;
+ }
+
+ if (app_resp.result == RESULT_FAILURE)
+ ssm->app_id = 0;
+ else
+ ssm->app_id = app_resp.data;
+
+ if (ssm->app_id) {
+ rc = 0;
+ dev_info(ssm->dev, "TZAPP already loaded...\n");
+ goto out;
+ }
+
+ /* APP not loaded get the firmware */
+ /* Get .mdt first */
+ rc = request_firmware(&fw_mdt, FIRMWARE_NAME".mdt", ssm->dev);
+ if (rc) {
+ dev_err(ssm->dev, "Unable to get mdt file %s\n",
+ FIRMWARE_NAME".mdt");
+ rc = -EIO;
+ goto out;
+ }
+
+ if (fw_mdt->size < sizeof(*ehdr)) {
+ dev_err(ssm->dev, "Not big enough to be an elf header\n");
+ rc = -EIO;
+ goto release_mdt;
+ }
+
+ ehdr = (struct elf32_hdr *)fw_mdt->data;
+ if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
+ dev_err(ssm->dev, "Not an elf header\n");
+ rc = -EIO;
+ goto release_mdt;
+ }
+
+ if (ehdr->e_phnum == 0) {
+ dev_err(ssm->dev, "No loadable segments\n");
+ rc = -EIO;
+ goto release_mdt;
+ }
+
+ phdr = (const struct elf32_phdr *)(fw_mdt->data +
+ sizeof(struct elf32_hdr));
+
+ fw = kzalloc((sizeof(struct firmware *) * ehdr->e_phnum), GFP_KERNEL);
+ if (!fw) {
+ rc = -ENOMEM;
+ goto release_mdt;
+ }
+
+ /* Valid .mdt now we need to load other parts .b0* */
+ for (fw_count = 0; fw_count < ehdr->e_phnum ; fw_count++) {
+ snprintf(name, MAX_APP_NAME_SIZE, FIRMWARE_NAME".b%02d",
+ fw_count);
+ rc = request_firmware(&fw[fw_count], name, ssm->dev);
+ if (rc < 0) {
+ rc = -EIO;
+ dev_err(ssm->dev, "Unable to get blob file\n");
+ goto release_blob;
+ }
+
+ if (fw[fw_count]->size != phdr->p_filesz) {
+ dev_err(ssm->dev, "Blob size %u doesn't match %u\n",
+ fw[fw_count]->size, phdr->p_filesz);
+ rc = -EIO;
+ goto release_blob;
+ }
+
+ phdr++;
+ size += fw[fw_count]->size;
+ }
+
+ /* Ion allocation for loading tzapp */
+ /* ION buffer size 4k aligned */
+ ion_len = ALIGN_BUFFER(size);
+ ion_handle = ion_alloc(ssm_drv->ssm_ion_client,
+ ion_len, SZ_4K, ION_HEAP(ION_QSECOM_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL(ion_handle)) {
+ rc = PTR_ERR(ion_handle);
+ dev_err(ssm->dev, "Unable to get ion handle\n");
+ goto release_blob;
+ }
+
+ rc = ion_phys(ssm_drv->ssm_ion_client, ion_handle,
+ &buff_phys, &buff_len);
+ if (rc < 0) {
+ dev_err(ssm->dev, "Unable to get ion physical address\n");
+ goto ion_free;
+ }
+
+ if (buff_len < size) {
+ rc = -ENOMEM;
+ goto ion_free;
+ }
+
+ buff_virt =
+ (ion_virt_addr_t)ion_map_kernel(ssm_drv->ssm_ion_client,
+ ion_handle);
+ if (IS_ERR_OR_NULL((void *)buff_virt)) {
+ rc = PTR_ERR((void *)buff_virt);
+ dev_err(ssm->dev, "Unable to get ion virtual address\n");
+ goto ion_free;
+ }
+
+ /* Copy firmware to ION memory */
+ memcpy((unsigned char *)buff_virt, fw_mdt->data, fw_mdt->size);
+ pos = (unsigned char *)buff_virt + fw_mdt->size;
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ memcpy(pos, fw[i]->data, fw[i]->size);
+ pos += fw[i]->size;
+ }
+
+ /* Loading app */
+ app_img_info.cmd_id = APP_START_COMMAND;
+ app_img_info.mdt_len = fw_mdt->size;
+ app_img_info.img_len = size;
+ app_img_info.phy_addr = buff_phys;
+
+ /* SCM call to load the TZ APP */
+ rc = scm_call(SCM_SVC_TZSCHEDULER, 1, &app_img_info,
+ sizeof(struct load_app), &app_resp, sizeof(app_resp));
+ if (rc) {
+ rc = -EIO;
+ dev_err(ssm->dev, "SCM call to load APP failed\n");
+ goto ion_unmap;
+ }
+
+ if (app_resp.result == RESULT_FAILURE) {
+ rc = -EIO;
+ dev_err(ssm->dev, "SCM command to load TzAPP failed\n");
+ goto ion_unmap;
+ }
+
+ ssm->app_id = app_resp.data;
+ ssm->app_status = SUCCESS;
+
+ion_unmap:
+ ion_unmap_kernel(ssm_drv->ssm_ion_client, ion_handle);
+ion_free:
+ ion_free(ssm_drv->ssm_ion_client, ion_handle);
+release_blob:
+ while (--fw_count >= 0)
+ release_firmware(fw[fw_count]);
+ kfree(fw);
+release_mdt:
+ release_firmware(fw_mdt);
+out:
+ return rc;
+}
+
+/*
+ * Allocate buffer for transactions.
+ */
+static int ssm_setup_ion(struct ssm_driver *ssm)
+{
+ int rc = 0;
+ unsigned int size;
+
+ size = ALIGN_BUFFER(ATOM_MSG_LEN);
+
+ /* ION client for communicating with TZ */
+ ssm->ssm_ion_client = msm_ion_client_create(UINT_MAX,
+ "ssm-kernel");
+ if (IS_ERR_OR_NULL(ssm->ssm_ion_client)) {
+ rc = PTR_ERR(ssm->ssm_ion_client);
+ dev_err(ssm->dev, "Ion client not created\n");
+ return rc;
+ }
+
+ /* Setup a small ION buffer for tz communication */
+ ssm->ssm_ion_handle = ion_alloc(ssm->ssm_ion_client,
+ size, SZ_4K, ION_HEAP(ION_QSECOM_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL(ssm->ssm_ion_handle)) {
+ rc = PTR_ERR(ssm->ssm_ion_handle);
+ dev_err(ssm->dev, "Unable to get ion handle\n");
+ goto out;
+ }
+
+ rc = ion_phys(ssm->ssm_ion_client, ssm->ssm_ion_handle,
+ &ssm->buff_phys, &ssm->buff_len);
+ if (rc < 0) {
+ dev_err(ssm->dev,
+ "Unable to get ion buffer physical address\n");
+ goto ion_free;
+ }
+
+ if (ssm->buff_len < size) {
+ rc = -ENOMEM;
+ goto ion_free;
+ }
+
+ ssm->buff_virt =
+ (ion_virt_addr_t)ion_map_kernel(ssm->ssm_ion_client,
+ ssm->ssm_ion_handle);
+ if (IS_ERR_OR_NULL((void *)ssm->buff_virt)) {
+ rc = PTR_ERR((void *)ssm->buff_virt);
+ dev_err(ssm->dev,
+ "Unable to get ion buffer virtual address\n");
+ goto ion_free;
+ }
+
+ return rc;
+
+ion_free:
+ ion_free(ssm->ssm_ion_client, ssm->ssm_ion_handle);
+out:
+ ion_client_destroy(ssm_drv->ssm_ion_client);
+ return rc;
+}
+
+static struct ssm_platform_data *populate_ssm_pdata(struct device *dev)
+{
+ struct ssm_platform_data *pdata;
+ int rc;
+
+ pdata = devm_kzalloc(dev, sizeof(struct ssm_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ pdata->need_key_exchg =
+ of_property_read_bool(dev->of_node, "qcom,need-keyexhg");
+
+ rc = of_property_read_string(dev->of_node, "qcom,channel-name",
+ &pdata->channel_name);
+ if (rc && rc != -EINVAL) {
+ dev_err(dev, "Error reading channel_name property %d\n", rc);
+ return NULL;
+ } else if (rc == -EINVAL)
+ pdata->channel_name = CHANNEL_NAME;
+
+ return pdata;
+}
+
+static int __devinit ssm_probe(struct platform_device *pdev)
+{
+ int rc;
+ uint32_t system_call_id;
+ char legacy = '\0';
+ struct ssm_platform_data *pdata;
+ struct ssm_driver *drv;
+
+ if (pdev->dev.of_node)
+ pdata = populate_ssm_pdata(&pdev->dev);
+ else
+ pdata = pdev->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Empty platform data\n");
+ return -ENOMEM;
+ }
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(struct ssm_driver),
+ GFP_KERNEL);
+ if (!drv) {
+ dev_err(&pdev->dev, "Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize the driver structure */
+ drv->atom_replay = -1;
+ drv->mtoa_replay = -1;
+ drv->app_id = -1;
+ drv->app_status = RETRY;
+ drv->ready = false;
+ drv->update_status = FAILED;
+ mutex_init(&drv->mutex);
+ drv->key_status = !pdata->need_key_exchg;
+ drv->channel_name = (char *)pdata->channel_name;
+ INIT_WORK(&drv->ipc_work, ssm_app_modem_work_fn);
+
+ /* Allocate memory for smd buffer */
+ drv->smd_buffer = devm_kzalloc(&pdev->dev,
+ (sizeof(char) * ATOM_MSG_LEN), GFP_KERNEL);
+ if (!drv->smd_buffer) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ /* Allocate response buffer */
+ drv->resp = devm_kzalloc(&pdev->dev,
+ sizeof(struct tzapp_get_mode_info_rsp),
+ GFP_KERNEL);
+ if (!drv->resp) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+
+ /* Check for TZ version */
+ system_call_id = QSEOS_CHECK_VERSION_CMD;
+ rc = scm_call(SCM_SVC_INFO, SSM_INFO_CMD_ID, &system_call_id,
+ sizeof(system_call_id), &legacy, sizeof(legacy));
+ if (rc) {
+ dev_err(&pdev->dev, "Get version failed %d\n", rc);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* This driver only support 1.4 TZ and QSEOS */
+ if (!legacy) {
+ dev_err(&pdev->dev,
+ "Driver doesn't support legacy version\n");
+ rc = -EINVAL;
+ goto exit;
+
+ }
+
+ /* Setup the ion buffer for transaction */
+ rc = ssm_setup_ion(drv);
+ if (rc < 0)
+ goto exit;
+
+ drv->dev = &pdev->dev;
+ ssm_drv = drv;
+ platform_set_drvdata(pdev, ssm_drv);
+
+ dev_dbg(&pdev->dev, "probe success\n");
+ return 0;
+
+exit:
+ mutex_destroy(&drv->mutex);
+ platform_set_drvdata(pdev, NULL);
+ return rc;
+
+}
+
+static int __devexit ssm_remove(struct platform_device *pdev)
+{
+ int rc;
+
+ struct scm_shutdown_req req;
+ struct scm_resp resp;
+
+ if (!ssm_drv)
+ return 0;
+ /*
+ * Step to exit
+ * 1. set ready to 0 (oem access closed).
+ * 2. Close SMD modem connection closed.
+ * 3. cleanup ion.
+ */
+ ssm_drv->ready = false;
+ smd_close(ssm_drv->ch);
+ flush_work_sync(&ssm_drv->ipc_work);
+
+ /* ION clean up*/
+ ion_unmap_kernel(ssm_drv->ssm_ion_client, ssm_drv->ssm_ion_handle);
+ ion_free(ssm_drv->ssm_ion_client, ssm_drv->ssm_ion_handle);
+ ion_client_destroy(ssm_drv->ssm_ion_client);
+
+ /* Shutdown tzapp */
+ req.app_id = ssm_drv->app_id;
+ req.cmd_id = APP_SHUTDOWN_COMMAND;
+ rc = scm_call(SCM_SVC_TZSCHEDULER, 1, &req, sizeof(req),
+ &resp, sizeof(resp));
+ if (rc)
+ dev_err(&pdev->dev, "TZ_app Unload failed\n");
+
+ return rc;
+}
+
+static struct of_device_id ssm_match_table[] = {
+ {
+ .compatible = "qcom,ssm",
+ },
+ {}
+};
+
+static struct platform_driver ssm_pdriver = {
+ .probe = ssm_probe,
+ .remove = __devexit_p(ssm_remove),
+ .driver = {
+ .name = SSM_DEV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = ssm_match_table,
+ },
+};
+module_platform_driver(ssm_pdriver);
+
+/*
+ * Interface for external OEM driver.
+ * This interface supports following functionalities:
+ * 1. Get TZAPP ID.
+ * 2. Set default mode.
+ * 3. Set mode (encrypted mode and it's length is passed as parameter).
+ * 4. Set mode from TZ.
+ * 5. Get status of mode update.
+ *
+ */
+int ssm_oem_driver_intf(int cmd, char *mode, int len)
+{
+ int rc, req_len, resp_len;
+ struct tzapp_get_mode_info_req get_mode_req;
+ struct tzapp_get_mode_info_rsp *get_mode_resp;
+
+ /* If ssm_drv is NULL, probe failed */
+ if (!ssm_drv)
+ return -ENODEV;
+
+ mutex_lock(&ssm_drv->mutex);
+
+ if (ssm_drv->app_status == RETRY) {
+ /* Load TZAPP */
+ rc = ssm_load_app(ssm_drv);
+ if (rc) {
+ rc = -ENODEV;
+ ssm_drv->app_status = FAILED;
+ goto unlock;
+ }
+ } else if (ssm_drv->app_status == FAILED) {
+ rc = -ENODEV;
+ goto unlock;
+ }
+
+ /* Open modem SMD interface */
+ if (!ssm_drv->ready) {
+ rc = smd_open(ssm_drv->channel_name, &ssm_drv->ch, ssm_drv,
+ modem_request);
+ if (rc) {
+ rc = -EAGAIN;
+ goto unlock;
+ } else
+ ssm_drv->ready = true;
+ }
+
+ /* Try again modem key-exchange not yet done.*/
+ if (!ssm_drv->key_status) {
+ rc = -EAGAIN;
+ goto unlock;
+ }
+
+ /* Set return status to success */
+ rc = 0;
+
+ switch (cmd) {
+ case SSM_READY:
+ break;
+
+ case SSM_GET_APP_ID:
+ rc = ssm_drv->app_id;
+ break;
+
+ case SSM_MODE_INFO_READY:
+ ssm_drv->update_status = RETRY;
+ /* Fill command structure */
+ req_len = sizeof(struct tzapp_get_mode_info_req);
+ resp_len = sizeof(struct tzapp_get_mode_info_rsp);
+ get_mode_req.tzapp_ssm_cmd = GET_ENC_MODE;
+ rc = tz_scm_call(ssm_drv, (void *)&get_mode_req,
+ req_len, (void **)&get_mode_resp, resp_len);
+ if (rc) {
+ ssm_drv->update_status = FAILED;
+ break;
+ }
+
+ /* Send mode_info to modem */
+ rc = update_modem(SSM_ATOM_MODE_UPDATE, ssm_drv,
+ get_mode_resp->enc_mode_len,
+ get_mode_resp->enc_mode_info);
+ if (rc)
+ ssm_drv->update_status = FAILED;
+ break;
+
+ case SSM_SET_MODE:
+ ssm_drv->update_status = RETRY;
+
+ if (len > ENC_MODE_MAX_SIZE) {
+ ssm_drv->update_status = FAILED;
+ rc = -EINVAL;
+ break;
+ }
+ memcpy(ssm_drv->resp->enc_mode_info, mode, len);
+ ssm_drv->resp->enc_mode_len = len;
+
+ /* Send mode_info to modem */
+ rc = update_modem(SSM_ATOM_MODE_UPDATE, ssm_drv,
+ ssm_drv->resp->enc_mode_len,
+ ssm_drv->resp->enc_mode_info);
+ if (rc)
+ ssm_drv->update_status = FAILED;
+ break;
+
+ case SSM_GET_MODE_STATUS:
+ rc = ssm_drv->update_status;
+ break;
+
+ case SSM_SET_DEFAULT_MODE:
+ /* Modem does not send response for this */
+ ssm_drv->update_status = RETRY;
+ rc = update_modem(SSM_ATOM_SET_DEFAULT_MODE, ssm_drv,
+ 1, "0");
+ if (rc)
+ ssm_drv->update_status = FAILED;
+ else
+ /* For default mode we don't get any resp
+ * from modem.
+ */
+ ssm_drv->update_status = SUCCESS;
+ break;
+ default:
+ rc = -EINVAL;
+ dev_err(ssm_drv->dev, "Invalid command\n");
+ break;
+ };
+
+unlock:
+ mutex_unlock(&ssm_drv->mutex);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(ssm_oem_driver_intf);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Qualcomm Secure Service Module");
+
diff --git a/drivers/platform/msm/ssm.h b/drivers/platform/msm/ssm.h
new file mode 100644
index 0000000..97add11
--- /dev/null
+++ b/drivers/platform/msm/ssm.h
@@ -0,0 +1,160 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef __SSM_H_
+#define __SSM_H_
+
+#define MAX_APP_NAME_SIZE 32
+#define MODE_INFO_MAX_SIZE 4
+#define ENC_MODE_MAX_SIZE (100 + MODE_INFO_MAX_SIZE)
+
+/* tzapp response.*/
+enum tz_response {
+ RESULT_SUCCESS = 0,
+ RESULT_FAILURE = 0xFFFFFFFF,
+};
+
+/* tzapp command list.*/
+enum tz_commands {
+ ENC_MODE,
+ GET_ENC_MODE,
+ KEY_EXCHANGE = 11,
+};
+
+/* Command list for QSEOS.*/
+enum qceos_cmd_id {
+ APP_START_COMMAND = 0x01,
+ APP_SHUTDOWN_COMMAND,
+ APP_LOOKUP_COMMAND,
+ CLIENT_SEND_DATA_COMMAND = 0x6,
+ QSEOS_CMD_MAX = 0xEFFFFFFF,
+};
+
+/* MODEM/SSM command list.*/
+enum ssm_ipc_req {
+ SSM_MTOA_KEY_EXCHANGE = 0x0000AAAA,
+ SSM_ATOM_KEY_STATUS,
+ SSM_ATOM_MODE_UPDATE,
+ SSM_MTOA_MODE_UPDATE_STATUS,
+ SSM_MTOA_PREV_INVALID,
+ SSM_ATOM_PREV_INVALID,
+ SSM_ATOM_SET_DEFAULT_MODE,
+ SSM_INVALID_REQ,
+};
+
+/* OEM reuest commands list.*/
+enum oem_req {
+ SSM_READY,
+ SSM_GET_APP_ID,
+ SSM_MODE_INFO_READY,
+ SSM_SET_MODE,
+ SSM_GET_MODE_STATUS,
+ SSM_SET_DEFAULT_MODE,
+ SSM_INVALID,
+};
+
+/* Modem mode update status.*/
+enum modem_mode_status {
+ SUCCESS,
+ RETRY,
+ FAILED = -1,
+};
+
+__packed struct load_app {
+ uint32_t cmd_id;
+ uint32_t mdt_len;
+ uint32_t img_len;
+ uint32_t phy_addr;
+ char app_name[MAX_APP_NAME_SIZE];
+};
+
+/* Stop tzapp reuest.*/
+__packed struct scm_shutdown_req {
+ uint32_t cmd_id;
+ uint32_t app_id;
+};
+
+/* Common tzos response.*/
+__packed struct scm_resp {
+ uint32_t result;
+ enum tz_response resp_type;
+ unsigned int data;
+};
+
+/* tzos request.*/
+__packed struct check_app_req {
+ uint32_t cmd_id;
+ char app_name[MAX_APP_NAME_SIZE];
+};
+
+/* tzapp encode mode reuest.*/
+__packed struct tzapp_mode_enc_req {
+ uint32_t tzapp_ssm_cmd;
+ uint8_t mode_info[4];
+};
+
+/* tzapp encode mode response.*/
+__packed struct tzapp_mode_enc_rsp {
+ uint32_t tzapp_ssm_cmd;
+ uint8_t enc_mode_info[ENC_MODE_MAX_SIZE];
+ uint32_t enc_mode_len;
+ long status;
+};
+
+/* tzapp get mode request.*/
+__packed struct tzapp_get_mode_info_req {
+ uint32_t tzapp_ssm_cmd;
+};
+
+/* tzapp get mode response.*/
+__packed struct tzapp_get_mode_info_rsp {
+ uint32_t tzapp_ssm_cmd;
+ uint8_t enc_mode_info[ENC_MODE_MAX_SIZE];
+ uint32_t enc_mode_len;
+ long status;
+};
+
+/* tzos key exchange request.*/
+__packed struct ssm_keyexchg_req {
+ uint32_t ssid;
+ void *address;
+ uint32_t length;
+ uint32_t *status;
+};
+
+/* tzos common request.*/
+__packed struct common_req {
+ uint32_t cmd_id;
+ uint32_t app_id;
+ void *req_ptr;
+ uint32_t req_len;
+ void *resp_ptr;
+ uint32_t resp_len;
+};
+
+/* tzos common response.*/
+__packed struct common_resp {
+ uint32_t result;
+ uint32_t type;
+ uint32_t data;
+};
+
+/* Modem/SSM packet format.*/
+struct ssm_common_msg {
+ unsigned long pktlen;
+ unsigned long replaynum;
+ enum ssm_ipc_req ipc_req;
+ unsigned long msg_len;
+ char *msg;
+};
+
+#endif
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index 13e23e8..c5b1db4 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -128,7 +128,6 @@
int catch_up_time_us;
enum battery_type batt_type;
uint16_t ocv_reading_at_100;
- int cc_reading_at_100;
int max_voltage_uv;
int chg_term_ua;
@@ -1042,10 +1041,8 @@
}
/* stop faking 100% after an OCV event */
- if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) {
+ if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw)
chip->ocv_reading_at_100 = OCV_RAW_UNINITIALIZED;
- chip->cc_reading_at_100 = 0;
- }
pr_debug("0p625 = %duV\n", chip->xoadc_v0625);
pr_debug("1p25 = %duV\n", chip->xoadc_v125);
pr_debug("last_good_ocv_raw= 0x%x, last_good_ocv_uv= %duV\n",
@@ -1185,10 +1182,7 @@
int64_t cc_voltage_uv, cc_pvh, cc_uah;
cc_voltage_uv = cc;
- cc_voltage_uv -= chip->cc_reading_at_100;
- pr_debug("cc = %d. after subtracting 0x%x cc = %lld\n",
- cc, chip->cc_reading_at_100,
- cc_voltage_uv);
+ pr_debug("cc = %d\n", cc);
cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);
pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
@@ -1513,10 +1507,7 @@
/* calculate cc micro_volt_hour */
calculate_cc_uah(chip, raw->cc, cc_uah);
- pr_debug("cc_uah = %duAh raw->cc = %x cc = %lld after subtracting %x\n",
- *cc_uah, raw->cc,
- (int64_t)raw->cc - chip->cc_reading_at_100,
- chip->cc_reading_at_100);
+ pr_debug("cc_uah = %duAh raw->cc = %x\n", *cc_uah, raw->cc);
soc_rbatt = ((*remaining_charge_uah - *cc_uah) * 100) / *fcc_uah;
if (soc_rbatt < 0)
@@ -2653,19 +2644,20 @@
if (is_battery_full) {
the_chip->ocv_reading_at_100 = raw.last_good_ocv_raw;
- the_chip->cc_reading_at_100 = raw.cc;
the_chip->last_ocv_uv = the_chip->max_voltage_uv;
raw.last_good_ocv_uv = the_chip->max_voltage_uv;
+ raw.cc = 0;
+ /* reset the cc in h/w */
+ reset_cc(the_chip);
the_chip->last_ocv_temp_decidegc = batt_temp;
/*
* since we are treating this as an ocv event
* forget the old cc value
*/
the_chip->last_cc_uah = 0;
- pr_debug("EOC BATT_FULL ocv_reading = 0x%x cc = 0x%x\n",
- the_chip->ocv_reading_at_100,
- the_chip->cc_reading_at_100);
+ pr_debug("EOC BATT_FULL ocv_reading = 0x%x\n",
+ the_chip->ocv_reading_at_100);
}
the_chip->end_percent = calculate_state_of_charge(the_chip, &raw,
diff --git a/drivers/thermal/msm8974-tsens.c b/drivers/thermal/msm8974-tsens.c
index e37b3c4..482d383 100644
--- a/drivers/thermal/msm8974-tsens.c
+++ b/drivers/thermal/msm8974-tsens.c
@@ -63,7 +63,7 @@
#define TSENS_SN_REMOTE_CONFIG(n) ((n) + 0x3c)
#define TSENS_EEPROM(n) ((n) + 0xd0)
-#define TSENS_EEPROM_REDUNDANCY_SEL(n) ((n) + 0x1cc)
+#define TSENS_EEPROM_REDUNDANCY_SEL(n) ((n) + 0x444)
#define TSENS_EEPROM_BACKUP_REGION(n) ((n) + 0x440)
#define TSENS_MAIN_CALIB_ADDR_RANGE 6
diff --git a/drivers/tty/serial/msm_serial_hs_lite.c b/drivers/tty/serial/msm_serial_hs_lite.c
index c9f4199..8069b35 100644
--- a/drivers/tty/serial/msm_serial_hs_lite.c
+++ b/drivers/tty/serial/msm_serial_hs_lite.c
@@ -2,7 +2,7 @@
* drivers/serial/msm_serial.c - driver for msm7k serial device and console
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -1257,6 +1257,9 @@
{
int ret;
struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
+ struct platform_device *pdev = to_platform_device(port->dev);
+ const struct msm_serial_hslite_platform_data *pdata =
+ pdev->dev.platform_data;
switch (state) {
case 0:
@@ -1268,9 +1271,11 @@
break;
case 3:
clk_en(port, 0);
- ret = clk_set_rate(msm_hsl_port->clk, 0);
- if (ret)
- pr_err("Error setting UART clock rate to zero.\n");
+ if (pdata && pdata->set_uart_clk_zero) {
+ ret = clk_set_rate(msm_hsl_port->clk, 0);
+ if (ret)
+ pr_err("Error setting UART clock rate to zero.\n");
+ }
break;
default:
pr_err("Unknown PM state %d\n", state);
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 116b5b0..8e25780 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -2051,6 +2051,7 @@
struct android_configuration *conf;
int enabled = 0;
bool audio_enabled = false;
+ static DEFINE_RATELIMIT_STATE(rl, 10*HZ, 1);
if (!cdev)
return -ENODEV;
@@ -2096,7 +2097,7 @@
f_holder->f->disable(f_holder->f);
}
dev->enabled = false;
- } else {
+ } else if (__ratelimit(&rl)) {
pr_err("android_usb: already %s\n",
dev->enabled ? "enabled" : "disabled");
}
diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c
index a55f0e5..ff2287e 100644
--- a/drivers/usb/gadget/f_adb.c
+++ b/drivers/usb/gadget/f_adb.c
@@ -463,7 +463,10 @@
static int adb_open(struct inode *ip, struct file *fp)
{
- pr_info("adb_open\n");
+ static DEFINE_RATELIMIT_STATE(rl, 10*HZ, 1);
+
+ if (__ratelimit(&rl))
+ pr_info("adb_open\n");
if (!_adb_dev)
return -ENODEV;
@@ -486,7 +489,10 @@
static int adb_release(struct inode *ip, struct file *fp)
{
- pr_info("adb_release\n");
+ static DEFINE_RATELIMIT_STATE(rl, 10*HZ, 1);
+
+ if (__ratelimit(&rl))
+ pr_info("adb_release\n");
/*
* ADB daemon closes the device file after I/O error. The
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 38a3c15..323b481 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -145,29 +145,37 @@
*/
static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
{
- union xhci_trb *next;
unsigned long long addr;
ring->deq_updates++;
- /* If this is not event ring, there is one more usable TRB */
+ /*
+ * If this is not event ring, and the dequeue pointer
+ * is not on a link TRB, there is one more usable TRB
+ */
if (ring->type != TYPE_EVENT &&
!last_trb(xhci, ring, ring->deq_seg, ring->dequeue))
ring->num_trbs_free++;
- next = ++(ring->dequeue);
- /* Update the dequeue pointer further if that was a link TRB or we're at
- * the end of an event ring segment (which doesn't have link TRBS)
- */
- while (last_trb(xhci, ring, ring->deq_seg, next)) {
- if (ring->type == TYPE_EVENT && last_trb_on_last_seg(xhci,
- ring, ring->deq_seg, next)) {
- ring->cycle_state = (ring->cycle_state ? 0 : 1);
+ do {
+ /*
+ * Update the dequeue pointer further if that was a link TRB or
+ * we're at the end of an event ring segment (which doesn't have
+ * link TRBS)
+ */
+ if (last_trb(xhci, ring, ring->deq_seg, ring->dequeue)) {
+ if (ring->type == TYPE_EVENT &&
+ last_trb_on_last_seg(xhci, ring,
+ ring->deq_seg, ring->dequeue)) {
+ ring->cycle_state = (ring->cycle_state ? 0 : 1);
+ }
+ ring->deq_seg = ring->deq_seg->next;
+ ring->dequeue = ring->deq_seg->trbs;
+ } else {
+ ring->dequeue++;
}
- ring->deq_seg = ring->deq_seg->next;
- ring->dequeue = ring->deq_seg->trbs;
- next = ring->dequeue;
- }
+ } while (last_trb(xhci, ring, ring->deq_seg, ring->dequeue));
+
addr = (unsigned long long) xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue);
}
@@ -885,6 +893,17 @@
num_trbs_free_temp = ep_ring->num_trbs_free;
dequeue_temp = ep_ring->dequeue;
+ /* If we get two back-to-back stalls, and the first stalled transfer
+ * ends just before a link TRB, the dequeue pointer will be left on
+ * the link TRB by the code in the while loop. So we have to update
+ * the dequeue pointer one segment further, or we'll jump off
+ * the segment into la-la-land.
+ */
+ if (last_trb(xhci, ep_ring, ep_ring->deq_seg, ep_ring->dequeue)) {
+ ep_ring->deq_seg = ep_ring->deq_seg->next;
+ ep_ring->dequeue = ep_ring->deq_seg->trbs;
+ }
+
while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) {
/* We have more usable TRBs */
ep_ring->num_trbs_free++;
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index 4d6298d..6a41fd4 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -883,7 +883,7 @@
struct mdss_mdp_pipe *pipe;
u32 off, blend_op, blend_stage;
u32 mixercfg = 0, blend_color_out = 0, bgalpha = 0;
- int stage;
+ int stage, secure = 0;
if (!mixer)
return -ENODEV;
@@ -897,6 +897,7 @@
mixercfg = 1 << (3 * pipe->num);
if (pipe->src_fmt->alpha_enable)
bgalpha = 1;
+ secure = pipe->flags & MDP_SECURE_OVERLAY_SESSION;
}
for (stage = MDSS_MDP_STAGE_0; stage < MDSS_MDP_MAX_STAGE; stage++) {
@@ -914,7 +915,8 @@
if (pipe->is_fg) {
bgalpha = 0;
- mixercfg = MDSS_MDP_LM_BORDER_COLOR;
+ if (!secure)
+ mixercfg = MDSS_MDP_LM_BORDER_COLOR;
blend_op = (MDSS_MDP_BLEND_FG_ALPHA_FG_CONST |
MDSS_MDP_BLEND_BG_ALPHA_BG_CONST);
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index 1c5c4b8..d4ffaff 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -221,6 +221,7 @@
#define MDSS_MDP_SCALE_FILTER_CA 0x3
#define MDSS_MDP_SCALEY_EN BIT(1)
#define MDSS_MDP_SCALEX_EN BIT(0)
+#define MDSS_MDP_FMT_SOLID_FILL 0x4037FF
#define MDSS_MDP_NUM_REG_MIXERS 3
#define MDSS_MDP_NUM_WB_MIXERS 2
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index 0a52561..8c88646 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c
@@ -98,6 +98,9 @@
num_blks = DIV_ROUND_UP(2 * ps.ystride[i],
mdss_res->smp_mb_size);
+ if (mdss_res->mdp_rev == MDSS_MDP_HW_REV_100)
+ num_blks = roundup_pow_of_two(num_blks);
+
pr_debug("reserving %d mmb for pnum=%d plane=%d\n",
num_blks, pipe->num, i);
reserved = mdss_mdp_smp_mmb_reserve(&pipe->smp[i], num_blks);
@@ -709,6 +712,28 @@
return 0;
}
+static int mdss_mdp_pipe_solidfill_setup(struct mdss_mdp_pipe *pipe)
+{
+ int ret;
+ u32 secure, format;
+
+ pr_debug("solid fill setup on pnum=%d\n", pipe->num);
+
+ ret = mdss_mdp_image_setup(pipe);
+ if (ret) {
+ pr_err("image setup error for pnum=%d\n", pipe->num);
+ return ret;
+ }
+
+ format = MDSS_MDP_FMT_SOLID_FILL;
+ secure = (pipe->flags & MDP_SECURE_OVERLAY_SESSION ? 0xF : 0x0);
+
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_FORMAT, format);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_ADDR_SW_STATUS, secure);
+
+ return 0;
+}
+
int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe,
struct mdss_mdp_data *src_data)
{
@@ -731,6 +756,11 @@
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
params_changed = pipe->params_changed;
+ if (src_data == NULL) {
+ mdss_mdp_pipe_solidfill_setup(pipe);
+ goto update_nobuf;
+ }
+
if (params_changed) {
pipe->params_changed = 0;
@@ -768,6 +798,7 @@
goto done;
}
+update_nobuf:
mdss_mdp_mixer_pipe_update(pipe, params_changed);
pipe->play_cnt++;
diff --git a/drivers/video/msm/mdss/mdss_mdp_wb.c b/drivers/video/msm/mdss/mdss_mdp_wb.c
index 23efcb8..1d55fa9 100644
--- a/drivers/video/msm/mdss/mdss_mdp_wb.c
+++ b/drivers/video/msm/mdss/mdss_mdp_wb.c
@@ -26,7 +26,6 @@
#include "mdss_mdp.h"
#include "mdss_fb.h"
-#define DEBUG_WRITEBACK
enum mdss_mdp_wb_state {
WB_OPEN,
@@ -43,6 +42,8 @@
struct list_head register_queue;
wait_queue_head_t wait_q;
u32 state;
+ int is_secure;
+ struct mdss_mdp_pipe *secure_pipe;
};
enum mdss_mdp_wb_node_state {
@@ -121,6 +122,72 @@
}
#endif
+int mdss_mdp_wb_set_secure(struct msm_fb_data_type *mfd, int enable)
+{
+ struct mdss_mdp_wb *wb;
+ struct mdss_mdp_pipe *pipe;
+ struct mdss_mdp_mixer *mixer;
+
+ pr_debug("setting secure=%d\n", enable);
+
+ wb = mfd->wb;
+ if (wb == NULL) {
+ pr_err("Invalid writeback session\n");
+ return -ENODEV;
+ }
+
+ wb->is_secure = enable;
+ pipe = wb->secure_pipe;
+
+ if (!enable) {
+ if (pipe) {
+ /* unset pipe */
+ mdss_mdp_mixer_pipe_unstage(pipe);
+ mdss_mdp_pipe_destroy(pipe);
+ wb->secure_pipe = NULL;
+ }
+ return 0;
+ }
+
+ mixer = mdss_mdp_mixer_get(mfd->ctl, MDSS_MDP_MIXER_MUX_DEFAULT);
+ if (!mixer) {
+ pr_err("Unable to find mixer for wb\n");
+ return -ENOENT;
+ }
+
+ if (!pipe) {
+ pipe = mdss_mdp_pipe_alloc(mixer, MDSS_MDP_PIPE_TYPE_RGB);
+ if (!pipe)
+ pipe = mdss_mdp_pipe_alloc(mixer,
+ MDSS_MDP_PIPE_TYPE_VIG);
+ if (!pipe) {
+ pr_err("Unable to get pipe to set secure session\n");
+ return -ENOMEM;
+ }
+
+ pipe->src_fmt = mdss_mdp_get_format_params(MDP_RGBA_8888);
+
+ pipe->mfd = mfd;
+ pipe->mixer_stage = MDSS_MDP_STAGE_BASE;
+ wb->secure_pipe = pipe;
+ }
+
+ pipe->img_height = mixer->height;
+ pipe->img_width = mixer->width;
+ pipe->src.x = 0;
+ pipe->src.y = 0;
+ pipe->src.w = pipe->img_width;
+ pipe->src.h = pipe->img_height;
+ pipe->dst = pipe->src;
+
+ pipe->flags = (enable ? MDP_SECURE_OVERLAY_SESSION : 0);
+ pipe->params_changed++;
+
+ pr_debug("setting secure pipe=%d flags=%x\n", pipe->num, pipe->flags);
+
+ return mdss_mdp_pipe_queue_data(pipe, NULL);
+}
+
static int mdss_mdp_wb_init(struct msm_fb_data_type *mfd)
{
struct mdss_mdp_wb *wb;
@@ -173,6 +240,10 @@
kfree(node);
}
}
+
+ wb->is_secure = false;
+ if (wb->secure_pipe)
+ mdss_mdp_pipe_destroy(wb->secure_pipe);
mutex_unlock(&wb->lock);
mfd->wb = NULL;
@@ -257,6 +328,8 @@
buf = &node->buf_data.p[0];
buf->addr = (u32) (data->iova + data->offset);
buf->len = UINT_MAX; /* trusted source */
+ if (wb->is_secure)
+ buf->flags |= MDP_SECURE_OVERLAY_SESSION;
ret = mdss_mdp_wb_register_node(wb, node);
if (IS_ERR_VALUE(ret)) {
pr_err("error registering wb node\n");
@@ -284,6 +357,8 @@
node->buf_data.num_planes = 1;
buf = &node->buf_data.p[0];
+ if (wb->is_secure)
+ buf->flags |= MDP_SECURE_OVERLAY_SESSION;
ret = mdss_mdp_get_img(data, buf);
if (IS_ERR_VALUE(ret)) {
pr_err("error getting buffer info\n");
@@ -419,6 +494,9 @@
wb = ctl->mfd->wb;
if (wb) {
mutex_lock(&wb->lock);
+ /* in case of reinit of control path need to reset secure */
+ if (ctl->play_cnt == 0)
+ mdss_mdp_wb_set_secure(ctl->mfd, wb->is_secure);
if (!list_empty(&wb->free_queue) && wb->state != WB_STOPING &&
wb->state != WB_STOP) {
node = list_first_entry(&wb->free_queue,
@@ -568,8 +646,31 @@
}
EXPORT_SYMBOL(msm_fb_writeback_terminate);
-int msm_fb_get_iommu_domain(void)
+int msm_fb_get_iommu_domain(struct fb_info *info, int domain)
{
- return mdss_get_iommu_domain(MDSS_IOMMU_DOMAIN_UNSECURE);
+ int mdss_domain;
+ switch (domain) {
+ case MDP_IOMMU_DOMAIN_CP:
+ mdss_domain = MDSS_IOMMU_DOMAIN_SECURE;
+ break;
+ case MDP_IOMMU_DOMAIN_NS:
+ mdss_domain = MDSS_IOMMU_DOMAIN_UNSECURE;
+ break;
+ default:
+ pr_err("Invalid mdp iommu domain (%d)\n", domain);
+ return -EINVAL;
+ }
+ return mdss_get_iommu_domain(mdss_domain);
}
EXPORT_SYMBOL(msm_fb_get_iommu_domain);
+
+int msm_fb_writeback_set_secure(struct fb_info *info, int enable)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *) info->par;
+
+ if (!mfd)
+ return -ENODEV;
+
+ return mdss_mdp_wb_set_secure(mfd, enable);
+}
+EXPORT_SYMBOL(msm_fb_writeback_set_secure);
diff --git a/include/linux/ion.h b/include/linux/ion.h
index f27782f..f159fe2 100644
--- a/include/linux/ion.h
+++ b/include/linux/ion.h
@@ -2,7 +2,7 @@
* include/linux/ion.h
*
* Copyright (C) 2011 Google, Inc.
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -482,6 +482,10 @@
return -ENODEV;
}
+static inline void ion_mark_dangling_buffers_locked(struct ion_device *dev)
+{
+}
+
static inline int msm_ion_do_cache_op(struct ion_client *client,
struct ion_handle *handle, void *vaddr,
unsigned long len, unsigned int cmd)
diff --git a/include/linux/msm_audio_aac.h b/include/linux/msm_audio_aac.h
index ee71c3e..88024d9 100644
--- a/include/linux/msm_audio_aac.h
+++ b/include/linux/msm_audio_aac.h
@@ -14,6 +14,9 @@
#define AUDIO_GET_AAC_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \
(AUDIO_MAX_COMMON_IOCTL_NUM+4), struct msm_audio_aac_enc_config)
+#define AUDIO_SET_AAC_MIX_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \
+(AUDIO_MAX_COMMON_IOCTL_NUM+5), unsigned)
+
#define AUDIO_AAC_FORMAT_ADTS -1
#define AUDIO_AAC_FORMAT_RAW 0x0000
#define AUDIO_AAC_FORMAT_PSUEDO_RAW 0x0001
diff --git a/include/linux/msm_ion.h b/include/linux/msm_ion.h
index c53cb35..2593154 100644
--- a/include/linux/msm_ion.h
+++ b/include/linux/msm_ion.h
@@ -319,19 +319,6 @@
unsigned int length;
};
-/* struct ion_flag_data - information about flags for this buffer
- *
- * @handle: handle to get flags from
- * @flags: flags of this handle
- *
- * Takes handle as an input and outputs the flags from the handle
- * in the flag field.
- */
-struct ion_flag_data {
- struct ion_handle *handle;
- unsigned long flags;
-};
-
#define ION_IOC_MSM_MAGIC 'M'
/**
@@ -356,13 +343,4 @@
#define ION_IOC_CLEAN_INV_CACHES _IOWR(ION_IOC_MSM_MAGIC, 2, \
struct ion_flush_data)
-/**
- * DOC: ION_IOC_GET_FLAGS - get the flags of the handle
- *
- * Gets the flags of the current handle which indicate cachability,
- * secure state etc.
- */
-#define ION_IOC_GET_FLAGS _IOWR(ION_IOC_MSM_MAGIC, 3, \
- struct ion_flag_data)
-
#endif
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index bc35d14..404ea52 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -646,8 +646,13 @@
ROTATOR_SUBSYSTEM_ID,
};
+enum {
+ MDP_IOMMU_DOMAIN_CP,
+ MDP_IOMMU_DOMAIN_NS,
+};
+
#ifdef __KERNEL__
-int msm_fb_get_iommu_domain(void);
+int msm_fb_get_iommu_domain(struct fb_info *info, int domain);
/* get the framebuffer physical address information */
int get_fb_phys_info(unsigned long *start, unsigned long *len, int fb_num,
int subsys_id);
@@ -660,6 +665,7 @@
struct msmfb_data *data);
int msm_fb_writeback_stop(struct fb_info *info);
int msm_fb_writeback_terminate(struct fb_info *info);
+int msm_fb_writeback_set_secure(struct fb_info *info, int enable);
#endif
#endif /*_MSM_MDP_H_*/
diff --git a/include/linux/platform_data/qcom_ssm.h b/include/linux/platform_data/qcom_ssm.h
new file mode 100644
index 0000000..03ac67a
--- /dev/null
+++ b/include/linux/platform_data/qcom_ssm.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef __QCOM_SSM_H_
+#define __QCOM_SSM_H_
+
+struct ssm_platform_data {
+ bool need_key_exchg;
+ const char *channel_name;
+};
+
+#endif /* __QCOM_SSM_H_ */
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index de41c6e..40b0e1e 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -1,6 +1,6 @@
/*
*
- * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2013, 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
@@ -1513,6 +1513,17 @@
struct asm_dual_mono channel_map;
} __packed;
+#define ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG 0x00010DD8
+
+/* Structure for AAC decoder stereo coefficient setting. */
+
+struct asm_aac_stereo_mix_coeff_selection_param {
+ struct apr_hdr hdr;
+ u32 param_id;
+ u32 param_size;
+ u32 aac_stereo_mix_coeff_flag;
+} __packed;
+
#define ASM_ENCDEC_DEC_CHAN_MAP 0x00010D82
struct asm_stream_cmd_encdec_channelmap {
struct apr_hdr hdr;
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index dc30cd6..5744a43 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -249,6 +249,8 @@
int q6asm_cfg_dual_mono_aac(struct audio_client *ac,
uint16_t sce_left, uint16_t sce_right);
+int q6asm_cfg_aac_sel_mix_coef(struct audio_client *ac, uint32_t mix_coeff);
+
int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
uint16_t min_rate, uint16_t max_rate,
uint16_t reduced_rate_level, uint16_t rate_modulation_cmd);
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
index e4757ff..406407d 100644
--- a/include/sound/q6asm.h
+++ b/include/sound/q6asm.h
@@ -273,6 +273,8 @@
int q6asm_cfg_dual_mono_aac(struct audio_client *ac,
uint16_t sce_left, uint16_t sce_right);
+int q6asm_cfg_aac_sel_mix_coef(struct audio_client *ac, uint32_t mix_coeff);
+
int q6asm_set_encdec_chan_map(struct audio_client *ac,
uint32_t num_channels);
diff --git a/sound/soc/msm/msm-pcm-host-voice.c b/sound/soc/msm/msm-pcm-host-voice.c
index 7cb309e3..36826cc 100644
--- a/sound/soc/msm/msm-pcm-host-voice.c
+++ b/sound/soc/msm/msm-pcm-host-voice.c
@@ -28,7 +28,7 @@
#include "qdsp6/q6voice.h"
-#define HPCM_MAX_Q_LEN 2
+#define HPCM_MAX_Q_LEN 10
#define HPCM_MIN_VOC_PKT_SIZE 320
#define HPCM_MAX_VOC_PKT_SIZE 640
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index 35c215c..a55700c 100644
--- a/sound/soc/msm/qdsp6/q6asm.c
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -2205,6 +2205,39 @@
return -EINVAL;
}
+int q6asm_cfg_aac_sel_mix_coef(struct audio_client *ac, uint32_t mix_coeff)
+{
+ struct asm_aac_stereo_mix_coeff_selection_param aac_mix_coeff;
+ int rc = 0;
+ q6asm_add_hdr(ac, &aac_mix_coeff.hdr, sizeof(aac_mix_coeff), TRUE);
+ aac_mix_coeff.hdr.opcode =
+ ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ aac_mix_coeff.param_id =
+ ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG;
+ aac_mix_coeff.param_size =
+ sizeof(struct asm_aac_stereo_mix_coeff_selection_param);
+ aac_mix_coeff.aac_stereo_mix_coeff_flag = mix_coeff;
+ pr_debug("%s, mix_coeff = %u", __func__, mix_coeff);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &aac_mix_coeff);
+ if (rc < 0) {
+ pr_err("%s:Command opcode[0x%x]paramid[0x%x] failed\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+ ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout opcode[0x%x]\n", __func__,
+ aac_mix_coeff.hdr.opcode);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
int q6asm_set_encdec_chan_map(struct audio_client *ac,
uint32_t num_channels)
{
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 87990a9..5be62cc 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -2091,6 +2091,13 @@
return -EINVAL;
}
+/* Support for selecting stereo mixing coefficients for B family not done */
+int q6asm_cfg_aac_sel_mix_coef(struct audio_client *ac, uint32_t mix_coeff)
+{
+ /* To Be Done */
+ return 0;
+}
+
int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
uint16_t min_rate, uint16_t max_rate,
uint16_t reduced_rate_level, uint16_t rate_modulation_cmd)