Merge "ASoC: msm: Add NULL checks before pointer dereferencing"
diff --git a/Documentation/devicetree/bindings/coresight/coresight.txt b/Documentation/devicetree/bindings/coresight/coresight.txt
index 0519aef..c30515c 100644
--- a/Documentation/devicetree/bindings/coresight/coresight.txt
+++ b/Documentation/devicetree/bindings/coresight/coresight.txt
@@ -32,6 +32,7 @@
component
- coresight-child-ports : list of input port numbers of the children
- coresight-default-sink : represents the default compile time CoreSight sink
+- coresight-ctis : list of ctis that this component interacts with
- qcom,pc-save : program counter save implemented
- qcom,blk-size : block size for tmc-etr to usb transfers
- qcom,round-robin : indicates if per core etms are allowed round-robin access
@@ -118,3 +119,24 @@
qcom,pc-save;
qcom,round-robin;
};
+
+4. Miscellaneous
+ cti0: cti@fc308000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc308000 0x1000>;
+ reg-names = "cti0-base";
+
+ coresight-id = <15>;
+ coresight-name = "coresight-cti0";
+ coresight-nr-inports = <0>;
+ };
+
+ cti1: cti@fc309000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc309000 0x1000>;
+ reg-names = "cti1-base";
+
+ coresight-id = <16>;
+ coresight-name = "coresight-cti1";
+ coresight-nr-inports = <0>;
+ };
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-iommu.dtsi b/arch/arm/boot/dts/msm8226-iommu.dtsi
index d23d324..9387bbd 100644
--- a/arch/arm/boot/dts/msm8226-iommu.dtsi
+++ b/arch/arm/boot/dts/msm8226-iommu.dtsi
@@ -28,6 +28,14 @@
qcom,iommu-secure-id = <0xFFFFFFFF>;
};
+&venus_ns {
+ qcom,iommu-ctx-sids = <0 1 2 3 4 5 7>;
+};
+
+&venus_cp {
+ qcom,iommu-ctx-sids = <0x80 0x81 0x82 0x83 0x84>;
+};
+
&kgsl_iommu {
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index b5f2dd0..3533d19 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -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-coresight.dtsi b/arch/arm/boot/dts/msm8974-coresight.dtsi
index 91de30e..5df8f10 100644
--- a/arch/arm/boot/dts/msm8974-coresight.dtsi
+++ b/arch/arm/boot/dts/msm8974-coresight.dtsi
@@ -23,6 +23,7 @@
coresight-id = <0>;
coresight-name = "coresight-tmc-etr";
coresight-nr-inports = <1>;
+ coresight-ctis = <&cti0 &cti8>;
};
tpiu: tpiu@fc318000 {
@@ -60,6 +61,7 @@
coresight-child-list = <&replicator>;
coresight-child-ports = <0>;
coresight-default-sink;
+ coresight-ctis = <&cti0 &cti8>;
};
funnel_merg: funnel@fc31b000 {
@@ -217,4 +219,144 @@
qcom,blk-size = <3>;
};
+
+ cti0: cti@fc308000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc308000 0x1000>;
+ reg-names = "cti0-base";
+
+ coresight-id = <15>;
+ coresight-name = "coresight-cti0";
+ coresight-nr-inports = <0>;
+ };
+
+ cti1: cti@fc309000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc309000 0x1000>;
+ reg-names = "cti1-base";
+
+ coresight-id = <16>;
+ coresight-name = "coresight-cti1";
+ coresight-nr-inports = <0>;
+ };
+
+ cti2: cti@fc30a000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30a000 0x1000>;
+ reg-names = "cti2-base";
+
+ coresight-id = <17>;
+ coresight-name = "coresight-cti2";
+ coresight-nr-inports = <0>;
+ };
+
+ cti3: cti@fc30b000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30b000 0x1000>;
+ reg-names = "cti3-base";
+
+ coresight-id = <18>;
+ coresight-name = "coresight-cti3";
+ coresight-nr-inports = <0>;
+ };
+
+ cti4: cti@fc30c000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30c000 0x1000>;
+ reg-names = "cti4-base";
+
+ coresight-id = <19>;
+ coresight-name = "coresight-cti4";
+ coresight-nr-inports = <0>;
+ };
+
+ cti5: cti@fc30d000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30d000 0x1000>;
+ reg-names = "cti5-base";
+
+ coresight-id = <20>;
+ coresight-name = "coresight-cti5";
+ coresight-nr-inports = <0>;
+ };
+
+ cti6: cti@fc30e000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30e000 0x1000>;
+ reg-names = "cti6-base";
+
+ coresight-id = <21>;
+ coresight-name = "coresight-cti6";
+ coresight-nr-inports = <0>;
+ };
+
+ cti7: cti@fc30f000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30f000 0x1000>;
+ reg-names = "cti7-base";
+
+ coresight-id = <22>;
+ coresight-name = "coresight-cti7";
+ coresight-nr-inports = <0>;
+ };
+
+ cti8: cti@fc310000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc310000 0x1000>;
+ reg-names = "cti8-base";
+
+ coresight-id = <23>;
+ coresight-name = "coresight-cti8";
+ coresight-nr-inports = <0>;
+ };
+
+ cti_l2: cti@fc340000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc340000 0x1000>;
+ reg-names = "cti-l2-base";
+
+ coresight-id = <24>;
+ coresight-name = "coresight-cti-l2";
+ coresight-nr-inports = <0>;
+ };
+
+ cti_cpu0: cti@fc341000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc341000 0x1000>;
+ reg-names = "cti-cpu0-base";
+
+ coresight-id = <25>;
+ coresight-name = "coresight-cti-cpu0";
+ coresight-nr-inports = <0>;
+ };
+
+ cti_cpu1: cti@fc342000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc342000 0x1000>;
+ reg-names = "cti-cpu1-base";
+
+ coresight-id = <26>;
+ coresight-name = "coresight-cti-cpu1";
+ coresight-nr-inports = <0>;
+ };
+
+ cti_cpu2: cti@fc343000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc343000 0x1000>;
+ reg-names = "cti-cpu2-base";
+
+ coresight-id = <27>;
+ coresight-name = "coresight-cti-cpu2";
+ coresight-nr-inports = <0>;
+ };
+
+ cti_cpu3: cti@fc344000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc344000 0x1000>;
+ reg-names = "cti-cpu3-base";
+
+ coresight-id = <28>;
+ coresight-name = "coresight-cti-cpu3";
+ coresight-nr-inports = <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..37b2c45 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 {
@@ -46,6 +45,11 @@
qcom,i2c-src-freq = <19200000>;
};
+/* CoreSight */
+&tmc_etr {
+ qcom,reset-flush-race;
+};
+
&msm_vidc {
qcom,vidc-cp-map = <0x1000000 0x3f000000>;
qcom,vidc-ns-map = <0x40000000 0x40000000>;
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-coresight.dtsi b/arch/arm/boot/dts/msm9625-coresight.dtsi
index 6a52361..0af8fa5 100644
--- a/arch/arm/boot/dts/msm9625-coresight.dtsi
+++ b/arch/arm/boot/dts/msm9625-coresight.dtsi
@@ -23,6 +23,7 @@
coresight-id = <0>;
coresight-name = "coresight-tmc-etr";
coresight-nr-inports = <1>;
+ coresight-ctis = <&cti0 &cti8>;
};
tpiu: tpiu@fc318000 {
@@ -60,6 +61,7 @@
coresight-child-list = <&replicator>;
coresight-child-ports = <0>;
coresight-default-sink;
+ coresight-ctis = <&cti0 &cti8>;
};
funnel_merg: funnel@fc31b000 {
@@ -141,4 +143,104 @@
qcom,blk-size = <1>;
};
+
+ cti0: cti@fc308000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc308000 0x1000>;
+ reg-names = "cti0-base";
+
+ coresight-id = <10>;
+ coresight-name = "coresight-cti0";
+ coresight-nr-inports = <0>;
+ };
+
+ cti1: cti@fc309000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc309000 0x1000>;
+ reg-names = "cti1-base";
+
+ coresight-id = <11>;
+ coresight-name = "coresight-cti1";
+ coresight-nr-inports = <0>;
+ };
+
+ cti2: cti@fc30a000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30a000 0x1000>;
+ reg-names = "cti2-base";
+
+ coresight-id = <12>;
+ coresight-name = "coresight-cti2";
+ coresight-nr-inports = <0>;
+ };
+
+ cti3: cti@fc30b000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30b000 0x1000>;
+ reg-names = "cti3-base";
+
+ coresight-id = <13>;
+ coresight-name = "coresight-cti3";
+ coresight-nr-inports = <0>;
+ };
+
+ cti4: cti@fc30c000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30c000 0x1000>;
+ reg-names = "cti4-base";
+
+ coresight-id = <14>;
+ coresight-name = "coresight-cti4";
+ coresight-nr-inports = <0>;
+ };
+
+ cti5: cti@fc30d000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30d000 0x1000>;
+ reg-names = "cti5-base";
+
+ coresight-id = <15>;
+ coresight-name = "coresight-cti5";
+ coresight-nr-inports = <0>;
+ };
+
+ cti6: cti@fc30e000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30e000 0x1000>;
+ reg-names = "cti6-base";
+
+ coresight-id = <16>;
+ coresight-name = "coresight-cti6";
+ coresight-nr-inports = <0>;
+ };
+
+ cti7: cti@fc30f000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30f000 0x1000>;
+ reg-names = "cti7-base";
+
+ coresight-id = <17>;
+ coresight-name = "coresight-cti7";
+ coresight-nr-inports = <0>;
+ };
+
+ cti8: cti@fc310000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc310000 0x1000>;
+ reg-names = "cti8-base";
+
+ coresight-id = <18>;
+ coresight-name = "coresight-cti8";
+ coresight-nr-inports = <0>;
+ };
+
+ cti_cpu: cti@fc333000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc333000 0x1000>;
+ reg-names = "cti-cpu-base";
+
+ coresight-id = <19>;
+ coresight-name = "coresight-cti-cpu";
+ coresight-nr-inports = <0>;
+ };
};
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/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index 896055d..bc0b939 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -77,6 +77,7 @@
CONFIG_VFP=y
CONFIG_NEON=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_WAKELOCK=y
CONFIG_PM_RUNTIME=y
CONFIG_NET=y
CONFIG_PACKET=y
@@ -180,6 +181,9 @@
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_MMC_MSM_SPS_SUPPORT=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_DRV_MSM is not set
+CONFIG_RTC_DRV_QPNP=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
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/board-8064.c b/arch/arm/mach-msm/board-8064.c
index beb064b..9ed71da 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -2887,6 +2887,7 @@
#ifdef CONFIG_MSM_ROTATOR
&msm_rotator_device,
#endif
+ &msm8064_cpu_slp_status,
};
static struct platform_device
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index 25ba1aa..fbcc6f1 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -2473,6 +2473,7 @@
&msm8930_iommu_domain_device,
&msm_tsens_device,
&msm8930_cache_dump_device,
+ &msm8930_cpu_slp_status,
};
static struct platform_device *cdp_devices[] __initdata = {
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 95f618a..819ccc5 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -2955,6 +2955,7 @@
&msm8960_cache_dump_device,
&msm8960_iommu_domain_device,
&msm_tsens_device,
+ &msm8960_cpu_slp_status,
};
static struct platform_device *cdp_devices[] __initdata = {
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index b7693ae..aee0fd1 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -5323,6 +5323,20 @@
CLK_LOOKUP("core_clk", qdss_clk.c, "fc33d000.etm"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc33e000.etm"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc33f000.etm"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc308000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc309000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30a000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30b000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30c000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30d000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30e000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30f000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc310000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc340000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc341000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc342000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc343000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc344000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc322000.tmc"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc318000.tpiu"),
@@ -5338,6 +5352,20 @@
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33d000.etm"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33e000.etm"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33f000.etm"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc308000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc309000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30a000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30b000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30c000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30d000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30e000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30f000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc310000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc340000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc341000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc342000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc343000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc344000.cti"),
CLK_LOOKUP("l2_m_clk", l2_m_clk, ""),
CLK_LOOKUP("krait0_m_clk", krait0_m_clk, ""),
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
index 2bfb323..06a83b6 100644
--- a/arch/arm/mach-msm/clock-9625.c
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -2271,6 +2271,16 @@
CLK_LOOKUP("core_clk", qdss_clk.c, "fc321000.stm"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc332000.etm"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc332000.jtagmm"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc308000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc309000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30a000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30b000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30c000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30d000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30e000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30f000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc310000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc333000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc322000.tmc"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc318000.tpiu"),
@@ -2282,6 +2292,16 @@
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc321000.stm"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc332000.etm"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc332000.jtagmm"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc308000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc309000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30a000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30b000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30c000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30d000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30e000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30f000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc310000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc333000.cti"),
};
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 10ee1e3..b7707d7 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -41,6 +41,7 @@
#include <mach/msm_rtb.h>
#include <linux/msm_ion.h>
#include "clock.h"
+#include "pm.h"
#include "devices.h"
#include "footswitch.h"
#include "msm_watchdog.h"
@@ -141,6 +142,19 @@
},
};
+static struct msm_pm_sleep_status_data msm_pm_slp_sts_data = {
+ .base_addr = MSM_ACC0_BASE + 0x08,
+ .cpu_offset = MSM_ACC1_BASE - MSM_ACC0_BASE,
+ .mask = 1UL << 13,
+};
+struct platform_device msm8064_cpu_slp_status = {
+ .name = "cpu_slp_status",
+ .id = -1,
+ .dev = {
+ .platform_data = &msm_pm_slp_sts_data,
+ },
+};
+
static struct msm_watchdog_pdata msm_watchdog_pdata = {
.pet_time = 10000,
.bark_time = 11000,
diff --git a/arch/arm/mach-msm/devices-8930.c b/arch/arm/mach-msm/devices-8930.c
index 6fe8ccb..2f8f547 100644
--- a/arch/arm/mach-msm/devices-8930.c
+++ b/arch/arm/mach-msm/devices-8930.c
@@ -53,6 +53,20 @@
.retention_calls_tz = true,
};
+static struct msm_pm_sleep_status_data msm_pm_slp_sts_data = {
+ .base_addr = MSM_ACC0_BASE + 0x08,
+ .cpu_offset = MSM_ACC1_BASE - MSM_ACC0_BASE,
+ .mask = 1UL << 13,
+};
+
+struct platform_device msm8930_cpu_slp_status = {
+ .name = "cpu_slp_status",
+ .id = -1,
+ .dev = {
+ .platform_data = &msm_pm_slp_sts_data,
+ },
+};
+
struct platform_device msm8930_pm_8x60 = {
.name = "pm-8x60",
.id = -1,
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 6a344be..2bd9dfe 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -1703,6 +1703,19 @@
.id = -1,
};
+static struct msm_pm_sleep_status_data msm_pm_slp_sts_data = {
+ .base_addr = MSM_ACC0_BASE + 0x08,
+ .cpu_offset = MSM_ACC1_BASE - MSM_ACC0_BASE,
+ .mask = 1UL << 13,
+};
+struct platform_device msm8960_cpu_slp_status = {
+ .name = "cpu_slp_status",
+ .id = -1,
+ .dev = {
+ .platform_data = &msm_pm_slp_sts_data,
+ },
+};
+
static struct msm_watchdog_pdata msm_watchdog_pdata = {
.pet_time = 10000,
.bark_time = 11000,
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 53eca3e..327c11d 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -124,6 +124,10 @@
extern struct platform_device msm_device_hsusb_host2;
extern struct platform_device msm_device_hsic_host;
+extern struct platform_device msm8960_cpu_slp_status;
+extern struct platform_device msm8064_cpu_slp_status;
+extern struct platform_device msm8930_cpu_slp_status;
+
extern struct platform_device msm_device_otg;
extern struct platform_device msm_android_usb_device;
extern struct platform_device msm_android_usb_hsic_device;
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/memory.c b/arch/arm/mach-msm/memory.c
index 90cb49e..806581d 100644
--- a/arch/arm/mach-msm/memory.c
+++ b/arch/arm/mach-msm/memory.c
@@ -1,7 +1,7 @@
/* arch/arm/mach-msm/memory.c
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-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
@@ -507,11 +507,10 @@
*/
void adjust_meminfo(unsigned long start, unsigned long size)
{
- int i, j;
+ int i;
- for (i = 0, j = 0; i < meminfo.nr_banks; i++) {
- struct membank *bank = &meminfo.bank[j];
- *bank = meminfo.bank[i];
+ for (i = 0; i < meminfo.nr_banks; i++) {
+ struct membank *bank = &meminfo.bank[i];
if (((start + size) <= (bank->start + bank->size)) &&
(start >= bank->start)) {
@@ -519,15 +518,15 @@
(meminfo.nr_banks - i) * sizeof(*bank));
meminfo.nr_banks++;
i++;
- bank[1].size -= (start + size);
- bank[1].start = (start + size);
- bank[1].highmem = 0;
- j++;
+
bank->size = start - bank->start;
+ bank[1].start = (start + size);
+ bank[1].size -= (bank->size + size);
+ bank[1].highmem = 0;
}
- j++;
}
}
+
unsigned long get_ddr_size(void)
{
unsigned int i;
diff --git a/arch/arm/mach-msm/peripheral-loader.c b/arch/arm/mach-msm/peripheral-loader.c
index affb451..fc9a0fa 100644
--- a/arch/arm/mach-msm/peripheral-loader.c
+++ b/arch/arm/mach-msm/peripheral-loader.c
@@ -656,7 +656,8 @@
void pil_shutdown(struct pil_desc *desc)
{
struct pil_priv *priv = desc->priv;
- desc->ops->shutdown(desc);
+ if (desc->ops->shutdown)
+ desc->ops->shutdown(desc);
if (proxy_timeout_ms == 0 && desc->ops->proxy_unvote)
desc->ops->proxy_unvote(desc);
else
diff --git a/arch/arm/mach-msm/pil-q6v5-mss.c b/arch/arm/mach-msm/pil-q6v5-mss.c
index c1d4ab4..cd6aaf4 100644
--- a/arch/arm/mach-msm/pil-q6v5-mss.c
+++ b/arch/arm/mach-msm/pil-q6v5-mss.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -462,7 +462,7 @@
if (!drv->is_loadable)
return 0;
- /* MBA doesn't support shutdown */
+ pil_shutdown(&drv->desc);
pil_shutdown(&drv->q6->desc);
return 0;
}
@@ -578,7 +578,7 @@
if (!drv->is_loadable)
return;
- /* MBA doesn't support shutdown */
+ pil_shutdown(&drv->desc);
pil_shutdown(&drv->q6->desc);
}
diff --git a/arch/arm/mach-msm/pm.h b/arch/arm/mach-msm/pm.h
index af0744c..c77304d 100644
--- a/arch/arm/mach-msm/pm.h
+++ b/arch/arm/mach-msm/pm.h
@@ -65,6 +65,12 @@
uint32_t modified_time_us;
};
+struct msm_pm_sleep_status_data {
+ void *base_addr;
+ uint32_t cpu_offset;
+ uint32_t mask;
+};
+
struct msm_pm_platform_data {
u8 idle_supported; /* Allow device to enter mode during idle */
u8 suspend_supported; /* Allow device to enter mode during suspend */
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/arch/arm/mach-msm/smp2p_debug.c b/arch/arm/mach-msm/smp2p_debug.c
index 1a5c96e..a493cbe 100644
--- a/arch/arm/mach-msm/smp2p_debug.c
+++ b/arch/arm/mach-msm/smp2p_debug.c
@@ -233,7 +233,7 @@
if (in_ptr) {
in_entries = (struct smp2p_entry_v1 *)((void *)in_ptr +
sizeof(struct smp2p_smem));
- in_valid = SMP2P_GET_ENT_VALID(out_ptr->valid_total_ent);
+ in_valid = SMP2P_GET_ENT_VALID(in_ptr->valid_total_ent);
}
for (entry = 0; out_entries || in_entries; ++entry) {
diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c
index 81409b0..5064126 100644
--- a/drivers/base/dma-contiguous.c
+++ b/drivers/base/dma-contiguous.c
@@ -61,8 +61,8 @@
* Users, who want to set the size of global CMA area for their system
* should use cma= kernel parameter.
*/
-static const unsigned long size_bytes = CMA_SIZE_MBYTES * SZ_1M;
-static long size_cmdline = -1;
+static const phys_addr_t size_bytes = CMA_SIZE_MBYTES * SZ_1M;
+static phys_addr_t size_cmdline = -1;
static int __init early_cma(char *p)
{
@@ -74,7 +74,7 @@
#ifdef CONFIG_CMA_SIZE_PERCENTAGE
-static unsigned long __init __maybe_unused cma_early_percent_memory(void)
+static phys_addr_t __init __maybe_unused cma_early_percent_memory(void)
{
struct memblock_region *reg;
unsigned long total_pages = 0;
@@ -92,7 +92,7 @@
#else
-static inline __maybe_unused unsigned long cma_early_percent_memory(void)
+static inline __maybe_unused phys_addr_t cma_early_percent_memory(void)
{
return 0;
}
@@ -110,7 +110,7 @@
*/
void __init dma_contiguous_reserve(phys_addr_t limit)
{
- unsigned long selected_size = 0;
+ phys_addr_t selected_size = 0;
pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit);
@@ -130,7 +130,7 @@
if (selected_size) {
pr_debug("%s: reserving %ld MiB for global area\n", __func__,
- selected_size / SZ_1M);
+ (unsigned long)selected_size / SZ_1M);
dma_declare_contiguous(NULL, selected_size, 0, limit);
}
@@ -231,11 +231,11 @@
* called by board specific code when early allocator (memblock or bootmem)
* is still activate.
*/
-int __init dma_declare_contiguous(struct device *dev, unsigned long size,
+int __init dma_declare_contiguous(struct device *dev, phys_addr_t size,
phys_addr_t base, phys_addr_t limit)
{
struct cma_reserved *r = &cma_reserved[cma_reserved_count];
- unsigned long alignment;
+ phys_addr_t alignment;
pr_debug("%s(size %lx, base %08lx, limit %08lx)\n", __func__,
(unsigned long)size, (unsigned long)base,
@@ -251,7 +251,7 @@
return -EINVAL;
/* Sanitise input arguments */
- alignment = PAGE_SIZE << max(MAX_ORDER, pageblock_order);
+ alignment = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order);
base = ALIGN(base, alignment);
size = ALIGN(size, alignment);
limit &= ~(alignment - 1);
@@ -272,10 +272,6 @@
if (!addr) {
base = -ENOMEM;
goto err;
- } else if (addr + size > ~(unsigned long)0) {
- memblock_free(addr, size);
- base = -EINVAL;
- goto err;
} else {
base = addr;
}
@@ -289,14 +285,14 @@
r->size = size;
r->dev = dev;
cma_reserved_count++;
- pr_info("CMA: reserved %ld MiB at %08lx\n", size / SZ_1M,
+ pr_info("CMA: reserved %ld MiB at %08lx\n", (unsigned long)size / SZ_1M,
(unsigned long)base);
/* Architecture specific contiguous memory fixup. */
dma_contiguous_early_fixup(base, size);
return 0;
err:
- pr_err("CMA: failed to reserve %ld MiB\n", size / SZ_1M);
+ pr_err("CMA: failed to reserve %ld MiB\n", (unsigned long)size / SZ_1M);
return base;
}
@@ -316,6 +312,7 @@
{
unsigned long mask, pfn, pageno, start = 0;
struct cma *cma = dev_get_cma_area(dev);
+ struct page *page = NULL;
int ret;
int tries = 0;
@@ -338,18 +335,17 @@
for (;;) {
pageno = bitmap_find_next_zero_area(cma->bitmap, cma->count,
start, count, mask);
- if (pageno >= cma->count) {
- ret = -ENOMEM;
- goto error;
- }
+ if (pageno >= cma->count)
+ break;
pfn = cma->base_pfn + pageno;
ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA);
if (ret == 0) {
bitmap_set(cma->bitmap, pageno, count);
+ page = pfn_to_page(pfn);
break;
} else if (ret != -EBUSY) {
- goto error;
+ break;
}
tries++;
trace_dma_alloc_contiguous_retry(tries);
@@ -361,12 +357,8 @@
}
mutex_unlock(&cma_mutex);
-
- pr_debug("%s(): returned %p\n", __func__, pfn_to_page(pfn));
- return pfn_to_page(pfn);
-error:
- mutex_unlock(&cma_mutex);
- return NULL;
+ pr_debug("%s(): returned %p\n", __func__, page);
+ return page;
}
/**
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/coresight/Kconfig b/drivers/coresight/Kconfig
index c77df95..5e00570 100644
--- a/drivers/coresight/Kconfig
+++ b/drivers/coresight/Kconfig
@@ -24,6 +24,14 @@
config HAVE_CORESIGHT_SINK
bool
+config CORESIGHT_CTI
+ bool "CoreSight Cross Trigger Interface driver"
+ help
+ This driver provides support for Cross Trigger Interface that is
+ used to input or output i.e. pass cross trigger events from one
+ hardware component to another. It can also be used to pass
+ software generated events.
+
config CORESIGHT_CSR
bool "CoreSight Slave Register driver"
help
@@ -32,6 +40,7 @@
config CORESIGHT_TMC
bool "CoreSight Trace Memory Controller driver"
+ select CORESIGHT_CTI
select CORESIGHT_CSR
select HAVE_CORESIGHT_SINK
help
diff --git a/drivers/coresight/Makefile b/drivers/coresight/Makefile
index 8c73794..0595064 100644
--- a/drivers/coresight/Makefile
+++ b/drivers/coresight/Makefile
@@ -3,6 +3,7 @@
#
obj-$(CONFIG_CORESIGHT) += coresight.o
obj-$(CONFIG_OF) += of_coresight.o
+obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o
obj-$(CONFIG_CORESIGHT_TMC) += coresight-tmc.o
obj-$(CONFIG_CORESIGHT_TPIU) += coresight-tpiu.o
diff --git a/drivers/coresight/coresight-cti.c b/drivers/coresight/coresight-cti.c
new file mode 100644
index 0000000..e077edf
--- /dev/null
+++ b/drivers/coresight/coresight-cti.c
@@ -0,0 +1,481 @@
+/* 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/of_coresight.h>
+#include <linux/coresight.h>
+#include <linux/coresight-cti.h>
+
+#include "coresight-priv.h"
+
+#define cti_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off)
+#define cti_readl(drvdata, off) __raw_readl(drvdata->base + off)
+
+#define CTI_LOCK(drvdata) \
+do { \
+ mb(); \
+ cti_writel(drvdata, 0x0, CORESIGHT_LAR); \
+} while (0)
+#define CTI_UNLOCK(drvdata) \
+do { \
+ cti_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR); \
+ mb(); \
+} while (0)
+
+#define CTICONTROL (0x000)
+#define CTIINTACK (0x010)
+#define CTIAPPSET (0x014)
+#define CTIAPPCLEAR (0x018)
+#define CTIAPPPULSE (0x01C)
+#define CTIINEN(n) (0x020 + (n * 4))
+#define CTIOUTEN(n) (0x0A0 + (n * 4))
+#define CTITRIGINSTATUS (0x130)
+#define CTITRIGOUTSTATUS (0x134)
+#define CTICHINSTATUS (0x138)
+#define CTICHOUTSTATUS (0x13C)
+#define CTIGATE (0x140)
+#define ASICCTL (0x144)
+#define ITCHINACK (0xEDC)
+#define ITTRIGINACK (0xEE0)
+#define ITCHOUT (0xEE4)
+#define ITTRIGOUT (0xEE8)
+#define ITCHOUTACK (0xEEC)
+#define ITTRIGOUTACK (0xEF0)
+#define ITCHIN (0xEF4)
+#define ITTRIGIN (0xEF8)
+
+#define CTI_MAX_TRIGGERS (8)
+#define CTI_MAX_CHANNELS (4)
+
+#define to_cti_drvdata(c) container_of(c, struct cti_drvdata, cti)
+
+struct cti_drvdata {
+ void __iomem *base;
+ struct device *dev;
+ struct coresight_device *csdev;
+ struct clk *clk;
+ struct mutex mutex;
+ struct coresight_cti cti;
+ int refcnt;
+};
+
+static LIST_HEAD(cti_list);
+static DEFINE_MUTEX(cti_lock);
+
+static int cti_verify_bounds(int trig, int ch)
+{
+ if (trig >= CTI_MAX_TRIGGERS)
+ return -EINVAL;
+
+ if (ch >= CTI_MAX_CHANNELS)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int cti_enable(struct cti_drvdata *drvdata)
+{
+ int ret;
+
+ ret = clk_prepare_enable(drvdata->clk);
+ if (ret)
+ return ret;
+
+ CTI_UNLOCK(drvdata);
+
+ cti_writel(drvdata, 0x1, CTICONTROL);
+
+ CTI_LOCK(drvdata);
+ return 0;
+}
+
+static void __cti_map_trigin(struct cti_drvdata *drvdata, int trig, int ch)
+{
+ uint32_t ctien;
+
+ CTI_UNLOCK(drvdata);
+
+ ctien = cti_readl(drvdata, CTIINEN(trig));
+ cti_writel(drvdata, (ctien | 0x1 << ch), CTIINEN(trig));
+
+ CTI_LOCK(drvdata);
+}
+
+int coresight_cti_map_trigin(struct coresight_cti *cti, int trig, int ch)
+{
+ struct cti_drvdata *drvdata;
+ int ret = 0;
+
+ if (IS_ERR_OR_NULL(cti))
+ return -EINVAL;
+
+ ret = cti_verify_bounds(trig, ch);
+ if (ret)
+ return ret;
+
+ drvdata = to_cti_drvdata(cti);
+
+ mutex_lock(&drvdata->mutex);
+ if (drvdata->refcnt == 0) {
+ ret = cti_enable(drvdata);
+ if (ret)
+ goto err;
+ }
+ drvdata->refcnt++;
+
+ __cti_map_trigin(drvdata, trig, ch);
+err:
+ mutex_unlock(&drvdata->mutex);
+ return ret;
+}
+EXPORT_SYMBOL(coresight_cti_map_trigin);
+
+static void __cti_map_trigout(struct cti_drvdata *drvdata, int trig, int ch)
+{
+ uint32_t ctien;
+
+ CTI_UNLOCK(drvdata);
+
+ ctien = cti_readl(drvdata, CTIOUTEN(trig));
+ cti_writel(drvdata, (ctien | 0x1 << ch), CTIOUTEN(trig));
+
+ CTI_LOCK(drvdata);
+}
+
+int coresight_cti_map_trigout(struct coresight_cti *cti, int trig, int ch)
+{
+ struct cti_drvdata *drvdata;
+ int ret = 0;
+
+ if (IS_ERR_OR_NULL(cti))
+ return -EINVAL;
+
+ ret = cti_verify_bounds(trig, ch);
+ if (ret)
+ return ret;
+
+ drvdata = to_cti_drvdata(cti);
+
+ mutex_lock(&drvdata->mutex);
+ if (drvdata->refcnt == 0) {
+ ret = cti_enable(drvdata);
+ if (ret)
+ goto err;
+ }
+ drvdata->refcnt++;
+
+ __cti_map_trigout(drvdata, trig, ch);
+err:
+ mutex_unlock(&drvdata->mutex);
+ return ret;
+}
+EXPORT_SYMBOL(coresight_cti_map_trigout);
+
+static void cti_disable(struct cti_drvdata *drvdata)
+{
+ CTI_UNLOCK(drvdata);
+
+ cti_writel(drvdata, 0x1, CTICONTROL);
+
+ CTI_LOCK(drvdata);
+}
+
+static void __cti_unmap_trigin(struct cti_drvdata *drvdata, int trig, int ch)
+{
+ uint32_t ctien;
+
+ CTI_UNLOCK(drvdata);
+
+ ctien = cti_readl(drvdata, CTIINEN(trig));
+ cti_writel(drvdata, (ctien & ~(0x1 << ch)), CTIINEN(trig));
+
+ CTI_LOCK(drvdata);
+}
+
+void coresight_cti_unmap_trigin(struct coresight_cti *cti, int trig, int ch)
+{
+ struct cti_drvdata *drvdata;
+
+ if (IS_ERR_OR_NULL(cti))
+ return;
+
+ if (cti_verify_bounds(trig, ch))
+ return;
+
+ drvdata = to_cti_drvdata(cti);
+
+ mutex_lock(&drvdata->mutex);
+ __cti_unmap_trigin(drvdata, trig, ch);
+
+ if (drvdata->refcnt == 1)
+ cti_disable(drvdata);
+ drvdata->refcnt--;
+ mutex_unlock(&drvdata->mutex);
+
+ clk_disable_unprepare(drvdata->clk);
+}
+EXPORT_SYMBOL(coresight_cti_unmap_trigin);
+
+static void __cti_unmap_trigout(struct cti_drvdata *drvdata, int trig, int ch)
+{
+ uint32_t ctien;
+
+ CTI_UNLOCK(drvdata);
+
+ ctien = cti_readl(drvdata, CTIOUTEN(trig));
+ cti_writel(drvdata, (ctien & ~(0x1 << ch)), CTIOUTEN(trig));
+
+ CTI_LOCK(drvdata);
+}
+
+void coresight_cti_unmap_trigout(struct coresight_cti *cti, int trig, int ch)
+{
+ struct cti_drvdata *drvdata;
+
+ if (IS_ERR_OR_NULL(cti))
+ return;
+
+ if (cti_verify_bounds(trig, ch))
+ return;
+
+ drvdata = to_cti_drvdata(cti);
+
+ mutex_lock(&drvdata->mutex);
+ __cti_unmap_trigout(drvdata, trig, ch);
+
+ if (drvdata->refcnt == 1)
+ cti_disable(drvdata);
+ drvdata->refcnt--;
+ mutex_unlock(&drvdata->mutex);
+
+ clk_disable_unprepare(drvdata->clk);
+}
+EXPORT_SYMBOL(coresight_cti_unmap_trigout);
+
+struct coresight_cti *coresight_cti_get(const char *name)
+{
+ struct coresight_cti *cti;
+
+ mutex_lock(&cti_lock);
+ list_for_each_entry(cti, &cti_list, link) {
+ if (!strncmp(cti->name, name, strlen(cti->name) + 1)) {
+ mutex_unlock(&cti_lock);
+ return cti;
+ }
+ }
+ mutex_unlock(&cti_lock);
+
+ return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL(coresight_cti_get);
+
+void coresight_cti_put(struct coresight_cti *cti)
+{
+}
+EXPORT_SYMBOL(coresight_cti_put);
+
+static ssize_t cti_store_map_trigin(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val1, val2;
+ int ret;
+
+ if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
+ return -EINVAL;
+
+ ret = coresight_cti_map_trigin(&drvdata->cti, val1, val2);
+
+ if (ret)
+ return ret;
+ return size;
+}
+static DEVICE_ATTR(map_trigin, S_IWUSR, NULL, cti_store_map_trigin);
+
+static ssize_t cti_store_map_trigout(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val1, val2;
+ int ret;
+
+ if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
+ return -EINVAL;
+
+ ret = coresight_cti_map_trigout(&drvdata->cti, val1, val2);
+
+ if (ret)
+ return ret;
+ return size;
+}
+static DEVICE_ATTR(map_trigout, S_IWUSR, NULL, cti_store_map_trigout);
+
+static ssize_t cti_store_unmap_trigin(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val1, val2;
+
+ if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
+ return -EINVAL;
+
+ coresight_cti_unmap_trigin(&drvdata->cti, val1, val2);
+
+ return size;
+}
+static DEVICE_ATTR(unmap_trigin, S_IWUSR, NULL, cti_store_unmap_trigin);
+
+static ssize_t cti_store_unmap_trigout(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val1, val2;
+
+ if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
+ return -EINVAL;
+
+ coresight_cti_unmap_trigout(&drvdata->cti, val1, val2);
+
+ return size;
+}
+static DEVICE_ATTR(unmap_trigout, S_IWUSR, NULL, cti_store_unmap_trigout);
+
+static struct attribute *cti_attrs[] = {
+ &dev_attr_map_trigin.attr,
+ &dev_attr_map_trigout.attr,
+ &dev_attr_unmap_trigin.attr,
+ &dev_attr_unmap_trigout.attr,
+ NULL,
+};
+
+static struct attribute_group cti_attr_grp = {
+ .attrs = cti_attrs,
+};
+
+static const struct attribute_group *cti_attr_grps[] = {
+ &cti_attr_grp,
+ NULL,
+};
+
+static int __devinit cti_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct device *dev = &pdev->dev;
+ struct coresight_platform_data *pdata;
+ struct cti_drvdata *drvdata;
+ struct resource *res;
+ struct coresight_desc *desc;
+
+ if (pdev->dev.of_node) {
+ pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ pdev->dev.platform_data = pdata;
+ }
+
+ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+ /* Store the driver data pointer for use in exported functions */
+ drvdata->dev = &pdev->dev;
+ platform_set_drvdata(pdev, drvdata);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!drvdata->base)
+ return -ENOMEM;
+
+ mutex_init(&drvdata->mutex);
+
+ drvdata->clk = devm_clk_get(dev, "core_clk");
+ if (IS_ERR(drvdata->clk))
+ return PTR_ERR(drvdata->clk);
+
+ ret = clk_set_rate(drvdata->clk, CORESIGHT_CLK_RATE_TRACE);
+ if (ret)
+ return ret;
+
+ mutex_lock(&cti_lock);
+ drvdata->cti.name = ((struct coresight_platform_data *)
+ (pdev->dev.platform_data))->name;
+ list_add_tail(&drvdata->cti.link, &cti_list);
+ mutex_unlock(&cti_lock);
+
+ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+ desc->type = CORESIGHT_DEV_TYPE_NONE;
+ desc->pdata = pdev->dev.platform_data;
+ desc->dev = &pdev->dev;
+ desc->groups = cti_attr_grps;
+ desc->owner = THIS_MODULE;
+ drvdata->csdev = coresight_register(desc);
+ if (IS_ERR(drvdata->csdev))
+ return PTR_ERR(drvdata->csdev);
+
+ dev_info(dev, "CTI initialized\n");
+ return 0;
+}
+
+static int __devexit cti_remove(struct platform_device *pdev)
+{
+ struct cti_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ coresight_unregister(drvdata->csdev);
+ return 0;
+}
+
+static struct of_device_id cti_match[] = {
+ {.compatible = "arm,coresight-cti"},
+ {}
+};
+
+static struct platform_driver cti_driver = {
+ .probe = cti_probe,
+ .remove = __devexit_p(cti_remove),
+ .driver = {
+ .name = "coresight-cti",
+ .owner = THIS_MODULE,
+ .of_match_table = cti_match,
+ },
+};
+
+static int __init cti_init(void)
+{
+ return platform_driver_register(&cti_driver);
+}
+module_init(cti_init);
+
+static void __exit cti_exit(void)
+{
+ platform_driver_unregister(&cti_driver);
+}
+module_exit(cti_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CoreSight CTI driver");
diff --git a/drivers/coresight/coresight-tmc.c b/drivers/coresight/coresight-tmc.c
index 10eabca..d9ab7e4 100644
--- a/drivers/coresight/coresight-tmc.c
+++ b/drivers/coresight/coresight-tmc.c
@@ -29,6 +29,7 @@
#include <linux/of.h>
#include <linux/of_coresight.h>
#include <linux/coresight.h>
+#include <linux/coresight-cti.h>
#include <linux/usb/usb_qdss.h>
#include <mach/memory.h>
#include <mach/sps.h>
@@ -136,6 +137,8 @@
struct miscdevice miscdev;
struct clk *clk;
spinlock_t spinlock;
+ struct coresight_cti *cti_flush;
+ struct coresight_cti *cti_reset;
struct mutex read_lock;
int read_count;
bool reading;
@@ -372,7 +375,7 @@
TMC_UNLOCK(drvdata);
tmc_writel(drvdata, TMC_MODE_CIRCULAR_BUFFER, TMC_MODE);
- tmc_writel(drvdata, 0x133, TMC_FFCR);
+ tmc_writel(drvdata, 0x1133, TMC_FFCR);
tmc_writel(drvdata, drvdata->trigger_cntr, TMC_TRG);
__tmc_enable(drvdata);
@@ -430,7 +433,10 @@
return ret;
mutex_lock(&drvdata->usb_lock);
- if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
+ if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
+ coresight_cti_map_trigout(drvdata->cti_flush, 1, 0);
+ coresight_cti_map_trigin(drvdata->cti_reset, 0, 0);
+ } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) {
drvdata->usbch = usb_qdss_open("qdss", drvdata,
usb_notifier);
@@ -440,6 +446,11 @@
goto err0;
}
}
+ } else {
+ if (mode == TMC_MODE_CIRCULAR_BUFFER) {
+ coresight_cti_map_trigout(drvdata->cti_flush, 1, 0);
+ coresight_cti_map_trigin(drvdata->cti_reset, 0, 0);
+ }
}
spin_lock_irqsave(&drvdata->spinlock, flags);
@@ -632,7 +643,6 @@
static void tmc_disable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
{
unsigned long flags;
- bool etr_bam_disable = false;
mutex_lock(&drvdata->usb_lock);
spin_lock_irqsave(&drvdata->spinlock, flags);
@@ -645,27 +655,28 @@
if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM)
__tmc_etr_disable_to_mem(drvdata);
else if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB)
- etr_bam_disable = true;
+ __tmc_etr_disable_to_bam(drvdata);
} else {
if (mode == TMC_MODE_CIRCULAR_BUFFER)
__tmc_etb_disable(drvdata);
else
__tmc_etf_disable(drvdata);
}
-out:
drvdata->enable = false;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
- if (etr_bam_disable) {
- if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
- if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) {
- spin_lock_irqsave(&drvdata->spinlock, flags);
- __tmc_etr_disable_to_bam(drvdata);
- spin_unlock_irqrestore(&drvdata->spinlock,
- flags);
- tmc_etr_bam_disable(drvdata);
- usb_qdss_close(drvdata->usbch);
- }
+ if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
+ coresight_cti_unmap_trigin(drvdata->cti_reset, 0, 0);
+ coresight_cti_unmap_trigout(drvdata->cti_flush, 1, 0);
+ } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
+ if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) {
+ tmc_etr_bam_disable(drvdata);
+ usb_qdss_close(drvdata->usbch);
+ }
+ } else {
+ if (mode == TMC_MODE_CIRCULAR_BUFFER) {
+ coresight_cti_unmap_trigin(drvdata->cti_reset, 0, 0);
+ coresight_cti_unmap_trigout(drvdata->cti_flush, 1, 0);
}
}
mutex_unlock(&drvdata->usb_lock);
@@ -673,6 +684,15 @@
clk_disable_unprepare(drvdata->clk);
dev_info(drvdata->dev, "TMC disabled\n");
+ return;
+out:
+ drvdata->enable = false;
+ spin_unlock_irqrestore(&drvdata->spinlock, flags);
+ mutex_unlock(&drvdata->usb_lock);
+
+ clk_disable_unprepare(drvdata->clk);
+
+ dev_info(drvdata->dev, "TMC disabled\n");
}
static void tmc_disable_sink(struct coresight_device *csdev)
@@ -1091,6 +1111,7 @@
static int count;
void *baddr;
struct msm_client_dump dump;
+ struct coresight_cti_data *ctidata;
struct coresight_desc *desc;
if (pdev->dev.of_node) {
@@ -1209,6 +1230,23 @@
}
count++;
+ if (pdev->dev.of_node) {
+ ctidata = of_get_coresight_cti_data(dev, pdev->dev.of_node);
+ if (IS_ERR(ctidata)) {
+ dev_err(dev, "invalid cti data\n");
+ } else if (ctidata && ctidata->nr_ctis == 2) {
+ drvdata->cti_flush = coresight_cti_get(
+ ctidata->names[0]);
+ if (IS_ERR(drvdata->cti_flush))
+ dev_err(dev, "failed to get flush cti\n");
+
+ drvdata->cti_reset = coresight_cti_get(
+ ctidata->names[1]);
+ if (IS_ERR(drvdata->cti_reset))
+ dev_err(dev, "failed to get reset cti\n");
+ }
+ }
+
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc) {
ret = -ENOMEM;
diff --git a/drivers/coresight/of_coresight.c b/drivers/coresight/of_coresight.c
index a9d0182..1eccd09 100644
--- a/drivers/coresight/of_coresight.c
+++ b/drivers/coresight/of_coresight.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
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/coresight.h>
+#include <linux/coresight-cti.h>
struct coresight_platform_data *of_get_coresight_platform_data(
struct device *dev, struct device_node *node)
@@ -97,3 +98,45 @@
return pdata;
}
EXPORT_SYMBOL_GPL(of_get_coresight_platform_data);
+
+struct coresight_cti_data *of_get_coresight_cti_data(
+ struct device *dev, struct device_node *node)
+{
+ int i, ret;
+ uint32_t ctis_len;
+ struct device_node *child_node;
+ struct coresight_cti_data *ctidata;
+
+ ctidata = devm_kzalloc(dev, sizeof(*ctidata), GFP_KERNEL);
+ if (!ctidata)
+ return ERR_PTR(-ENOMEM);
+
+ if (of_get_property(node, "coresight-ctis", &ctis_len))
+ ctidata->nr_ctis = ctis_len/sizeof(uint32_t);
+ else
+ return ERR_PTR(-EINVAL);
+
+ if (ctidata->nr_ctis) {
+ ctidata->names = devm_kzalloc(dev, ctidata->nr_ctis *
+ sizeof(*ctidata->names),
+ GFP_KERNEL);
+ if (!ctidata->names)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < ctidata->nr_ctis; i++) {
+ child_node = of_parse_phandle(node, "coresight-ctis",
+ i);
+ if (!child_node)
+ return ERR_PTR(-EINVAL);
+
+ ret = of_property_read_string(child_node,
+ "coresight-name",
+ &ctidata->names[i]);
+ of_node_put(child_node);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+ }
+ return ctidata;
+}
+EXPORT_SYMBOL(of_get_coresight_cti_data);
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 2f0083a..3e3e3e4 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -103,6 +103,34 @@
}
/**
+ * cpuidle_enter_state - enter the state and update stats
+ * @dev: cpuidle device for this cpu
+ * @drv: cpuidle driver for this cpu
+ * @next_state: index into drv->states of the state to enter
+ */
+int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
+ int next_state)
+{
+ int entered_state;
+
+ entered_state = cpuidle_enter_ops(dev, drv, next_state);
+
+ if (entered_state >= 0) {
+ /* Update cpuidle counters */
+ /* This can be moved to within driver enter routine
+ * but that results in multiple copies of same code.
+ */
+ dev->states_usage[entered_state].time +=
+ (unsigned long long)dev->last_residency;
+ dev->states_usage[entered_state].usage++;
+ } else {
+ dev->last_residency = 0;
+ }
+
+ return entered_state;
+}
+
+/**
* cpuidle_idle_call - the main idle loop
*
* NOTE: no locks or semaphores should be used here
@@ -143,23 +171,11 @@
trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu);
trace_cpu_idle_rcuidle(next_state, dev->cpu);
- entered_state = cpuidle_enter_ops(dev, drv, next_state);
+ entered_state = cpuidle_enter_state(dev, drv, next_state);
trace_power_end_rcuidle(dev->cpu);
trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
- if (entered_state >= 0) {
- /* Update cpuidle counters */
- /* This can be moved to within driver enter routine
- * but that results in multiple copies of same code.
- */
- dev->states_usage[entered_state].time +=
- (unsigned long long)dev->last_residency;
- dev->states_usage[entered_state].usage++;
- } else {
- dev->last_residency = 0;
- }
-
/* give the governor an opportunity to reflect on the outcome */
if (cpuidle_curr_governor->reflect)
cpuidle_curr_governor->reflect(dev, entered_state);
diff --git a/drivers/cpuidle/cpuidle.h b/drivers/cpuidle/cpuidle.h
index 7db1866..d8a3ccc 100644
--- a/drivers/cpuidle/cpuidle.h
+++ b/drivers/cpuidle/cpuidle.h
@@ -14,6 +14,8 @@
extern struct mutex cpuidle_lock;
extern spinlock_t cpuidle_driver_lock;
extern int cpuidle_disabled(void);
+extern int cpuidle_enter_state(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int next_state);
/* idle loop */
extern void cpuidle_install_idle_handler(void);
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index bbe97de..a3739a2 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -2764,6 +2764,11 @@
};
static struct a3xx_vbif_data a305b_vbif[] = {
+ { A3XX_VBIF_IN_RD_LIM_CONF0, 0x00181818 },
+ { A3XX_VBIF_IN_WR_LIM_CONF0, 0x00181818 },
+ { A3XX_VBIF_OUT_RD_LIM_CONF0, 0x00000018 },
+ { A3XX_VBIF_OUT_WR_LIM_CONF0, 0x00000018 },
+ { A3XX_VBIF_DDR_OUT_MAX_BURST, 0x00000303 },
{ A3XX_VBIF_ROUND_ROBIN_QOS_ARB, 0x0003 },
{0, 0},
};
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 0c20815..6abfede 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -888,6 +888,17 @@
input_sync(input_dev);
}
+static void mxt_release_all(struct mxt_data *data)
+{
+ int id;
+
+ for (id = 0; id < MXT_MAX_FINGER; id++)
+ if (data->finger[id].status)
+ data->finger[id].status = MXT_RELEASE;
+
+ mxt_input_report(data, 0);
+}
+
static void mxt_input_touchevent(struct mxt_data *data,
struct mxt_message *message, int id)
{
@@ -899,6 +910,10 @@
int area;
int pressure;
+ if (status & MXT_SUPPRESS) {
+ mxt_release_all(data);
+ return;
+ }
/* Check the touch is present on the screen */
if (!(status & MXT_DETECT)) {
if (status & MXT_RELEASE) {
@@ -973,18 +988,7 @@
data->keyarray_old = data->keyarray_new;
}
-static void mxt_release_all(struct mxt_data *data)
-{
- int id;
-
- for (id = 0; id < MXT_MAX_FINGER; id++)
- if (data->finger[id].status)
- data->finger[id].status = MXT_RELEASE;
-
- mxt_input_report(data, 0);
-}
-
-static void mxt_handle_touch_supression(struct mxt_data *data, u8 status)
+static void mxt_handle_touch_suppression(struct mxt_data *data, u8 status)
{
dev_dbg(&data->client->dev, "touch suppression\n");
/* release all touches */
@@ -1039,7 +1043,7 @@
id = reportid - data->t9_min_reportid;
- /* check whether report id is part of T9,T15 or T42*/
+ /* check whether report id is part of T9, T15 or T42 */
if (reportid >= data->t9_min_reportid &&
reportid <= data->t9_max_reportid)
mxt_input_touchevent(data, &message, id);
@@ -1047,8 +1051,9 @@
reportid <= data->t15_max_reportid)
mxt_handle_key_array(data, &message);
else if (reportid >= data->t42_min_reportid &&
- reportid <= data->t42_max_reportid)
- mxt_handle_touch_supression(data, message.message[0]);
+ reportid <= data->t42_max_reportid)
+ mxt_handle_touch_suppression(data,
+ message.message[0]);
else
mxt_dump_message(dev, &message);
} while (reportid != 0xff);
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 63ec1cf..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
@@ -18,7 +18,7 @@
#include "mpq_dmx_plugin_common.h"
#include "mpq_sdmx.h"
-#define SDMX_MAJOR_VERSION_MATCH (2)
+#define SDMX_MAJOR_VERSION_MATCH (3)
#define TS_PACKET_HEADER_LENGTH (4)
@@ -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/dvb/demux/mpq_sdmx.h b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h
index 0bd04e8..d292992 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h
+++ b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h
@@ -43,6 +43,7 @@
/* Filter-level status indicators */
#define SDMX_FILTER_STATUS_EOS BIT(0)
+#define SDMX_FILTER_STATUS_WR_PTR_CHANGED BIT(1)
/* Filter-level flags */
#define SDMX_FILTER_FLAG_VERIFY_SECTION_CRC BIT(0)
@@ -91,10 +92,9 @@
SDMX_STATUS_SINGLE_PID_RAW_FILTER = -11,
SDMX_STATUS_INP_BUF_INVALID_PARAMS = -12,
SDMX_STATUS_INVALID_FILTER_CFG = -13,
- SDMX_STATUS_ILLEGAL_WR_PTR_CHANGE = -14,
- SDMX_STATUS_STALLED_IN_PULL_MODE = -15,
- SDMX_STATUS_SECURITY_FAULT = -16,
- SDMX_STATUS_NS_BUFFER_ERROR = -17,
+ SDMX_STATUS_STALLED_IN_PULL_MODE = -14,
+ SDMX_STATUS_SECURITY_FAULT = -15,
+ SDMX_STATUS_NS_BUFFER_ERROR = -16,
};
enum sdmx_filter {
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 6402437..cee48c7 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -544,7 +544,19 @@
(1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_I_FRAME)
),
.cluster = 0,
- }
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_SECURE,
+ .name = "Secure mode",
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .minimum = 0,
+ .maximum = 0,
+ .default_value = 0,
+ .step = 0,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ .cluster = 0,
+ },
};
#define NUM_CTRLS ARRAY_SIZE(msm_venc_ctrls)
@@ -1434,6 +1446,10 @@
}
pdata = &enable;
break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_SECURE:
+ inst->mode = VIDC_SECURE;
+ dprintk(VIDC_INFO, "Setting secure mode to :%d\n", inst->mode);
+ break;
default:
rc = -ENOTSUPP;
break;
diff --git a/drivers/media/platform/msm/wfd/enc-subdev.h b/drivers/media/platform/msm/wfd/enc-subdev.h
index 93c0079..8bfb884 100644
--- a/drivers/media/platform/msm/wfd/enc-subdev.h
+++ b/drivers/media/platform/msm/wfd/enc-subdev.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
@@ -76,6 +76,8 @@
(a->offset == b->offset);
else if (a->kvaddr || b->kvaddr)
return a->kvaddr == b->kvaddr;
+ else if (a->paddr || b->paddr)
+ return a->paddr == b->paddr;
else
return false;
}
@@ -107,6 +109,7 @@
#define ENC_MMAP _IOWR('V', 25, struct mem_region_map *)
#define ENC_MUNMAP _IOWR('V', 26, struct mem_region_map *)
#define SET_FRAMERATE_MODE _IO('V', 27)
+#define ENC_SECURE _IO('V', 28)
extern int venc_init(struct v4l2_subdev *sd, u32 val);
extern int venc_load_fw(struct v4l2_subdev *sd);
diff --git a/drivers/media/platform/msm/wfd/enc-venus-subdev.c b/drivers/media/platform/msm/wfd/enc-venus-subdev.c
index 00d0d07..d37576d 100644
--- a/drivers/media/platform/msm/wfd/enc-venus-subdev.c
+++ b/drivers/media/platform/msm/wfd/enc-venus-subdev.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
@@ -252,12 +252,23 @@
return msm_vidc_s_ctrl(inst->vidc_context, &ctrl);
}
+static long get_iommu_domain(struct venc_inst *inst)
+{
+ struct msm_vidc_iommu_info maps[MAX_MAP];
+ int rc = msm_vidc_get_iommu_maps(inst->vidc_context, maps);
+ if (rc) {
+ WFD_MSG_ERR("Failed to retreive domain mappings\n");
+ return rc;
+ }
+
+ return maps[inst->secure ? CP_MAP : NS_MAP].domain;
+}
+
static long venc_open(struct v4l2_subdev *sd, void *arg)
{
struct venc_inst *inst = NULL;
struct venc_msg_ops *vmops = arg;
struct v4l2_event_subscription event = {0};
- struct msm_vidc_iommu_info maps[MAX_MAP];
int rc = 0;
if (!vmops) {
@@ -305,15 +316,12 @@
goto vidc_subscribe_fail;
}
- rc = msm_vidc_get_iommu_maps(inst->vidc_context, maps);
- if (rc) {
- WFD_MSG_ERR("Failed to retreive domain mappings\n");
- rc = -ENODATA;
+ inst->domain = get_iommu_domain(inst);
+ if (inst->domain < 0) {
+ WFD_MSG_ERR("Failed to get domain\n");
goto vidc_subscribe_fail;
}
- inst->domain = maps[inst->secure ? CP_MAP : NS_MAP].domain;
-
inst->callback_thread = kthread_run(venc_vidc_callback_thread, inst,
"venc_vidc_callback_thread");
if (IS_ERR(inst->callback_thread)) {
@@ -477,7 +485,8 @@
}
bufreq->count = v4l2_bufreq.count;
- bufreq->size = v4l2_format.fmt.pix_mp.plane_fmt[0].sizeimage;
+ bufreq->size = ALIGN(v4l2_format.fmt.pix_mp.plane_fmt[0].sizeimage,
+ inst->secure ? SZ_1M : SZ_4K);
inst->free_input_indices.size_bits = bufreq->count;
inst->free_input_indices.size = roundup(bufreq->count,
@@ -632,12 +641,19 @@
struct mem_region *mregion)
{
int rc = 0;
- unsigned long flags = 0, size = 0;
+ unsigned long flags = 0, size = 0, align_req = 0;
if (!mregion) {
rc = -EINVAL;
goto venc_map_fail;
}
+ align_req = inst->secure ? SZ_1M : SZ_4K;
+ if (mregion->size % align_req != 0) {
+ WFD_MSG_ERR("Memregion not aligned to %ld\n", align_req);
+ rc = -EINVAL;
+ goto venc_map_fail;
+ }
+
mregion->ion_handle = ion_import_dma_buf(venc_ion_client, mregion->fd);
if (IS_ERR_OR_NULL(mregion->ion_handle)) {
rc = PTR_ERR(mregion->ion_handle);
@@ -653,20 +669,31 @@
goto venc_map_fail;
}
- mregion->kvaddr = ion_map_kernel(venc_ion_client,
+ if (!inst->secure) {
+ mregion->kvaddr = ion_map_kernel(venc_ion_client,
mregion->ion_handle);
-
- if (IS_ERR_OR_NULL(mregion->kvaddr)) {
- WFD_MSG_ERR("Failed to map buffer into kernel\n");
- rc = PTR_ERR(mregion->kvaddr);
+ if (IS_ERR_OR_NULL(mregion->kvaddr)) {
+ WFD_MSG_ERR("Failed to map buffer into kernel\n");
+ rc = PTR_ERR(mregion->kvaddr);
+ mregion->kvaddr = NULL;
+ goto venc_map_fail;
+ }
+ } else {
mregion->kvaddr = NULL;
- goto venc_map_fail;
+ }
+
+ if (inst->secure) {
+ rc = msm_ion_secure_buffer(venc_ion_client,
+ mregion->ion_handle, VIDEO_BITSTREAM, 0);
+ if (rc) {
+ WFD_MSG_ERR("Failed to secure output buffer\n");
+ goto venc_map_iommu_map_fail;
+ }
}
rc = ion_map_iommu(venc_ion_client, mregion->ion_handle,
- inst->domain, 0, SZ_4K, 0,
+ inst->domain, 0, align_req, 0,
(unsigned long *)&mregion->paddr, &size, flags, 0);
-
if (rc) {
WFD_MSG_ERR("Failed to map into iommu\n");
goto venc_map_iommu_map_fail;
@@ -679,8 +706,12 @@
venc_map_iommu_size_fail:
ion_unmap_iommu(venc_ion_client, mregion->ion_handle,
inst->domain, 0);
+
+ if (inst->secure)
+ msm_ion_unsecure_buffer(venc_ion_client, mregion->ion_handle);
venc_map_iommu_map_fail:
- ion_unmap_kernel(venc_ion_client, mregion->ion_handle);
+ if (!inst->secure)
+ ion_unmap_kernel(venc_ion_client, mregion->ion_handle);
venc_map_fail:
return rc;
}
@@ -702,6 +733,8 @@
mregion->kvaddr = NULL;
}
+ if (inst->secure)
+ msm_ion_unsecure_buffer(venc_ion_client, mregion->ion_handle);
return 0;
}
@@ -787,7 +820,7 @@
{
struct venc_inst *inst = NULL;
struct v4l2_format *fmt = arg, temp;
- int rc = 0;
+ int rc = 0, align_req = 0;
if (!sd) {
WFD_MSG_ERR("Subdevice required for %s\n", __func__);
@@ -823,7 +856,10 @@
rc = -EINVAL;
goto venc_set_format_fail;
}
- fmt->fmt.pix.sizeimage = temp.fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ align_req = inst->secure ? SZ_1M : SZ_4K;
+ fmt->fmt.pix.sizeimage = ALIGN(temp.fmt.pix_mp.plane_fmt[0].sizeimage,
+ align_req);
inst->num_output_planes = temp.fmt.pix_mp.num_planes;
temp.type = BUF_TYPE_INPUT;
@@ -995,7 +1031,6 @@
WFD_MSG_ERR("Trying to free a buffer of unknown type\n");
return -EINVAL;
}
-
mregion = get_registered_mregion(buf_list, to_free);
if (!mregion) {
@@ -1115,7 +1150,7 @@
{
struct mem_region_map *mmap = arg;
struct mem_region *mregion = NULL;
- unsigned long rc = 0, size = 0;
+ unsigned long rc = 0, size = 0, align_req = 0;
void *paddr = NULL;
struct venc_inst *inst = NULL;
@@ -1129,24 +1164,47 @@
inst = (struct venc_inst *)sd->dev_priv;
mregion = mmap->mregion;
- if (mregion->size % SZ_4K != 0) {
- WFD_MSG_ERR("Memregion not aligned to %d\n", SZ_4K);
- return -EINVAL;
+
+ align_req = inst->secure ? SZ_1M : SZ_4K;
+ if (mregion->size % align_req != 0) {
+ WFD_MSG_ERR("Memregion not aligned to %ld\n", align_req);
+ rc = -EINVAL;
+ goto venc_map_bad_align;
+ }
+
+ if (inst->secure) {
+ rc = msm_ion_secure_buffer(mmap->ion_client,
+ mregion->ion_handle, VIDEO_PIXEL, 0);
+ if (rc) {
+ WFD_MSG_ERR("Failed to secure input buffer\n");
+ goto venc_map_bad_align;
+ }
}
rc = ion_map_iommu(mmap->ion_client, mregion->ion_handle,
- inst->domain, 0, SZ_4K, 0, (unsigned long *)&paddr,
+ inst->domain, 0, align_req, 0, (unsigned long *)&paddr,
&size, 0, 0);
if (rc) {
- WFD_MSG_ERR("Failed to get physical addr\n");
+ WFD_MSG_ERR("Failed to get physical addr %ld\n", rc);
paddr = NULL;
+ goto venc_map_bad_align;
} else if (size < mregion->size) {
WFD_MSG_ERR("Failed to map enough memory\n");
rc = -ENOMEM;
+ goto venc_map_iommu_size_fail;
}
mregion->paddr = paddr;
+ return 0;
+
+venc_map_iommu_size_fail:
+ ion_unmap_iommu(venc_ion_client, mregion->ion_handle,
+ inst->domain, 0);
+
+ if (inst->secure)
+ msm_ion_unsecure_buffer(mmap->ion_client, mregion->ion_handle);
+venc_map_bad_align:
return rc;
}
@@ -1167,8 +1225,13 @@
inst = (struct venc_inst *)sd->dev_priv;
mregion = mmap->mregion;
- ion_unmap_iommu(mmap->ion_client, mregion->ion_handle,
+ if (mregion->paddr)
+ ion_unmap_iommu(mmap->ion_client, mregion->ion_handle,
inst->domain, 0);
+
+ if (inst->secure)
+ msm_ion_unsecure_buffer(mmap->ion_client, mregion->ion_handle);
+
return 0;
}
@@ -1181,6 +1244,55 @@
return 0;
}
+static long secure_toggle(struct venc_inst *inst, bool secure)
+{
+ if (inst->secure == secure)
+ return 0;
+
+ if (!list_empty(&inst->registered_input_bufs.list) ||
+ !list_empty(&inst->registered_output_bufs.list)) {
+ WFD_MSG_ERR(
+ "Attempt to (un)secure encoder not allowed after registering buffers"
+ );
+ return -EEXIST;
+ }
+
+ inst->secure = secure;
+ inst->domain = get_iommu_domain(inst);
+ return 0;
+}
+
+static long venc_secure(struct v4l2_subdev *sd)
+{
+ struct venc_inst *inst = NULL;
+ struct v4l2_control ctrl;
+ int rc = 0;
+
+ if (!sd) {
+ WFD_MSG_ERR("Subdevice required for %s\n", __func__);
+ return -EINVAL;
+ }
+
+ inst = sd->dev_priv;
+ rc = secure_toggle(inst, true);
+ if (rc) {
+ WFD_MSG_ERR("Failed to toggle into secure mode\n");
+ goto secure_fail;
+ }
+
+ ctrl.id = V4L2_CID_MPEG_VIDC_VIDEO_SECURE;
+ rc = msm_vidc_s_ctrl(inst->vidc_context, &ctrl);
+ if (rc) {
+ WFD_MSG_ERR("Failed to move vidc into secure mode\n");
+ goto secure_fail;
+ }
+
+ return 0;
+secure_fail:
+ secure_toggle(sd->dev_priv, false);
+ return rc;
+}
+
long venc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
long rc = 0;
@@ -1253,6 +1365,9 @@
case SET_FRAMERATE_MODE:
rc = venc_set_framerate_mode(sd, arg);
break;
+ case ENC_SECURE:
+ rc = venc_secure(sd);
+ break;
default:
WFD_MSG_ERR("Unknown ioctl %d to enc-subdev\n", cmd);
rc = -ENOTSUPP;
diff --git a/drivers/media/platform/msm/wfd/mdp-5-subdev.c b/drivers/media/platform/msm/wfd/mdp-5-subdev.c
index 218bbe5..be705df 100644
--- a/drivers/media/platform/msm/wfd/mdp-5-subdev.c
+++ b/drivers/media/platform/msm/wfd/mdp-5-subdev.c
@@ -47,6 +47,10 @@
WFD_MSG_ERR("Invalid arguments\n");
rc = -EINVAL;
goto mdp_open_fail;
+ } else if (mops->secure) {
+ /* Deprecated API; use MDP_SECURE ioctl */
+ WFD_MSG_ERR("Deprecated API for securing subdevice\n");
+ return -ENOTSUPP;
}
fbi = msm_fb_get_writeback_fb();
@@ -120,6 +124,8 @@
struct fb_info *fbi = NULL;
if (inst) {
fbi = (struct fb_info *)inst->mdp;
+ if (inst->secure)
+ msm_fb_writeback_set_secure(inst->mdp, false);
msm_fb_writeback_terminate(fbi);
kfree(inst);
/* Unregister wfd node from switch driver */
@@ -193,7 +199,7 @@
int mdp_mmap(struct v4l2_subdev *sd, void *arg)
{
- int rc = 0;
+ int rc = 0, align = 0;
struct mem_region_map *mmap = arg;
struct mem_region *mregion;
bool domain = -1;
@@ -206,17 +212,39 @@
inst = mmap->cookie;
mregion = mmap->mregion;
- if (mregion->size % SZ_4K != 0) {
- WFD_MSG_ERR("Memregion not aligned to %d\n", SZ_4K);
+ align = inst->secure ? SZ_1M : SZ_4K;
+ if (mregion->size % align != 0) {
+ WFD_MSG_ERR("Memregion not aligned to %d\n", align);
return -EINVAL;
}
- domain = msm_fb_get_iommu_domain(inst->mdp, MDP_IOMMU_DOMAIN_NS);
+ if (inst->secure) {
+ rc = msm_ion_secure_buffer(mmap->ion_client,
+ mregion->ion_handle, VIDEO_PIXEL, 0);
+ if (rc) {
+ WFD_MSG_ERR("Failed to secure input buffer\n");
+ goto secure_fail;
+ }
+ }
+
+ domain = msm_fb_get_iommu_domain(inst->mdp,
+ inst->secure ? MDP_IOMMU_DOMAIN_CP :
+ MDP_IOMMU_DOMAIN_NS);
+
rc = ion_map_iommu(mmap->ion_client, mregion->ion_handle,
- domain, 0, SZ_4K, 0,
+ domain, 0, align, 0,
(unsigned long *)&mregion->paddr,
(unsigned long *)&mregion->size,
0, 0);
+ if (rc) {
+ WFD_MSG_ERR("Failed to map into %ssecure domain: %d\n",
+ !inst->secure ? "non" : "", rc);
+ goto iommu_fail;
+ }
+iommu_fail:
+ if (inst->secure)
+ msm_ion_unsecure_buffer(mmap->ion_client, mregion->ion_handle);
+secure_fail:
return rc;
}
@@ -235,13 +263,37 @@
inst = mmap->cookie;
mregion = mmap->mregion;
- domain = msm_fb_get_iommu_domain(inst->mdp, MDP_IOMMU_DOMAIN_NS);
+ domain = msm_fb_get_iommu_domain(inst->mdp,
+ inst->secure ? MDP_IOMMU_DOMAIN_CP :
+ MDP_IOMMU_DOMAIN_NS);
ion_unmap_iommu(mmap->ion_client,
mregion->ion_handle,
domain, 0);
+
+ if (inst->secure)
+ msm_ion_unsecure_buffer(mmap->ion_client, mregion->ion_handle);
+
return 0;
}
+int mdp_secure(struct v4l2_subdev *sd, void *arg)
+{
+ struct mdp_instance *inst = NULL;
+ int rc = 0;
+
+ if (!arg) {
+ WFD_MSG_ERR("Invalid argument\n");
+ return -EINVAL;
+ }
+
+ inst = arg;
+ rc = msm_fb_writeback_set_secure(inst->mdp, true);
+ if (!rc)
+ inst->secure = true;
+
+ return rc;
+}
+
long mdp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
int rc = 0;
@@ -277,6 +329,9 @@
case MDP_MUNMAP:
rc = mdp_munmap(sd, arg);
break;
+ case MDP_SECURE:
+ rc = mdp_secure(sd, arg);
+ break;
default:
WFD_MSG_ERR("IOCTL: %u not supported\n", cmd);
rc = -EINVAL;
diff --git a/drivers/media/platform/msm/wfd/mdp-dummy-subdev.c b/drivers/media/platform/msm/wfd/mdp-dummy-subdev.c
index b2db208..2242c76 100644
--- a/drivers/media/platform/msm/wfd/mdp-dummy-subdev.c
+++ b/drivers/media/platform/msm/wfd/mdp-dummy-subdev.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
@@ -28,11 +28,12 @@
struct mutex mutex;
};
-int mdp_init(struct v4l2_subdev *sd, u32 val)
+static int mdp_init(struct v4l2_subdev *sd, u32 val)
{
return 0;
}
-int mdp_open(struct v4l2_subdev *sd, void *arg)
+
+static int mdp_open(struct v4l2_subdev *sd, void *arg)
{
struct mdp_instance *inst = kzalloc(sizeof(struct mdp_instance),
GFP_KERNEL);
@@ -50,49 +51,54 @@
return rc;
}
-int mdp_start(struct v4l2_subdev *sd, void *arg)
+static int mdp_start(struct v4l2_subdev *sd, void *arg)
{
return 0;
}
-int mdp_stop(struct v4l2_subdev *sd, void *arg)
+
+static int mdp_stop(struct v4l2_subdev *sd, void *arg)
{
return 0;
}
-int mdp_close(struct v4l2_subdev *sd, void *arg)
+
+static int mdp_close(struct v4l2_subdev *sd, void *arg)
{
return 0;
}
-int mdp_q_buffer(struct v4l2_subdev *sd, void *arg)
+
+static int mdp_q_buffer(struct v4l2_subdev *sd, void *arg)
{
static int foo;
int rc = 0;
struct mdp_buf_info *binfo = arg;
struct mdp_instance *inst = NULL;
+ struct mdp_buf_queue *new_entry = NULL;
if (!binfo || !binfo->inst || !binfo->cookie) {
WFD_MSG_ERR("Invalid argument\n");
return -EINVAL;
}
-
inst = binfo->inst;
- if (binfo->kvaddr) {
- struct mdp_buf_queue *new_entry = kzalloc(sizeof(*new_entry),
- GFP_KERNEL);
- memset((void *)binfo->kvaddr, foo++, 1024);
- new_entry->mdp_buf_info = *binfo;
- mutex_lock(&inst->mutex);
- list_add_tail(&new_entry->node, &inst->mdp_bufs.node);
- mutex_unlock(&inst->mutex);
- WFD_MSG_DBG("Queue %p with cookie %p\n",
- (void *)binfo->paddr, (void *)binfo->cookie);
- } else {
- rc = -EINVAL;
- }
+ new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
+ if (!new_entry)
+ return -ENOMEM;
+ new_entry->mdp_buf_info = *binfo;
+ if (binfo->kvaddr)
+ memset((void *)binfo->kvaddr, foo++, 1024);
+
+
+ mutex_lock(&inst->mutex);
+ list_add_tail(&new_entry->node, &inst->mdp_bufs.node);
+ mutex_unlock(&inst->mutex);
+
+ WFD_MSG_DBG("Queue %p with cookie %p\n",
+ (void *)binfo->paddr, (void *)binfo->cookie);
return rc;
}
-int mdp_dq_buffer(struct v4l2_subdev *sd, void *arg)
+
+static int mdp_dq_buffer(struct v4l2_subdev *sd, void *arg)
{
struct mdp_buf_info *binfo = arg;
struct mdp_buf_queue *head = NULL;
@@ -121,12 +127,13 @@
return 0;
}
-int mdp_set_prop(struct v4l2_subdev *sd, void *arg)
+
+static int mdp_set_prop(struct v4l2_subdev *sd, void *arg)
{
return 0;
}
-int mdp_mmap(struct v4l2_subdev *sd, void *arg)
+static int mdp_mmap(struct v4l2_subdev *sd, void *arg)
{
int rc = 0;
struct mem_region_map *mmap = arg;
@@ -137,12 +144,17 @@
return rc;
}
-int mdp_munmap(struct v4l2_subdev *sd, void *arg)
+static int mdp_munmap(struct v4l2_subdev *sd, void *arg)
{
/* Whatever */
return 0;
}
+static int mdp_secure(struct v4l2_subdev *sd)
+{
+ return 0;
+}
+
long mdp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
int rc = 0;
@@ -178,6 +190,9 @@
case MDP_MUNMAP:
rc = mdp_munmap(sd, arg);
break;
+ case MDP_SECURE:
+ rc = mdp_secure(sd);
+ break;
default:
WFD_MSG_ERR("IOCTL: %u not supported\n", cmd);
rc = -EINVAL;
diff --git a/drivers/media/platform/msm/wfd/mdp-subdev.h b/drivers/media/platform/msm/wfd/mdp-subdev.h
index b04d448..f2c6fb1 100644
--- a/drivers/media/platform/msm/wfd/mdp-subdev.h
+++ b/drivers/media/platform/msm/wfd/mdp-subdev.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
@@ -59,6 +59,7 @@
#define MDP_STOP _IOR(MDP_MAGIC_IOCTL, 7, void *)
#define MDP_MMAP _IOR(MDP_MAGIC_IOCTL, 8, struct mem_region_map *)
#define MDP_MUNMAP _IOR(MDP_MAGIC_IOCTL, 9, struct mem_region_map *)
+#define MDP_SECURE _IO(MDP_MAGIC_IOCTL, 9)
extern int mdp_init(struct v4l2_subdev *sd, u32 val);
diff --git a/drivers/media/platform/msm/wfd/wfd-ioctl.c b/drivers/media/platform/msm/wfd/wfd-ioctl.c
index 3b732ae..9fb7c6d 100644
--- a/drivers/media/platform/msm/wfd/wfd-ioctl.c
+++ b/drivers/media/platform/msm/wfd/wfd-ioctl.c
@@ -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
@@ -52,7 +52,7 @@
struct v4l2_subdev enc_sdev;
struct v4l2_subdev vsg_sdev;
struct ion_client *ion_client;
- bool secure_device;
+ bool secure;
bool in_use;
bool mdp_iommu_split_domain;
};
@@ -154,16 +154,16 @@
{
struct ion_handle *handle = NULL;
void *kvaddr = NULL;
- unsigned int alloc_regions = 0;
- unsigned int ion_flags = 0;
+ unsigned int alloc_regions = 0, ion_flags = 0, align = 0;
int rc = 0;
alloc_regions = ION_HEAP(ION_CP_MM_HEAP_ID);
alloc_regions |= secure ? 0 :
ION_HEAP(ION_IOMMU_HEAP_ID);
ion_flags |= secure ? ION_SECURE : 0;
- handle = ion_alloc(client,
- mregion->size, SZ_4K, alloc_regions, ion_flags);
+ align = secure ? SZ_1M : SZ_4K;
+ handle = ion_alloc(client, mregion->size, align,
+ alloc_regions, ion_flags);
if (IS_ERR_OR_NULL(handle)) {
WFD_MSG_ERR("Failed to allocate input buffer\n");
@@ -171,12 +171,16 @@
goto alloc_fail;
}
- kvaddr = ion_map_kernel(client, handle);
+ if (!secure) {
+ kvaddr = ion_map_kernel(client, handle);
- if (IS_ERR_OR_NULL(kvaddr)) {
- WFD_MSG_ERR("Failed to get virtual addr\n");
- rc = PTR_ERR(kvaddr);
- goto alloc_fail;
+ if (IS_ERR_OR_NULL(kvaddr)) {
+ WFD_MSG_ERR("Failed to get virtual addr\n");
+ rc = PTR_ERR(kvaddr);
+ goto alloc_fail;
+ }
+ } else {
+ kvaddr = NULL;
}
mregion->kvaddr = kvaddr;
@@ -206,7 +210,8 @@
"Invalid client or region");
return -EINVAL;
}
- ion_unmap_kernel(client, mregion->ion_handle);
+ if (mregion->kvaddr)
+ ion_unmap_kernel(client, mregion->ion_handle);
ion_free(client, mregion->ion_handle);
return 0;
}
@@ -256,7 +261,7 @@
enc_mregion->size = ALIGN(inst->input_buf_size, SZ_4K);
rc = wfd_allocate_ion_buffer(wfd_dev->ion_client,
- wfd_dev->secure_device, enc_mregion);
+ wfd_dev->secure, enc_mregion);
if (rc) {
WFD_MSG_ERR("Failed to allocate input memory\n");
goto alloc_fail;
@@ -391,6 +396,7 @@
&inst->input_mem_list) {
mpair = list_entry(ptr, struct mem_region_pair,
list);
+
rc = v4l2_subdev_call(&wfd_dev->enc_sdev,
core, ioctl, FREE_INPUT_BUFFER,
(void *)mpair->enc);
@@ -1004,8 +1010,31 @@
{
int rc = 0;
struct wfd_device *wfd_dev = video_drvdata(filp);
- rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core,
- ioctl, SET_PROP, a);
+ struct wfd_inst *inst = filp->private_data;
+
+ switch (a->id) {
+ case V4L2_CID_MPEG_VIDC_VIDEO_SECURE:
+ rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core,
+ ioctl, ENC_SECURE, NULL);
+ if (rc) {
+ WFD_MSG_ERR("Couldn't secure encoder");
+ break;
+ }
+
+ rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core,
+ ioctl, MDP_SECURE, (void *)inst->mdp_inst);
+ if (rc) {
+ WFD_MSG_ERR("Couldn't secure MDP");
+ break;
+ }
+
+ wfd_dev->secure = true;
+ break;
+ default:
+ rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core,
+ ioctl, SET_PROP, a);
+ }
+
if (rc)
WFD_MSG_ERR("Failed to set encoder property\n");
return rc;
@@ -1355,7 +1384,7 @@
wfd_stats_init(&inst->stats, MINOR(filp->f_dentry->d_inode->i_rdev));
- mdp_mops.secure = wfd_dev->secure_device;
+ mdp_mops.secure = wfd_dev->secure;
mdp_mops.iommu_split_domain = wfd_dev->mdp_iommu_split_domain;
rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl, MDP_OPEN,
(void *)&mdp_mops);
@@ -1373,7 +1402,7 @@
enc_mops.op_buffer_done = venc_op_buffer_done;
enc_mops.ip_buffer_done = venc_ip_buffer_done;
enc_mops.cbdata = filp;
- enc_mops.secure = wfd_dev->secure_device;
+ enc_mops.secure = wfd_dev->secure;
rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl, OPEN,
(void *)&enc_mops);
if (rc || !enc_mops.cookie) {
@@ -1421,22 +1450,21 @@
inst = filp->private_data;
if (inst) {
wfdioc_streamoff(filp, NULL, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ vb2_queue_release(&inst->vid_bufq);
+ wfd_free_input_buffers(wfd_dev, inst);
+
rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl,
MDP_CLOSE, (void *)inst->mdp_inst);
if (rc)
WFD_MSG_ERR("Failed to CLOSE mdp subdevice: %d\n", rc);
- vb2_queue_release(&inst->vid_bufq);
- wfd_free_input_buffers(wfd_dev, inst);
rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
CLOSE, (void *)inst->venc_inst);
-
if (rc)
WFD_MSG_ERR("Failed to CLOSE enc subdev: %d\n", rc);
rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core, ioctl,
VSG_CLOSE, NULL);
-
if (rc)
WFD_MSG_ERR("Failed to CLOSE vsg subdev: %d\n", rc);
@@ -1604,7 +1632,7 @@
switch (WFD_DEVICE_NUMBER_BASE + c) {
case WFD_DEVICE_SECURE:
- wfd_dev[c].secure_device = true;
+ wfd_dev[c].secure = true;
break;
default:
break;
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/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_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index f84fd82..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);
diff --git a/include/asm-generic/dma-contiguous.h b/include/asm-generic/dma-contiguous.h
index c544356..294b1e7 100644
--- a/include/asm-generic/dma-contiguous.h
+++ b/include/asm-generic/dma-contiguous.h
@@ -18,7 +18,7 @@
{
if (dev)
dev->cma_area = cma;
- if (!dev || !dma_contiguous_default_area)
+ if (!dev && !dma_contiguous_default_area)
dma_contiguous_default_area = cma;
}
diff --git a/include/linux/coresight-cti.h b/include/linux/coresight-cti.h
new file mode 100644
index 0000000..7f2da3f
--- /dev/null
+++ b/include/linux/coresight-cti.h
@@ -0,0 +1,59 @@
+/* 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 _LINUX_CORESIGHT_CTI_H
+#define _LINUX_CORESIGHT_CTI_H
+
+struct coresight_cti_data {
+ int nr_ctis;
+ const char **names;
+};
+
+struct coresight_cti {
+ const char *name;
+ struct list_head link;
+};
+
+#ifdef CONFIG_CORESIGHT_CTI
+extern struct coresight_cti *coresight_cti_get(const char *name);
+extern void coresight_cti_put(struct coresight_cti *cti);
+extern int coresight_cti_map_trigin(
+ struct coresight_cti *cti, int trig, int ch);
+extern int coresight_cti_map_trigout(
+ struct coresight_cti *cti, int trig, int ch);
+extern void coresight_cti_unmap_trigin(
+ struct coresight_cti *cti, int trig, int ch);
+extern void coresight_cti_unmap_trigout(
+ struct coresight_cti *cti, int trig, int ch);
+#else
+static inline struct coresight_cti *coresight_cti_get(const char *name)
+{
+ return NULL;
+}
+static inline void coresight_cti_put(struct coresight_cti *cti) {}
+static inline int coresight_cti_map_trigin(
+ struct coresight_cti *cti, int trig, int ch)
+{
+ return -ENOSYS;
+}
+static inline int coresight_cti_map_trigout(
+ struct coresight_cti *cti, int trig, int ch)
+{
+ return -ENOSYS;
+}
+static inline void coresight_cti_unmap_trigin(
+ struct coresight_cti *cti, int trig, int ch) {}
+static inline void coresight_cti_unmap_trigout(
+ struct coresight_cti *cti, int trig, int ch) {}
+#endif
+
+#endif
diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h
index 2f303e4..01b5c84 100644
--- a/include/linux/dma-contiguous.h
+++ b/include/linux/dma-contiguous.h
@@ -68,7 +68,7 @@
extern struct cma *dma_contiguous_default_area;
void dma_contiguous_reserve(phys_addr_t addr_limit);
-int dma_declare_contiguous(struct device *dev, unsigned long size,
+int dma_declare_contiguous(struct device *dev, phys_addr_t size,
phys_addr_t base, phys_addr_t limit);
struct page *dma_alloc_from_contiguous(struct device *dev, int count,
@@ -83,7 +83,7 @@
static inline void dma_contiguous_reserve(phys_addr_t limit) { }
static inline
-int dma_declare_contiguous(struct device *dev, unsigned long size,
+int dma_declare_contiguous(struct device *dev, phys_addr_t size,
phys_addr_t base, phys_addr_t limit)
{
return -ENOSYS;
diff --git a/include/linux/of_coresight.h b/include/linux/of_coresight.h
index 6a5e4d4..0943dda 100644
--- a/include/linux/of_coresight.h
+++ b/include/linux/of_coresight.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
@@ -16,12 +16,19 @@
#ifdef CONFIG_OF
extern struct coresight_platform_data *of_get_coresight_platform_data(
struct device *dev, struct device_node *node);
+extern struct coresight_cti_data *of_get_coresight_cti_data(
+ struct device *dev, struct device_node *node);
#else
static inline struct coresight_platform_data *of_get_coresight_platform_data(
struct device *dev, struct device_node *node)
{
return NULL;
}
+static inline struct coresight_cti_data *of_get_coresight_cti_data(
+ struct device *dev, struct device_node *node)
+{
+ return NULL;
+}
#endif
#endif
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/sound/soc/codecs/msm8x10-wcd.c b/sound/soc/codecs/msm8x10-wcd.c
index 4bcea07..c8647fb1 100644
--- a/sound/soc/codecs/msm8x10-wcd.c
+++ b/sound/soc/codecs/msm8x10-wcd.c
@@ -50,9 +50,9 @@
#define MSM8X10_WCD_I2S_MASTER_MODE_MASK 0x08
#define MSM8X10_DINO_CODEC_BASE_ADDR 0xFE043000
-#define MAX_MSM8X10_WCD_DEVICE 2
+#define MAX_MSM8X10_WCD_DEVICE 4
#define CODEC_DT_MAX_PROP_SIZE 40
-#define MSM8X10_WCD_I2C_GSBI_SLAVE_ID "2-000d"
+#define MSM8X10_WCD_I2C_GSBI_SLAVE_ID "1-000d"
enum {
MSM8X10_WCD_I2C_TOP_LEVEL = 0,
@@ -88,6 +88,7 @@
IIR2,
IIR_MAX,
};
+
/* Codec supports 5 bands */
enum {
BAND1 = 0,
@@ -119,7 +120,6 @@
struct wcd9xxx_mbhc mbhc;
};
-
static unsigned short rx_digital_gain_reg[] = {
MSM8X10_WCD_A_CDC_RX1_VOL_CTL_B2_CTL,
MSM8X10_WCD_A_CDC_RX2_VOL_CTL_B2_CTL,
@@ -171,7 +171,7 @@
return rtn;
}
-static int msm8x10_wcd_abh_write_device(u16 reg, u8 *value, u32 bytes)
+static int msm8x10_wcd_abh_write_device(u16 reg, unsigned int *value, u32 bytes)
{
u32 temp = ((u32)(*value)) & 0x000000FF;
u32 offset = (((u32)(reg)) ^ 0x00000400) & 0x00000FFF;
@@ -179,10 +179,10 @@
return 0;
}
-static int msm8x10_wcd_abh_read_device(u16 reg, u32 bytes, u8 *value)
+static int msm8x10_wcd_abh_read_device(u16 reg, u32 bytes, unsigned int *value)
{
u32 offset = (((u32)(reg)) ^ 0x00000400) & 0x00000FFF;
- *value = (u8)ioread32(ioremap(MSM8X10_DINO_CODEC_BASE_ADDR +
+ *value = ioread32(ioremap(MSM8X10_DINO_CODEC_BASE_ADDR +
offset, 4));
return 0;
}
@@ -194,10 +194,10 @@
int ret;
u8 reg_addr = 0;
u8 data[bytes + 1];
- struct msm8x10_wcd_i2c *msm8x10_wcd;
+ struct msm8x10_wcd_i2c *msm8x10_wcd = NULL;
ret = get_i2c_msm8x10_wcd_device_info(reg, &msm8x10_wcd);
- if (!ret) {
+ if (ret) {
pr_err("%s: Invalid register address\n", __func__);
return ret;
}
@@ -219,7 +219,7 @@
/* Try again if the write fails */
if (ret != 1) {
ret = i2c_transfer(msm8x10_wcd->client->adapter,
- msm8x10_wcd->xfer_msg, 1);
+ msm8x10_wcd->xfer_msg, 1);
if (ret != 1) {
pr_err("failed to write the device\n");
return ret;
@@ -235,11 +235,11 @@
struct i2c_msg *msg;
int ret = 0;
u8 reg_addr = 0;
- struct msm8x10_wcd_i2c *msm8x10_wcd;
+ struct msm8x10_wcd_i2c *msm8x10_wcd = NULL;
u8 i = 0;
ret = get_i2c_msm8x10_wcd_device_info(reg, &msm8x10_wcd);
- if (!ret) {
+ if (ret) {
pr_err("%s: Invalid register address\n", __func__);
return ret;
}
@@ -256,7 +256,6 @@
msg->len = 1;
msg->flags = 0;
msg->buf = ®_addr;
-
msg = &msm8x10_wcd->xfer_msg[1];
msg->addr = msm8x10_wcd->client->addr;
msg->len = 1;
@@ -275,38 +274,45 @@
}
}
}
+ pr_debug("%s: Reg 0x%x = 0x%x\n", __func__, reg, *dest);
return 0;
}
-static int msm8x10_wcd_reg_read(struct msm8x10_wcd *msm8x10_wcd, u16 reg)
+int msm8x10_wcd_i2c_read(unsigned short reg, int bytes, void *dest)
{
- u8 val;
+ return msm8x10_wcd_i2c_read_device(reg, bytes, dest);
+}
+
+int msm8x10_wcd_i2c_write(unsigned short reg, int bytes, void *src)
+{
+ return msm8x10_wcd_i2c_write_device(reg, src, bytes);
+}
+
+static int msm8x10_wcd_reg_read(struct msm8x10_wcd *msm8x10_wcd,
+ u16 reg, unsigned int *val)
+{
int ret = -EINVAL;
/* check if use I2C interface for Helicon or AHB for Dino */
mutex_lock(&msm8x10_wcd->io_lock);
if (MSM8X10_WCD_IS_HELICON_REG(reg))
- ret = msm8x10_wcd_i2c_read_device(reg, 1, &val);
+ ret = msm8x10_wcd_i2c_read(reg, 1, val);
else if (MSM8X10_WCD_IS_DINO_REG(reg))
- ret = msm8x10_wcd_abh_read_device(reg, 1, &val);
+ ret = msm8x10_wcd_abh_read_device(reg, 1, val);
mutex_unlock(&msm8x10_wcd->io_lock);
-
- if (ret < 0)
- return ret;
- else
- return val;
+ return ret;
}
static int msm8x10_wcd_reg_write(struct msm8x10_wcd *msm8x10_wcd, u16 reg,
- u8 val)
+ unsigned int val)
{
int ret = -EINVAL;
/* check if use I2C interface for Helicon or AHB for Dino */
mutex_lock(&msm8x10_wcd->io_lock);
if (MSM8X10_WCD_IS_HELICON_REG(reg))
- ret = msm8x10_wcd_i2c_write_device(reg, &val, 1);
+ ret = msm8x10_wcd_i2c_write(reg, 1, &val);
else if (MSM8X10_WCD_IS_DINO_REG(reg))
ret = msm8x10_wcd_abh_write_device(reg, &val, 1);
mutex_unlock(&msm8x10_wcd->io_lock);
@@ -331,12 +337,13 @@
return rtn;
}
-static int msm8x10_wcd_volatile(struct snd_soc_codec *ssc, unsigned int reg)
+static int msm8x10_wcd_volatile(struct snd_soc_codec *codec, unsigned int reg)
{
/*
* Registers lower than 0x100 are top level registers which can be
* written by the Taiko core driver.
*/
+ dev_dbg(codec->dev, "%s: reg 0x%x\n", __func__, reg);
if ((reg >= MSM8X10_WCD_A_CDC_MBHC_EN_CTL) || (reg < 0x100))
return 1;
@@ -373,7 +380,7 @@
unsigned int value)
{
int ret;
-
+ dev_dbg(codec->dev, "%s: Write from reg 0x%x\n", __func__, reg);
if (reg == SND_SOC_NOPM)
return 0;
@@ -395,6 +402,7 @@
unsigned int val;
int ret;
+ dev_dbg(codec->dev, "%s: Read from reg 0x%x\n", __func__, reg);
if (reg == SND_SOC_NOPM)
return 0;
@@ -411,7 +419,7 @@
reg, ret);
}
- val = msm8x10_wcd_reg_read(codec->control_data, reg);
+ ret = msm8x10_wcd_reg_read(codec->control_data, reg, &val);
return val;
}
@@ -431,7 +439,7 @@
if (!regnode) {
dev_err(dev, "Looking up %s property in node %s failed",
- prop_name, dev->of_node->full_name);
+ prop_name, dev->of_node->full_name);
return -ENODEV;
}
vreg->name = vreg_name;
@@ -442,7 +450,7 @@
if (!prop || (len != (2 * sizeof(__be32)))) {
dev_err(dev, "%s %s property\n",
- prop ? "invalid format" : "no", prop_name);
+ prop ? "invalid format" : "no", prop_name);
return -ENODEV;
} else {
vreg->min_uV = be32_to_cpup(&prop[0]);
@@ -450,18 +458,18 @@
}
snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE,
- "qcom,%s-current", vreg_name);
+ "qcom,%s-current", vreg_name);
ret = of_property_read_u32(dev->of_node, prop_name, &prop_val);
if (ret) {
dev_err(dev, "Looking up %s property in node %s failed",
- prop_name, dev->of_node->full_name);
+ prop_name, dev->of_node->full_name);
return -ENODEV;
}
vreg->optimum_uA = prop_val;
dev_info(dev, "%s: vol=[%d %d]uV, curr=[%d]uA\n", vreg->name,
- vreg->min_uV, vreg->max_uV, vreg->optimum_uA);
+ vreg->min_uV, vreg->max_uV, vreg->optimum_uA);
return 0;
}
@@ -473,7 +481,7 @@
u32 prop_val;
snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE,
- "qcom,cdc-micbias-ldoh-v");
+ "qcom,cdc-micbias-ldoh-v");
ret = of_property_read_u32(dev->of_node, prop_name, &prop_val);
if (ret) {
dev_err(dev, "Looking up %s property in node %s failed",
@@ -483,7 +491,7 @@
micbias->ldoh_v = (u8)prop_val;
snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE,
- "qcom,cdc-micbias-cfilt1-mv");
+ "qcom,cdc-micbias-cfilt1-mv");
ret = of_property_read_u32(dev->of_node, prop_name,
&micbias->cfilt1_mv);
if (ret) {
@@ -493,7 +501,7 @@
}
snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE,
- "qcom,cdc-micbias1-cfilt-sel");
+ "qcom,cdc-micbias1-cfilt-sel");
ret = of_property_read_u32(dev->of_node, prop_name, &prop_val);
if (ret) {
dev_err(dev, "Looking up %s property in node %s failed",
@@ -508,7 +516,7 @@
MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
dev_dbg(dev, "ldoh_v %u cfilt1_mv %u\n",
- (u32)micbias->ldoh_v, (u32)micbias->cfilt1_mv);
+ (u32)micbias->ldoh_v, (u32)micbias->cfilt1_mv);
dev_dbg(dev, "bias1_cfilt_sel %u\n", (u32)micbias->bias1_cfilt_sel);
dev_dbg(dev, "bias1_ext_cap %d\n", micbias->bias1_cap_mode);
@@ -533,13 +541,14 @@
num_of_supplies = ARRAY_SIZE(msm8x10_wcd_supplies);
} else {
dev_err(dev, "%s unsupported device %s\n",
- __func__, dev_name(dev));
+ __func__, dev_name(dev));
goto err;
}
if (num_of_supplies > ARRAY_SIZE(pdata->regulator)) {
dev_err(dev, "%s: Num of supplies %u > max supported %u\n",
- __func__, num_of_supplies, ARRAY_SIZE(pdata->regulator));
+ __func__, num_of_supplies,
+ ARRAY_SIZE(pdata->regulator));
goto err;
}
@@ -574,8 +583,8 @@
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ dev_dbg(codec->dev, "%s: event = %d\n", __func__, event);
- pr_debug("%s %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* Enable charge pump clock*/
@@ -623,13 +632,11 @@
} else if (ear_pa_gain == 0x04) {
ucontrol->value.integer.value[0] = 1;
} else {
- pr_err("%s: ERROR: Unsupported Ear Gain = 0x%x\n",
- __func__, ear_pa_gain);
+ dev_err(codec->dev, "%s: ERROR: Unsupported Ear Gain = 0x%x\n",
+ __func__, ear_pa_gain);
return -EINVAL;
}
-
- pr_debug("%s: ear_pa_gain = 0x%x\n", __func__, ear_pa_gain);
-
+ dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__, ear_pa_gain);
return 0;
}
@@ -639,8 +646,8 @@
u8 ear_pa_gain;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- pr_debug("%s: ucontrol->value.integer.value[0] = %ld\n",
- __func__, ucontrol->value.integer.value[0]);
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
switch (ucontrol->value.integer.value[0]) {
case 0:
@@ -673,7 +680,7 @@
(MSM8X10_WCD_A_CDC_IIR1_CTL + 64 * iir_idx)) &
(1 << band_idx);
- pr_debug("%s: IIR #%d band #%d enable %d\n", __func__,
+ dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__,
iir_idx, band_idx,
(uint32_t)ucontrol->value.integer.value[0]);
return 0;
@@ -692,15 +699,15 @@
/* Mask first 5 bits, 6-8 are reserved */
snd_soc_update_bits(codec, (MSM8X10_WCD_A_CDC_IIR1_CTL + 64 * iir_idx),
- (1 << band_idx), (value << band_idx));
+ (1 << band_idx), (value << band_idx));
- pr_debug("%s: IIR #%d band #%d enable %d\n", __func__,
+ dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__,
iir_idx, band_idx, value);
return 0;
}
static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec,
- int iir_idx, int band_idx,
- int coeff_idx)
+ int iir_idx, int band_idx,
+ int coeff_idx)
{
/* Address does not automatically update if reading */
snd_soc_write(codec,
@@ -734,7 +741,7 @@
ucontrol->value.integer.value[4] =
get_iir_band_coeff(codec, iir_idx, band_idx, 4);
- pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n"
+ dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n"
"%s: IIR #%d band #%d b1 = 0x%x\n"
"%s: IIR #%d band #%d b2 = 0x%x\n"
"%s: IIR #%d band #%d a1 = 0x%x\n"
@@ -780,17 +787,17 @@
kcontrol->private_value)->shift;
set_iir_band_coeff(codec, iir_idx, band_idx, 0,
- ucontrol->value.integer.value[0]);
+ ucontrol->value.integer.value[0]);
set_iir_band_coeff(codec, iir_idx, band_idx, 1,
- ucontrol->value.integer.value[1]);
+ ucontrol->value.integer.value[1]);
set_iir_band_coeff(codec, iir_idx, band_idx, 2,
- ucontrol->value.integer.value[2]);
+ ucontrol->value.integer.value[2]);
set_iir_band_coeff(codec, iir_idx, band_idx, 3,
- ucontrol->value.integer.value[3]);
+ ucontrol->value.integer.value[3]);
set_iir_band_coeff(codec, iir_idx, band_idx, 4,
- ucontrol->value.integer.value[4]);
+ ucontrol->value.integer.value[4]);
- pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n"
+ dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n"
"%s: IIR #%d band #%d b1 = 0x%x\n"
"%s: IIR #%d band #%d b2 = 0x%x\n"
"%s: IIR #%d band #%d a1 = 0x%x\n"
@@ -970,7 +977,6 @@
"ZERO", "ADC1", "ADC2", "DMIC1", "DMIC2"
};
-
static const char * const anc_mux_text[] = {
"ZERO", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6", "ADC_MB",
"RSVD_1", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", "DMIC6"
@@ -1076,14 +1082,16 @@
dec_name = strsep(&widget_name, " ");
widget_name = temp;
if (!dec_name) {
- pr_err("%s: Invalid decimator = %s\n", __func__, w->name);
+ dev_err(codec->dev, "%s: Invalid decimator = %s\n",
+ __func__, w->name);
ret = -EINVAL;
goto out;
}
ret = kstrtouint(strpbrk(dec_name, "12"), 10, &decimator);
if (ret < 0) {
- pr_err("%s: Invalid decimator = %s\n", __func__, dec_name);
+ dev_err(codec->dev, "%s: Invalid decimator = %s\n",
+ __func__, dec_name);
ret = -EINVAL;
goto out;
}
@@ -1100,7 +1108,8 @@
adc_dmic_sel = 0x0;
break;
default:
- pr_err("%s: Invalid Decimator = %u\n", __func__, decimator);
+ dev_err(codec->dev, "%s: Invalid Decimator = %u\n",
+ __func__, decimator);
ret = -EINVAL;
goto out;
}
@@ -1204,18 +1213,18 @@
static void msm8x10_wcd_codec_enable_adc_block(struct snd_soc_codec *codec,
int enable)
{
- struct msm8x10_wcd_priv *taiko = snd_soc_codec_get_drvdata(codec);
+ struct msm8x10_wcd_priv *wcd8x10 = snd_soc_codec_get_drvdata(codec);
- pr_debug("%s %d\n", __func__, enable);
+ dev_dbg(codec->dev, "%s %d\n", __func__, enable);
if (enable) {
- taiko->adc_count++;
+ wcd8x10->adc_count++;
snd_soc_update_bits(codec,
MSM8X10_WCD_A_CDC_ANA_CLK_CTL,
0x20, 0x20);
} else {
- taiko->adc_count--;
- if (!taiko->adc_count)
+ wcd8x10->adc_count--;
+ if (!wcd8x10->adc_count)
snd_soc_update_bits(codec,
MSM8X10_WCD_A_CDC_ANA_CLK_CTL,
0x20, 0x0);
@@ -1229,7 +1238,7 @@
u16 adc_reg;
u8 init_bit_shift;
- pr_debug("%s %d\n", __func__, event);
+ dev_dbg(codec->dev, "%s %d\n", __func__, event);
adc_reg = MSM8X10_WCD_A_TX_1_2_TEST_CTL;
if (w->reg == MSM8X10_WCD_A_TX_1_EN)
@@ -1237,7 +1246,8 @@
else if (adc_reg == MSM8X10_WCD_A_TX_2_EN)
init_bit_shift = 6;
else {
- pr_err("%s: Error, invalid adc register\n", __func__);
+ dev_err(codec->dev, "%s: Error, invalid adc register\n",
+ __func__);
return -EINVAL;
}
@@ -1263,14 +1273,15 @@
struct snd_soc_codec *codec = w->codec;
u16 lineout_gain_reg;
- pr_debug("%s %d %s\n", __func__, event, w->name);
+ dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name);
switch (w->shift) {
case 0:
lineout_gain_reg = MSM8X10_WCD_A_RX_LINE_1_GAIN;
break;
default:
- pr_err("%s: Error, incorrect lineout register value\n",
+ dev_err(codec->dev,
+ "%s: Error, incorrect lineout register value\n",
__func__);
return -EINVAL;
}
@@ -1280,8 +1291,8 @@
snd_soc_update_bits(codec, lineout_gain_reg, 0x40, 0x40);
break;
case SND_SOC_DAPM_POST_PMU:
- pr_debug("%s: sleeping 16 ms after %s PA turn on\n",
- __func__, w->name);
+ dev_dbg(codec->dev, "%s: sleeping 16 ms after %s PA turn on\n",
+ __func__, w->name);
usleep_range(16000, 16100);
break;
case SND_SOC_DAPM_POST_PMD:
@@ -1294,7 +1305,7 @@
static int msm8x10_wcd_codec_enable_spk_pa(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- pr_debug("%s %d %s\n", __func__, event, w->name);
+ dev_dbg(w->codec->dev, "%s %d %s\n", __func__, event, w->name);
return 0;
}
@@ -1311,7 +1322,8 @@
ret = kstrtouint(strpbrk(w->name, "12"), 10, &dmic);
if (ret < 0) {
- pr_err("%s: Invalid DMIC line on the codec\n", __func__);
+ dev_err(codec->dev,
+ "%s: Invalid DMIC line on the codec\n", __func__);
return -EINVAL;
}
@@ -1321,11 +1333,12 @@
dmic_clk_en = 0x01;
dmic_clk_cnt = &(msm8x10_wcd->dmic_1_2_clk_cnt);
dmic_clk_reg = MSM8X10_WCD_A_CDC_CLK_DMIC_B1_CTL;
- pr_debug("%s() event %d DMIC%d dmic_1_2_clk_cnt %d\n",
+ dev_dbg(codec->dev,
+ "%s() event %d DMIC%d dmic_1_2_clk_cnt %d\n",
__func__, event, dmic, *dmic_clk_cnt);
break;
default:
- pr_err("%s: Invalid DMIC Selection\n", __func__);
+ dev_err(codec->dev, "%s: Invalid DMIC Selection\n", __func__);
return -EINVAL;
}
@@ -1360,7 +1373,7 @@
char *internal3_text = "Internal3";
enum wcd9xxx_notify_event e_post_off, e_pre_on, e_post_on;
- pr_debug("%s %d\n", __func__, event);
+ dev_dbg(codec->dev, "%s %d\n", __func__, event);
switch (w->reg) {
case MSM8X10_WCD_A_MICB_1_CTL:
micb_int_reg = MSM8X10_WCD_A_MICB_1_INT_RBIAS;
@@ -1371,7 +1384,8 @@
e_post_off = WCD9XXX_EVENT_POST_MICBIAS_1_OFF;
break;
default:
- pr_err("%s: Error, invalid micbias register\n", __func__);
+ dev_err(codec->dev,
+ "%s: Error, invalid micbias register\n", __func__);
return -EINVAL;
}
@@ -1432,7 +1446,7 @@
u8 dec_hpf_cut_of_freq;
int offset;
- pr_debug("%s %d\n", __func__, event);
+ dev_dbg(codec->dev, "%s %d\n", __func__, event);
widget_name = kstrndup(w->name, 15, GFP_KERNEL);
if (!widget_name)
@@ -1442,26 +1456,29 @@
dec_name = strsep(&widget_name, " ");
widget_name = temp;
if (!dec_name) {
- pr_err("%s: Invalid decimator = %s\n", __func__, w->name);
+ dev_err(codec->dev,
+ "%s: Invalid decimator = %s\n", __func__, w->name);
ret = -EINVAL;
goto out;
}
ret = kstrtouint(strpbrk(dec_name, "12"), 10, &decimator);
if (ret < 0) {
- pr_err("%s: Invalid decimator = %s\n", __func__, dec_name);
+ dev_err(codec->dev,
+ "%s: Invalid decimator = %s\n", __func__, dec_name);
ret = -EINVAL;
goto out;
}
- pr_debug("%s(): widget = %s dec_name = %s decimator = %u\n", __func__,
- w->name, dec_name, decimator);
+ dev_dbg(codec->dev,
+ "%s(): widget = %s dec_name = %s decimator = %u\n", __func__,
+ w->name, dec_name, decimator);
if (w->reg == MSM8X10_WCD_A_CDC_CLK_TX_CLK_EN_B1_CTL) {
dec_reset_reg = MSM8X10_WCD_A_CDC_CLK_TX_RESET_B1_CTL;
offset = 0;
} else {
- pr_err("%s: Error, incorrect dec\n", __func__);
+ dev_err(codec->dev, "%s: Error, incorrect dec\n", __func__);
ret = -EINVAL;
goto out;
}
@@ -1531,11 +1548,12 @@
}
static int msm8x10_wcd_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol,
+ int event)
{
struct snd_soc_codec *codec = w->codec;
- pr_debug("%s %d %s\n", __func__, event, w->name);
+ dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -1565,7 +1583,7 @@
struct snd_soc_codec *codec = w->codec;
struct msm8x10_wcd_priv *msm8x10_wcd = snd_soc_codec_get_drvdata(codec);
- pr_debug("%s %d\n", __func__, event);
+ dev_dbg(codec->dev, "%s %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -1583,7 +1601,7 @@
{
struct snd_soc_codec *codec = w->codec;
- pr_debug("%s %s %d\n", __func__, w->name, event);
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -1603,7 +1621,7 @@
struct msm8x10_wcd_priv *msm8x10_wcd = snd_soc_codec_get_drvdata(codec);
enum wcd9xxx_notify_event e_pre_on, e_post_off;
- pr_debug("%s: %s event = %d\n", __func__, w->name, event);
+ dev_dbg(codec->dev, "%s: %s event = %d\n", __func__, w->name, event);
if (w->shift == 5) {
e_pre_on = WCD9XXX_EVENT_PRE_HPHR_PA_ON;
e_post_off = WCD9XXX_EVENT_POST_HPHR_PA_OFF;
@@ -1611,7 +1629,8 @@
e_pre_on = WCD9XXX_EVENT_PRE_HPHL_PA_ON;
e_post_off = WCD9XXX_EVENT_POST_HPHL_PA_OFF;
} else {
- pr_err("%s: Invalid w->shift %d\n", __func__, w->shift);
+ dev_err(codec->dev,
+ "%s: Invalid w->shift %d\n", __func__, w->shift);
return -EINVAL;
}
@@ -1635,8 +1654,9 @@
* would have been locked while snd_soc_jack_report also
* attempts to acquire same lock.
*/
- pr_debug("%s: sleep 10 ms after %s PA disable.\n", __func__,
- w->name);
+ dev_dbg(codec->dev,
+ "%s: sleep 10 ms after %s PA disable.\n", __func__,
+ w->name);
usleep_range(10000, 10100);
break;
}
@@ -1648,7 +1668,7 @@
{
struct snd_soc_codec *codec = w->codec;
- pr_debug("%s %s %d\n", __func__, w->name, event);
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -1665,7 +1685,7 @@
static int msm8x10_wcd_spk_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- pr_debug("%s %s %d\n", __func__, w->name, event);
+ dev_dbg(w->codec->dev, "%s %s %d\n", __func__, w->name, event);
return 0;
}
@@ -1812,14 +1832,14 @@
{"MIC BIAS1 External", NULL, "LDO_H"},
};
-
static int msm8x10_wcd_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct msm8x10_wcd *msm8x10_wcd_core =
dev_get_drvdata(dai->codec->dev);
- pr_debug("%s(): substream = %s stream = %d\n" , __func__,
- substream->name, substream->stream);
+ dev_dbg(dai->codec->dev, "%s(): substream = %s stream = %d\n",
+ __func__,
+ substream->name, substream->stream);
if ((msm8x10_wcd_core != NULL) &&
(msm8x10_wcd_core->dev != NULL))
pm_runtime_get_sync(msm8x10_wcd_core->dev);
@@ -1832,8 +1852,9 @@
{
struct msm8x10_wcd *msm8x10_wcd_core =
dev_get_drvdata(dai->codec->dev);
- pr_debug("%s(): substream = %s stream = %d\n" , __func__,
- substream->name, substream->stream);
+ dev_dbg(dai->codec->dev,
+ "%s(): substream = %s stream = %d\n" , __func__,
+ substream->name, substream->stream);
if ((msm8x10_wcd_core != NULL) &&
(msm8x10_wcd_core->dev != NULL)) {
pm_runtime_mark_last_busy(msm8x10_wcd_core->dev);
@@ -1846,9 +1867,9 @@
{
struct msm8x10_wcd_priv *msm8x10_wcd = snd_soc_codec_get_drvdata(codec);
- pr_debug("%s: mclk_enable = %u, dapm = %d\n", __func__, mclk_enable,
- dapm);
-
+ dev_dbg(codec->dev,
+ "%s: mclk_enable = %u, dapm = %d\n", __func__,
+ mclk_enable, dapm);
WCD9XXX_BCL_LOCK(&msm8x10_wcd->resmgr);
if (mclk_enable) {
wcd9xxx_resmgr_get_bandgap(&msm8x10_wcd->resmgr,
@@ -1870,13 +1891,13 @@
static int msm8x10_wcd_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- pr_debug("%s\n", __func__);
+ dev_dbg(dai->codec->dev, "%s\n", __func__);
return 0;
}
static int msm8x10_wcd_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- pr_debug("%s\n", __func__);
+ dev_dbg(dai->codec->dev, "%s\n", __func__);
return 0;
}
@@ -1885,7 +1906,7 @@
unsigned int rx_num, unsigned int *rx_slot)
{
- pr_debug("%s\n", __func__);
+ dev_dbg(dai->codec->dev, "%s\n", __func__);
return 0;
}
@@ -1894,7 +1915,7 @@
unsigned int *rx_num, unsigned int *rx_slot)
{
- pr_debug("%s\n", __func__);
+ dev_dbg(dai->codec->dev, "%s\n", __func__);
return 0;
}
@@ -1917,7 +1938,8 @@
u8 tx_fs_rate, rx_fs_rate;
int ret;
- pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
+ dev_dbg(dai->codec->dev,
+ "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
dai->name, dai->id, params_rate(params),
params_channels(params));
@@ -1947,7 +1969,8 @@
rx_fs_rate = 0xA0;
break;
default:
- pr_err("%s: Invalid sampling rate %d\n", __func__,
+ dev_err(dai->codec->dev,
+ "%s: Invalid sampling rate %d\n", __func__,
params_rate(params));
return -EINVAL;
}
@@ -1957,7 +1980,8 @@
ret = msm8x10_wcd_set_decimator_rate(dai, tx_fs_rate,
params_rate(params));
if (ret < 0) {
- pr_err("%s: set decimator rate failed %d\n", __func__,
+ dev_err(dai->codec->dev,
+ "%s: set decimator rate failed %d\n", __func__,
ret);
return ret;
}
@@ -1966,13 +1990,15 @@
ret = msm8x10_wcd_set_interpolator_rate(dai, rx_fs_rate,
params_rate(params));
if (ret < 0) {
- pr_err("%s: set decimator rate failed %d\n", __func__,
+ dev_err(dai->codec->dev,
+ "%s: set decimator rate failed %d\n", __func__,
ret);
return ret;
}
break;
default:
- pr_err("%s: Invalid stream type %d\n", __func__,
+ dev_err(dai->codec->dev,
+ "%s: Invalid stream type %d\n", __func__,
substream->stream);
return -EINVAL;
}
@@ -2026,13 +2052,15 @@
{
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- pr_debug("%s: Sleeping 20ms after enabling EAR PA\n",
- __func__);
+ dev_dbg(w->codec->dev,
+ "%s: Sleeping 20ms after enabling EAR PA\n",
+ __func__);
msleep(20);
break;
case SND_SOC_DAPM_POST_PMD:
- pr_debug("%s: Sleeping 20ms after disabling EAR PA\n",
- __func__);
+ dev_dbg(w->codec->dev,
+ "%s: Sleeping 20ms after disabling EAR PA\n",
+ __func__);
msleep(20);
break;
}
@@ -2310,11 +2338,12 @@
static int msm8x10_wcd_codec_probe(struct snd_soc_codec *codec)
{
- msm8x10_wcd_codec_init_reg(codec);
+ dev_dbg(codec->dev, "%s()\n", __func__);
+ codec->control_data = dev_get_drvdata(codec->dev);
+ msm8x10_wcd_codec_init_reg(codec);
msm8x10_wcd_update_reg_defaults(codec);
- dev_dbg(codec->dev, "%s()\n", __func__);
return 0;
}
@@ -2324,6 +2353,18 @@
return 0;
}
+static int msm8x10_wcd_device_init(struct msm8x10_wcd *msm8x10)
+{
+
+ mutex_init(&msm8x10->io_lock);
+ mutex_init(&msm8x10->xfer_lock);
+ mutex_init(&msm8x10->pm_lock);
+ msm8x10->wlock_holders = 0;
+
+ return 0;
+}
+
+
static struct snd_soc_codec_driver soc_codec_dev_msm8x10_wcd = {
.probe = msm8x10_wcd_codec_probe,
.remove = msm8x10_wcd_codec_remove,
@@ -2349,8 +2390,21 @@
static int __devinit msm8x10_wcd_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- int ret;
+ int ret = 0;
+ struct msm8x10_wcd *msm8x10 = NULL;
struct msm8x10_wcd_pdata *pdata;
+ static int device_id;
+ struct device *dev;
+
+ dev_dbg(&client->dev, "%s:slave addr = 0x%x device_id = %d\n",
+ __func__, client->addr, device_id);
+
+ if (device_id > 0) {
+ msm8x10_wcd_modules[device_id++].client = client;
+ return ret;
+ }
+
+ dev = &client->dev;
if (client->dev.of_node) {
dev_dbg(&client->dev, "%s:Platform data from device tree\n",
__func__);
@@ -2362,16 +2416,50 @@
pdata = client->dev.platform_data;
}
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_dev_msm8x10_wcd,
- msm8x10_wcd_i2s_dai, ARRAY_SIZE(msm8x10_wcd_i2s_dai));
- dev_dbg(&client->dev, "%s:ret = 0x%x\n", __func__, ret);
+ msm8x10 = kzalloc(sizeof(struct msm8x10_wcd), GFP_KERNEL);
+ if (msm8x10 == NULL) {
+ dev_err(&client->dev,
+ "%s: error, allocation failed\n", __func__);
+ ret = -ENOMEM;
+ goto fail;
+ }
+ msm8x10->dev = &client->dev;
+ msm8x10_wcd_modules[device_id++].client = client;
+ msm8x10->read_dev = msm8x10_wcd_reg_read;
+ msm8x10->write_dev = msm8x10_wcd_reg_write;
+ ret = msm8x10_wcd_device_init(msm8x10);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s:msm8x10_wcd_device_init failed with error %d\n",
+ __func__, ret);
+ goto fail;
+ }
+ dev_set_drvdata(&client->dev, msm8x10);
+ ret = snd_soc_register_codec(&client->dev, &soc_codec_dev_msm8x10_wcd,
+ msm8x10_wcd_i2s_dai,
+ ARRAY_SIZE(msm8x10_wcd_i2s_dai));
+ if (ret)
+ dev_err(&client->dev,
+ "%s:snd_soc_register_codec failed with error %d\n",
+ __func__, ret);
+fail:
return ret;
}
+static void msm8x10_wcd_device_exit(struct msm8x10_wcd *msm8x10)
+{
+ mutex_destroy(&msm8x10->pm_lock);
+ mutex_destroy(&msm8x10->io_lock);
+ mutex_destroy(&msm8x10->xfer_lock);
+ kfree(msm8x10);
+}
+
static int __devexit msm8x10_wcd_i2c_remove(struct i2c_client *client)
{
+ struct msm8x10_wcd *msm8x10 = dev_get_drvdata(&client->dev);
+
+ msm8x10_wcd_device_exit(msm8x10);
return 0;
}
@@ -2407,8 +2495,8 @@
pr_debug("%s:\n", __func__);
ret = i2c_add_driver(&msm8x10_wcd_i2c_driver);
if (ret != 0)
- pr_err("%s: Failed to add msm8x10 wcd I2C driver - error code %d\n",
- __func__, ret);
+ pr_err("%s: Failed to add msm8x10 wcd I2C driver - error %d\n",
+ __func__, ret);
return ret;
}
diff --git a/sound/soc/codecs/msm8x10-wcd.h b/sound/soc/codecs/msm8x10-wcd.h
index 365d526..44e8a6d 100644
--- a/sound/soc/codecs/msm8x10-wcd.h
+++ b/sound/soc/codecs/msm8x10-wcd.h
@@ -196,6 +196,10 @@
u8 version;
int reset_gpio;
+ int (*read_dev)(struct msm8x10_wcd *msm8x10,
+ unsigned short reg, unsigned int *val);
+ int (*write_dev)(struct msm8x10_wcd *msm8x10,
+ unsigned short reg, unsigned int val);
u32 num_of_supplies;
struct regulator_bulk_data *supplies;
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/msm8x10.c b/sound/soc/msm/msm8x10.c
index 981a9a7..4dd85fc 100644
--- a/sound/soc/msm/msm8x10.c
+++ b/sound/soc/msm/msm8x10.c
@@ -114,8 +114,8 @@
.stream_name = "Primary MI2S Playback",
.cpu_dai_name = "msm-dai-q6-mi2s.0",
.platform_name = "msm-pcm-routing",
- .codec_name = "msm-stub-codec.1",
- .codec_dai_name = "msm-stub-tx",
+ .codec_name = "msm8x10-wcd-i2c-core.1-000d",
+ .codec_dai_name = "msm8x10_wcd_i2s_rx1",
.no_pcm = 1,
.be_id = MSM_BACKEND_DAI_MI2S_RX,
.init = &msm_audrx_init,
@@ -127,8 +127,8 @@
.stream_name = "Secondary MI2S Capture",
.cpu_dai_name = "msm-dai-q6-mi2s.1",
.platform_name = "msm-pcm-routing",
- .codec_name = "msm-stub-codec.1",
- .codec_dai_name = "msm-stub-tx",
+ .codec_name = "msm8x10-wcd-i2c-core.1-000d",
+ .codec_dai_name = "msm8x10_wcd_i2s_tx1",
.no_pcm = 1,
.be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
.be_hw_params_fixup = msm_be_hw_params_fixup,