Merge "defconfig: 8910: enable watchdog in defconfig"
diff --git a/Documentation/devicetree/bindings/arm/msm/smem.txt b/Documentation/devicetree/bindings/arm/msm/smem.txt
new file mode 100644
index 0000000..a38984c
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/smem.txt
@@ -0,0 +1,107 @@
+Qualcomm Shared Memory
+
+[Root level node]
+Required properties:
+-compatible : should be "qcom,smem"
+-reg : the location and size of smem, the irq register base memory, and
+ optionally any auxiliary smem areas
+-reg-names : "smem" - string to identify the shared memory region
+ "irq-reg-base" - string to identify the irq register region
+ "aux-mem1", "aux-mem2", "aux-mem3", ... - optional strings to
+ identify any auxiliary shared memory regions
+
+[Second level nodes]
+
+qcom,smd
+Required properties:
+-compatible : should be "qcom,smd"
+-qcom,smd-edge : the smd edge
+-qcom,smd-irq-offset : the offset into the irq register base memory for sending
+ interrupts
+-qcom,smd-irq-bitmask : the sending irq bitmask
+-interrupts : the receiving interrupt line
+
+Optional properties:
+-qcom,pil-string : the name to use when loading this edge
+-qcom,irq-no-suspend: configure the incoming irq line as active during suspend
+
+qcom,smsm
+Required properties:
+-compatible : should be "qcom,smsm"
+-qcom,smsm-edge : the smsm edge
+-qcom,smsm-irq-offset : the offset into the irq register base memory for sending
+ interrupts
+-qcom,smsm-irq-bitmask : the sending irq bitmask
+-interrupts : the receiving interrupt line
+
+
+Example:
+
+ qcom,smem@fa00000 {
+ compatible = "qcom,smem";
+ reg = <0xfa00000 0x200000>,
+ <0xfa006000 0x1000>,
+ <0xfc428000 0x4000>;
+ reg-names = "smem", "irq-reg-base", "aux-mem1";
+
+ qcom,smd-modem {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <0>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x1000>;
+ qcom,pil-string = "modem";
+ interrupts = <0 25 1>;
+ };
+
+ qcom,smsm-modem {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <0>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x2000>;
+ interrupts = <0 26 1>;
+ };
+
+ qcom,smd-adsp {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <1>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x100>;
+ qcom,pil-string = "adsp";
+ interrupts = <0 156 1>;
+ };
+
+ qcom,smsm-adsp {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <1>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x200>;
+ interrupts = <0 157 1>;
+ };
+
+ qcom,smd-wcnss {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <6>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x20000>;
+ qcom,pil-string = "wcnss";
+ interrupts = <0 142 1>;
+ };
+
+ qcom,smsm-wcnss {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <6>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x80000>;
+ interrupts = <0 144 1>;
+ };
+
+ qcom,smd-rpm {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <15>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x1>;
+ interrupts = <0 168 1>;
+ qcom,irq-no-syspend;
+ };
+ };
+
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 5d5f9de..89c7417 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -264,6 +264,29 @@
def_bool y
depends on BUG
+config GENERIC_TIME_VSYSCALL
+ bool "Enable gettimeofday updates"
+ depends on CPU_V7
+ help
+ Enables updating the kernel user helper area with the xtime struct
+ data for gettimeofday via kernel user helpers.
+
+config ARM_USE_USER_ACCESSIBLE_TIMERS
+ bool "Enables mapping a timer counter page to user space"
+ depends on USE_USER_ACCESSIBLE_TIMERS && GENERIC_TIME_VSYSCALL
+ help
+ Enables ARM-specific user-accessible timers via a shared
+ memory page containing the cycle counter.
+
+config ARM_USER_ACCESSIBLE_TIMER_BASE
+ hex "Base address of user-accessible timer counter page"
+ default 0xfffef000
+ depends on ARM_USE_USER_ACCESSIBLE_TIMERS
+ help
+ Specify the base user-space virtual address where the user-accessible
+ timer counter page should be mapped by the kernel. User-space apps
+ will read directly from the page at this address.
+
source "init/Kconfig"
source "kernel/Kconfig.freezer"
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index 8018e6a..bf1c971 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -95,6 +95,7 @@
qcom,bms-calculate-soc-ms = <20000>;
qcom,bms-chg-term-ua = <100000>;
qcom,bms-batt-type = <0>;
+ qcom,bms-use-voltage-soc;
};
clkdiv@5b00 {
diff --git a/arch/arm/boot/dts/msm8974-cdp.dtsi b/arch/arm/boot/dts/msm8974-cdp.dtsi
index b2c73e1..e1b2863 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8974-cdp.dtsi
@@ -153,6 +153,43 @@
vdd-phy-supply = <&spi_eth_vreg>;
};
};
+
+ sound {
+ compatible = "qcom,msm8974-audio-taiko";
+ qcom,model = "msm8974-taiko-cdp-snd-card";
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "LDO_H", "MCLK",
+ "Ext Spk Bottom Pos", "LINEOUT1",
+ "Ext Spk Bottom Neg", "LINEOUT3",
+ "Ext Spk Top Pos", "LINEOUT2",
+ "Ext Spk Top Neg", "LINEOUT4",
+ "AMIC1", "MIC BIAS1 Internal1",
+ "MIC BIAS1 Internal1", "Handset Mic",
+ "AMIC2", "MIC BIAS2 External",
+ "MIC BIAS2 External", "Headset Mic",
+ "AMIC3", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCLeft Headset Mic",
+ "DMIC1", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic1",
+ "DMIC2", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic2",
+ "DMIC3", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic3",
+ "DMIC4", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic4",
+ "DMIC5", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic5",
+ "DMIC6", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic6";
+
+ qcom,cdc-mclk-gpios = <&pm8941_gpios 15 0>;
+ taiko-mclk-clk = <&pm8941_clkdiv1>;
+ qcom,taiko-mclk-clk-freq = <9600000>;
+ };
};
&sdcc2 {
diff --git a/arch/arm/boot/dts/msm8974-fluid.dtsi b/arch/arm/boot/dts/msm8974-fluid.dtsi
index 9092dd0..15fb799 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dtsi
+++ b/arch/arm/boot/dts/msm8974-fluid.dtsi
@@ -153,6 +153,43 @@
vdd-phy-supply = <&spi_eth_vreg>;
};
};
+
+ sound {
+ compatible = "qcom,msm8974-audio-taiko";
+ qcom,model = "msm8974-taiko-fluid-snd-card";
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "LDO_H", "MCLK",
+ "Ext Spk Bottom Pos", "LINEOUT1",
+ "Ext Spk Bottom Neg", "LINEOUT3",
+ "Ext Spk Top Pos", "LINEOUT2",
+ "Ext Spk Top Neg", "LINEOUT4",
+ "AMIC1", "MIC BIAS1 Internal1",
+ "MIC BIAS1 Internal1", "Handset Mic",
+ "AMIC2", "MIC BIAS2 External",
+ "MIC BIAS2 External", "Headset Mic",
+ "AMIC3", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCLeft Headset Mic",
+ "DMIC1", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic1",
+ "DMIC2", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic2",
+ "DMIC3", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic3",
+ "DMIC4", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic4",
+ "DMIC5", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic5",
+ "DMIC6", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic6";
+
+ qcom,cdc-mclk-gpios = <&pm8941_gpios 15 0>;
+ taiko-mclk-clk = <&pm8941_clkdiv1>;
+ qcom,taiko-mclk-clk-freq = <9600000>;
+ };
};
&sdcc1 {
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index a34694e..f391621 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -78,6 +78,32 @@
status = "ok";
};
+ drv2667_vreg: drv2667_vdd_vreg {
+ compatible = "regulator-fixed";
+ regulator-name = "vdd_drv2667";
+ };
+
+ i2c@f9967000 {
+ ti-drv2667@59 {
+ compatible = "ti,drv2667";
+ reg = <0x59>;
+ vdd-supply = <&drv2667_vreg>;
+ vdd-i2c-supply = <&pm8941_s3>;
+ ti,label = "vibrator";
+ ti,gain = <2>;
+ ti,idle-timeout-ms = <20>;
+ ti,max-runtime-ms = <15000>;
+ ti,mode = <2>;
+ ti,wav-seq = [
+ /* wave form id */
+ 01
+ /* header size, start and stop bytes */
+ 05 80 06 00 09
+ /* repeat, amp, freq, duration, envelope */
+ 01 ff 19 02 00];
+ };
+ };
+
i2c@f9924000 {
atmel_mxt_ts@4a {
compatible = "atmel,mxt-ts";
diff --git a/arch/arm/boot/dts/msm8974-mtp.dtsi b/arch/arm/boot/dts/msm8974-mtp.dtsi
index 1c6302c..e2c80c2 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8974-mtp.dtsi
@@ -153,6 +153,43 @@
vdd-phy-supply = <&spi_eth_vreg>;
};
};
+
+ sound {
+ compatible = "qcom,msm8974-audio-taiko";
+ qcom,model = "msm8974-taiko-mtp-snd-card";
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "LDO_H", "MCLK",
+ "Ext Spk Bottom Pos", "LINEOUT1",
+ "Ext Spk Bottom Neg", "LINEOUT3",
+ "Ext Spk Top Pos", "LINEOUT2",
+ "Ext Spk Top Neg", "LINEOUT4",
+ "AMIC1", "MIC BIAS1 Internal1",
+ "MIC BIAS1 Internal1", "Handset Mic",
+ "AMIC2", "MIC BIAS2 External",
+ "MIC BIAS2 External", "Headset Mic",
+ "AMIC3", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCLeft Headset Mic",
+ "DMIC1", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic1",
+ "DMIC2", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic2",
+ "DMIC3", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic3",
+ "DMIC4", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic4",
+ "DMIC5", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic5",
+ "DMIC6", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic6";
+
+ qcom,cdc-mclk-gpios = <&pm8941_gpios 15 0>;
+ taiko-mclk-clk = <&pm8941_clkdiv1>;
+ qcom,taiko-mclk-clk-freq = <9600000>;
+ };
};
&sdcc2 {
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 0e30129..20249be 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -1216,6 +1216,74 @@
compatible = "qcom,msm-mem-hole";
qcom,memblock-remove = <0x8400000 0x7b00000>; /* Address and Size of Hole */
};
+
+ qcom,smem@fa00000 {
+ compatible = "qcom,smem";
+ reg = <0xfa00000 0x200000>,
+ <0xfa006000 0x1000>,
+ <0xfc428000 0x4000>;
+ reg-names = "smem", "irq-reg-base", "aux-mem1";
+
+ qcom,smd-modem {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <0>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x1000>;
+ qcom,pil-string = "modem";
+ interrupts = <0 25 1>;
+ };
+
+ qcom,smsm-modem {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <0>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x2000>;
+ interrupts = <0 26 1>;
+ };
+
+ qcom,smd-adsp {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <1>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x100>;
+ qcom,pil-string = "adsp";
+ interrupts = <0 156 1>;
+ };
+
+ qcom,smsm-adsp {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <1>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x200>;
+ interrupts = <0 157 1>;
+ };
+
+ qcom,smd-wcnss {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <6>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x20000>;
+ qcom,pil-string = "wcnss";
+ interrupts = <0 142 1>;
+ };
+
+ qcom,smsm-wcnss {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <6>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x80000>;
+ interrupts = <0 144 1>;
+ };
+
+ qcom,smd-rpm {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <15>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x1>;
+ interrupts = <0 168 1>;
+ qcom,irq-no-suspend;
+ };
+ };
};
/include/ "msm-pm8x41-rpm-regulator.dtsi"
diff --git a/arch/arm/boot/dts/msm9625-cdp.dts b/arch/arm/boot/dts/msm9625-cdp.dts
index ba5bbcf..232fba7 100644
--- a/arch/arm/boot/dts/msm9625-cdp.dts
+++ b/arch/arm/boot/dts/msm9625-cdp.dts
@@ -37,6 +37,14 @@
summit,temperature-max = <3>; /* 45 C */
};
};
+
+ wlan0: qca,wlan {
+ cell-index = <0>;
+ compatible = "qca,ar6004-sdio";
+ qca,chip-pwd-l-gpios = <&msmgpio 62 0>;
+ qca,pm-enable-gpios = <&pm8019_gpios 3 0x0>;
+ qca,ar6004-vdd-io-supply = <&pm8019_l11>;
+ };
};
/* PM8019 GPIO and MPP configuration */
diff --git a/arch/arm/boot/dts/msm9625-mtp.dts b/arch/arm/boot/dts/msm9625-mtp.dts
index 7780686..faf86d4 100644
--- a/arch/arm/boot/dts/msm9625-mtp.dts
+++ b/arch/arm/boot/dts/msm9625-mtp.dts
@@ -37,6 +37,14 @@
summit,temperature-max = <3>; /* 45 C */
};
};
+
+ wlan0: qca,wlan {
+ cell-index = <0>;
+ compatible = "qca,ar6004-sdio";
+ qca,chip-pwd-l-gpios = <&msmgpio 62 0>;
+ qca,pm-enable-gpios = <&pm8019_gpios 3 0x0>;
+ qca,ar6004-vdd-io-supply = <&pm8019_l11>;
+ };
};
/* PM8019 GPIO and MPP configuration */
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index a4bec1b..b79f370 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -274,7 +274,172 @@
compatible = "qcom,msm-rng";
reg = <0xf9bff000 0x200>;
qcom,msm-rng-iface-clk;
- };
+ };
+
+ wcd9xxx_intc: wcd9xxx-irq {
+ compatible = "qcom,wcd9xxx-irq";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <20 0>;
+ interrupt-names = "cdc-int";
+ };
+
+ i2c@f9925000 {
+ cell-index = <3>;
+ compatible = "qcom,i2c-qup";
+ reg = <0xf9925000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ interrupts = <0 97 0>;
+ interrupt-names = "qup_err_intr";
+ qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <24000000>;
+
+ wcd9xxx_codec@0d{
+ compatible = "qcom,wcd9xxx-i2c";
+ reg = <0x0d>;
+ qcom,cdc-reset-gpio = <&msmgpio 22 0>;
+ interrupt-parent = <&wcd9xxx_intc>;
+ interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28>;
+ cdc-vdd-buck-supply = <&pm8019_l11>;
+ qcom,cdc-vdd-buck-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-buck-current = <25000>;
+
+ cdc-vdd-tx-h-supply = <&pm8019_l11>;
+ qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-tx-h-current = <25000>;
+
+ cdc-vdd-rx-h-supply = <&pm8019_l11>;
+ qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-rx-h-current = <25000>;
+
+ cdc-vddpx-1-supply = <&pm8019_l11>;
+ qcom,cdc-vddpx-1-voltage = <1800000 1800000>;
+ qcom,cdc-vddpx-1-current = <10000>;
+
+ cdc-vdd-a-1p2v-supply = <&pm8019_l9>;
+ qcom,cdc-vdd-a-1p2v-voltage = <1200000 1200000>;
+ qcom,cdc-vdd-a-1p2v-current = <10000>;
+
+ cdc-vddcx-1-supply = <&pm8019_l9>;
+ qcom,cdc-vddcx-1-voltage = <1200000 1200000>;
+ qcom,cdc-vddcx-1-current = <10000>;
+
+ cdc-vddcx-2-supply = <&pm8019_l9>;
+ qcom,cdc-vddcx-2-voltage = <1200000 1200000>;
+ qcom,cdc-vddcx-2-current = <10000>;
+
+ qcom,cdc-micbias-ldoh-v = <0x3>;
+ qcom,cdc-micbias-cfilt1-mv = <1800>;
+ qcom,cdc-micbias-cfilt2-mv = <2700>;
+ qcom,cdc-micbias-cfilt3-mv = <1800>;
+ qcom,cdc-micbias1-cfilt-sel = <0x0>;
+ qcom,cdc-micbias2-cfilt-sel = <0x1>;
+ qcom,cdc-micbias3-cfilt-sel = <0x2>;
+ qcom,cdc-micbias4-cfilt-sel = <0x2>;
+ };
+
+ wcd9xxx_codec@77{
+ compatible = "qcom,wcd9xxx-i2c";
+ reg = <0x77>;
+ };
+
+ wcd9xxx_codec@66{
+ compatible = "qcom,wcd9xxx-i2c";
+ reg = <0x66>;
+ };
+
+ wcd9xxx_codec@55{
+ compatible = "qcom,wcd9xxx-i2c";
+ reg = <0x55>;
+ };
+ };
+
+ sound {
+ compatible = "qcom,mdm9625-audio-taiko";
+ qcom,model = "mdm9625-taiko-i2s-snd-card";
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "LDO_H", "MCLK",
+ "Ext Spk Bottom Pos", "LINEOUT1",
+ "Ext Spk Bottom Neg", "LINEOUT3",
+ "Ext Spk Top Pos", "LINEOUT2",
+ "Ext Spk Top Neg", "LINEOUT4",
+ "AMIC1", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Handset Mic",
+ "AMIC2", "MIC BIAS2 External",
+ "MIC BIAS2 External", "Headset Mic",
+ "AMIC3", "MIC BIAS3 Internal1",
+ "MIC BIAS3 Internal1", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS1 Internal2",
+ "MIC BIAS1 Internal2", "ANCLeft Headset Mic",
+ "DMIC1", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic1",
+ "DMIC2", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic2",
+ "DMIC3", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic3",
+ "DMIC4", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic4",
+ "DMIC5", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic5",
+ "DMIC6", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic6";
+ qcom,taiko-mclk-clk-freq = <12288000>;
+ };
+
+ qcom,msm-adsp-loader {
+ compatible = "qcom,adsp-loader";
+ };
+
+ qcom,msm-pcm {
+ compatible = "qcom,msm-pcm-dsp";
+ };
+
+ qcom,msm-pcm-routing {
+ compatible = "qcom,msm-pcm-routing";
+ };
+
+ qcom,msm-compr-dsp {
+ compatible = "qcom,msm-compr-dsp";
+ };
+
+ qcom,msm-voip-dsp {
+ compatible = "qcom,msm-voip-dsp";
+ };
+
+ qcom,msm-pcm-voice {
+ compatible = "qcom,msm-pcm-voice";
+ };
+
+ qcom,msm-dai-fe {
+ compatible = "qcom,msm-dai-fe";
+ };
+
+ qcom,msm-pcm-afe {
+ compatible = "qcom,msm-pcm-afe";
+ };
+
+ qcom,msm-pcm-hostless {
+ compatible = "qcom,msm-pcm-hostless";
+ };
+
+ qcom,msm-dai-mi2s {
+ compatible = "qcom,msm-dai-mi2s";
+ qcom,msm-dai-q6-mi2s-prim {
+ compatible = "qcom,msm-dai-q6-mi2s";
+ qcom,msm-dai-q6-mi2s-dev-id = <0>;
+ qcom,msm-mi2s-rx-lines = <2>;
+ qcom,msm-mi2s-tx-lines = <1>;
+ };
+ };
+
+ qcom,msm-dai-q6 {
+ compatible = "qcom,msm-dai-q6";
+ };
};
/include/ "msm-pm8019-rpm-regulator.dtsi"
diff --git a/arch/arm/configs/msm8910_defconfig b/arch/arm/configs/msm8910_defconfig
index 5f542d3..e2e05b2 100644
--- a/arch/arm/configs/msm8910_defconfig
+++ b/arch/arm/configs/msm8910_defconfig
@@ -40,6 +40,7 @@
# CONFIG_MSM_FIQ_SUPPORT is not set
# CONFIG_MSM_PROC_COMM is not set
CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_PKG4=y
# CONFIG_MSM_HW3D is not set
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_WATCHDOG_V2=y
diff --git a/arch/arm/include/asm/mach/map.h b/arch/arm/include/asm/mach/map.h
index cd5be28..f705388 100644
--- a/arch/arm/include/asm/mach/map.h
+++ b/arch/arm/include/asm/mach/map.h
@@ -37,6 +37,7 @@
#define MT_MEMORY_RW 16
#define MT_MEMORY_RX 17
#define MT_MEMORY_DMA_READY 18
+#define MT_DEVICE_USER_ACCESSIBLE 19
#ifdef CONFIG_MMU
extern void iotable_init(struct map_desc *, int);
diff --git a/arch/arm/include/asm/user_accessible_timer.h b/arch/arm/include/asm/user_accessible_timer.h
new file mode 100644
index 0000000..c6d7bd4
--- /dev/null
+++ b/arch/arm/include/asm/user_accessible_timer.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ARM_KERNEL_USER_ACCESSIBLE_TIMER_H_
+#define _ARM_KERNEL_USER_ACCESSIBLE_TIMER_H_
+
+#define ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE -1
+
+extern unsigned long zero_pfn;
+
+#ifdef CONFIG_ARM_USE_USER_ACCESSIBLE_TIMERS
+#ifndef CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE
+#define CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE 0xfffef000
+#endif
+extern void setup_user_timer_offset(unsigned long addr);
+extern int get_timer_page_address(void);
+static inline int get_user_accessible_timers_base(void)
+{
+ return CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE;
+}
+extern void set_user_accessible_timer_flag(bool flag);
+#else
+#define CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE 0
+static inline void setup_user_timer_offset(unsigned long addr)
+{
+}
+static inline int get_timer_page_address(void)
+{
+ return ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE;
+}
+static inline int get_user_accessible_timers_base(void)
+{
+ return 0;
+}
+static inline void set_user_accessible_timer_flag(bool flag)
+{
+}
+#endif
+
+#endif
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 22b0f1e..fc00a23 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -63,6 +63,7 @@
obj-$(CONFIG_SWP_EMULATE) += swp_emulate.o
CFLAGS_swp_emulate.o := -Wa,-march=armv7-a
obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
+obj-$(CONFIG_GENERIC_TIME_VSYSCALL) += update_vsyscall_arm.o
obj-$(CONFIG_CPU_XSCALE) += xscale-cp0.o
obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o
@@ -73,6 +74,7 @@
obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
+obj-$(CONFIG_ARM_USE_USER_ACCESSIBLE_TIMERS) += user_accessible_timer.o
ifneq ($(CONFIG_ARCH_EBSA110),y)
obj-y += io.o
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 7a8c2d6..ddd421c 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -764,6 +764,97 @@
.align 5
.globl __kuser_helper_start
__kuser_helper_start:
+#ifdef GENERIC_TIME_VSYSCALL
+/*
+ * Reference declaration:
+ *
+ * extern struct timezone __kernel_helper_gtod_timezone
+ * extern unsigned int __kernel_helper_gtod_seqnum
+ *
+ * Definition and user space usage example:
+ *
+ * #define __kernel_helper_gtod_timezone (*(unsigned int*)0xffff0f20)
+ * #define __kernel_helper_gtod_seqnum (*(unsigned int*)0xffff0f28)
+ *
+ * unsigned int prelock, postlock ;
+ * do {
+ * prelock = __kernel_helper_gtod_seqnum;
+ * memcpy(&tz, (void*)&(__kernel_helper_gtod_timezone),
+ * sizeof(struct timezone)) ;
+ * postlock = __kernel_helper_gtod_seqnum;
+ * } while (prelock != postlock);
+ *
+ * 0xffff0f20-3: tz_minuteswest
+ * 0xffff0f24-7: tz_dsttime
+ * 0xffff0f28-b: sequence #.
+ * 0xffff0f30-3: offset into CONFIG_USER_ACCESSIBLE_TIMER_BASE to get the timer.
+ * 0xffff0f34-7: Feature flag
+ * 0xffff0f38-b: wall-to-mononic: tv_sec
+ * 0xffff0f3c-f: wall-to-mononic: tv_nsec
+ */
+ .globl __kuser_gtod_timezone
+__kuser_gtod_timezone: @0xffff0f20
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ /* This offset is where the flag to enable the
+ * user accessible timers is located.
+ */
+ .word 0
+ .word 0
+ .word 0
+ .align 5
+
+/*
+ * Reference declaration:
+ *
+ * extern struct timeval __kernel_helper_gtod_timeval
+ * extern unsigned int __kernel_helper_gtod_seqnum
+ *
+ * Definition and user space usage example:
+ *
+ * #define __kernel_helper_gtod_timeval (*(unsigned int*)0xffff0f40)
+ * #define __kernel_helper_gtod_seqnum (*(unsigned int*)0xffff0f48)
+ *
+ * unsigned int prelock, postlock ;
+ * struct gtod {
+ * uint64_t cycle_last;
+ * uint64_t mask;
+ * uint32_t mult;
+ * uint32_t shift;
+ * uint32_t tv_sec;
+ * uint32_t tv_nsec;
+ * };
+ * struct gtod gdtod;
+ *
+ * do {
+ * prelock = __kernel_helper_gtod_seqnum;
+ * memcpy(&gdtod, (void*)&(__kernel_helper_gtod_timeval),
+ * sizeof(struct gtod)) ;
+ * postlock = __kernel_helper_gtod_seqnum;
+ * } while (prelock != postlock);
+ *
+ * 0xffff0f40-7: cycle_last
+ * 0xffff0f48-f: mask
+ * 0xffff0f50-3: mult
+ * 0xffff0f54-7: shift
+ * 0xffff0f58-b: tv_sec
+ * 0xffff0f5c-f: tv_nsec
+ */
+ .globl __kuser_gtod_timeval
+__kuser_gtod_timeval: @0xffff0f40
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .align 5
+#endif
/*
* Due to the length of some sequences, __kuser_cmpxchg64 spans 2 regular
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index af21496..ac17480 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -680,6 +680,11 @@
const char *arch_vma_name(struct vm_area_struct *vma)
{
- return (vma == &gate_vma) ? "[vectors]" : NULL;
+ if (vma == &gate_vma)
+ return "[vectors]";
+ else if (vma == get_user_timers_vma(NULL))
+ return "[timers]";
+ else
+ return NULL;
}
#endif
diff --git a/arch/arm/kernel/update_vsyscall_arm.c b/arch/arm/kernel/update_vsyscall_arm.c
new file mode 100644
index 0000000..51f47ae
--- /dev/null
+++ b/arch/arm/kernel/update_vsyscall_arm.c
@@ -0,0 +1,100 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+#include "update_vsyscall_arm.h"
+/*
+ * See entry-armv.S for the offsets into the kernel user helper for
+ * these fields.
+ */
+#define ARM_VSYSCALL_TIMER_TZ 0xf20
+#define ARM_VSYSCALL_TIMER_SEQ 0xf28
+#define ARM_VSYSCALL_TIMER_OFFSET 0xf30
+#define ARM_VSYSCALL_TIMER_WTM_TV_SEC 0xf38
+#define ARM_VSYSCALL_TIMER_WTM_TV_NSEC 0xf3c
+#define ARM_VSYSCALL_TIMER_CYCLE_LAST 0xf40
+#define ARM_VSYSCALL_TIMER_MASK 0xf48
+#define ARM_VSYSCALL_TIMER_MULT 0xf50
+#define ARM_VSYSCALL_TIMER_SHIFT 0xf54
+#define ARM_VSYSCALL_TIMER_TV_SEC 0xf58
+#define ARM_VSYSCALL_TIMER_TV_NSEC 0xf5c
+
+struct kernel_gtod_t {
+ u64 cycle_last;
+ u64 mask;
+ u32 mult;
+ u32 shift;
+ u32 tv_sec;
+ u32 tv_nsec;
+};
+
+struct kernel_tz_t {
+ u32 tz_minuteswest;
+ u32 tz_dsttime;
+};
+
+struct kernel_wtm_t {
+ u32 tv_sec;
+ u32 tv_nsec;
+};
+
+/*
+ * Updates the kernel user helper area with the current timespec
+ * data, as well as additional fields needed to calculate
+ * gettimeofday, clock_gettime, etc.
+ */
+void
+update_vsyscall(struct timespec *ts, struct timespec *wtm,
+ struct clocksource *c, u32 mult)
+{
+ unsigned long vectors = (unsigned long)vectors_page;
+ unsigned long flags;
+ unsigned *seqnum = (unsigned *)(vectors + ARM_VSYSCALL_TIMER_SEQ);
+ struct kernel_gtod_t *dgtod = (struct kernel_gtod_t *)(vectors +
+ ARM_VSYSCALL_TIMER_CYCLE_LAST);
+ struct kernel_wtm_t *dgwtm = (struct kernel_wtm_t *)(vectors +
+ ARM_VSYSCALL_TIMER_WTM_TV_SEC);
+
+ write_seqlock_irqsave(&kuh_time_lock, flags);
+ *seqnum = kuh_time_lock.sequence;
+ dgtod->cycle_last = c->cycle_last;
+ dgtod->mask = c->mask;
+ dgtod->mult = c->mult;
+ dgtod->shift = c->shift;
+ dgtod->tv_sec = ts->tv_sec;
+ dgtod->tv_nsec = ts->tv_nsec;
+ dgwtm->tv_sec = wtm->tv_sec;
+ dgwtm->tv_nsec = wtm->tv_nsec;
+ *seqnum = kuh_time_lock.sequence + 1;
+ write_sequnlock_irqrestore(&kuh_time_lock, flags);
+}
+EXPORT_SYMBOL(update_vsyscall);
+
+void
+update_vsyscall_tz(void)
+{
+ unsigned long vectors = (unsigned long)vectors_page;
+ unsigned long flags;
+ unsigned *seqnum = (unsigned *)(vectors + ARM_VSYSCALL_TIMER_SEQ);
+ struct kernel_tz_t *dgtod = (struct kernel_tz_t *)(vectors +
+ ARM_VSYSCALL_TIMER_TZ);
+
+ write_seqlock_irqsave(&kuh_time_lock, flags);
+ *seqnum = kuh_time_lock.sequence;
+ dgtod->tz_minuteswest = sys_tz.tz_minuteswest;
+ dgtod->tz_dsttime = sys_tz.tz_dsttime;
+ *seqnum = kuh_time_lock.sequence + 1;
+ write_sequnlock_irqrestore(&kuh_time_lock, flags);
+}
+EXPORT_SYMBOL(update_vsyscall_tz);
diff --git a/arch/arm/kernel/update_vsyscall_arm.h b/arch/arm/kernel/update_vsyscall_arm.h
new file mode 100644
index 0000000..d06ca56
--- /dev/null
+++ b/arch/arm/kernel/update_vsyscall_arm.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/export.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+
+extern void *vectors_page;
+extern struct timezone sys_tz;
+
+/*
+ * This read-write spinlock protects us from races in SMP while
+ * updating the kernel user helper-embedded time.
+ */
+__cacheline_aligned_in_smp DEFINE_SEQLOCK(kuh_time_lock);
diff --git a/arch/arm/kernel/user_accessible_timer.c b/arch/arm/kernel/user_accessible_timer.c
new file mode 100644
index 0000000..c550c03
--- /dev/null
+++ b/arch/arm/kernel/user_accessible_timer.c
@@ -0,0 +1,132 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <asm/user_accessible_timer.h>
+#include <asm/traps.h>
+
+#define USER_ACCESS_TIMER_OFFSET 0xf30
+#define USER_ACCESS_FEATURE_OFFSET 0xf34
+#define USER_ACCESS_FEATURE_FLAG 0xffff0f20
+
+static struct vm_area_struct user_timers_vma;
+static int __init user_timers_vma_init(void)
+{
+ user_timers_vma.vm_start = CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE;
+ user_timers_vma.vm_end = CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE
+ + PAGE_SIZE;
+ user_timers_vma.vm_page_prot = PAGE_READONLY;
+ user_timers_vma.vm_flags = VM_READ | VM_MAYREAD;
+ return 0;
+}
+arch_initcall(user_timers_vma_init);
+
+int in_user_timers_area(struct mm_struct *mm, unsigned long addr)
+{
+ return (addr >= user_timers_vma.vm_start) &&
+ (addr < user_timers_vma.vm_end);
+}
+EXPORT_SYMBOL(in_user_timers_area);
+
+struct vm_area_struct *get_user_timers_vma(struct mm_struct *mm)
+{
+ return &user_timers_vma;
+}
+EXPORT_SYMBOL(get_user_timers_vma);
+
+int get_user_timer_page(struct vm_area_struct *vma,
+ struct mm_struct *mm, unsigned long start, unsigned int gup_flags,
+ struct page **pages, int idx, int *goto_next_page)
+{
+ /* Replicates the earlier work done in mm/memory.c */
+ unsigned long pg = start & PAGE_MASK;
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ /* Unset this flag -- this only gets activated if the
+ * caller should go straight to the next_page label on
+ * return.
+ */
+ *goto_next_page = 0;
+
+ /* user gate pages are read-only */
+ if (gup_flags & FOLL_WRITE)
+ return idx ? : -EFAULT;
+ if (pg > TASK_SIZE)
+ pgd = pgd_offset_k(pg);
+ else
+ pgd = pgd_offset_gate(mm, pg);
+ BUG_ON(pgd_none(*pgd));
+ pud = pud_offset(pgd, pg);
+ BUG_ON(pud_none(*pud));
+ pmd = pmd_offset(pud, pg);
+ if (pmd_none(*pmd))
+ return idx ? : -EFAULT;
+ VM_BUG_ON(pmd_trans_huge(*pmd));
+ pte = pte_offset_map(pmd, pg);
+ if (pte_none(*pte)) {
+ pte_unmap(pte);
+ return idx ? : -EFAULT;
+ }
+ vma = get_user_timers_vma(mm);
+ if (pages) {
+ struct page *page;
+
+ page = vm_normal_page(vma, start, *pte);
+ if (!page) {
+ if (!(gup_flags & FOLL_DUMP) &&
+ zero_pfn == pte_pfn(*pte))
+ page = pte_page(*pte);
+ else {
+ pte_unmap(pte);
+ return idx ? : -EFAULT;
+ }
+ }
+ pages[idx] = page;
+ get_page(page);
+ }
+ pte_unmap(pte);
+ /* In this case, set the next page */
+ *goto_next_page = 1;
+ return 0;
+}
+EXPORT_SYMBOL(get_user_timer_page);
+
+void setup_user_timer_offset(unsigned long addr)
+{
+#if defined(CONFIG_CPU_USE_DOMAINS)
+ unsigned long vectors = CONFIG_VECTORS_BASE;
+#else
+ unsigned long vectors = (unsigned long)vectors_page;
+#endif
+ unsigned long *timer_offset = (unsigned long *)(vectors +
+ USER_ACCESS_TIMER_OFFSET);
+ *timer_offset = addr;
+}
+EXPORT_SYMBOL(setup_user_timer_offset);
+
+void set_user_accessible_timer_flag(bool flag)
+{
+#if defined(CONFIG_CPU_USE_DOMAINS)
+ unsigned long vectors = CONFIG_VECTORS_BASE;
+#else
+ unsigned long vectors = (unsigned long)vectors_page;
+#endif
+ unsigned long *timer_offset = (unsigned long *)(vectors +
+ USER_ACCESS_FEATURE_OFFSET);
+ *timer_offset = (flag ? USER_ACCESS_FEATURE_FLAG : 0);
+}
+EXPORT_SYMBOL(set_user_accessible_timer_flag);
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index dbb4328..a0868c7 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -177,6 +177,10 @@
select ARM_HAS_SG_CHAIN
select MSM_KRAIT_WFE_FIXUP
select MSM_ULTRASOUND_A
+ select GENERIC_TIME_VSYSCALL
+ select USE_USER_ACCESSIBLE_TIMERS
+ select ARM_USE_USER_ACCESSIBLE_TIMERS
+ select MSM_USE_USER_ACCESSIBLE_TIMERS
config ARCH_MSM8930
bool "MSM8930"
@@ -209,6 +213,10 @@
select HOLES_IN_ZONE if SPARSEMEM
select ARM_HAS_SG_CHAIN
select MSM_KRAIT_WFE_FIXUP
+ select GENERIC_TIME_VSYSCALL
+ select USE_USER_ACCESSIBLE_TIMERS
+ select ARM_USE_USER_ACCESSIBLE_TIMERS
+ select MSM_USE_USER_ACCESSIBLE_TIMERS
config ARCH_APQ8064
bool "APQ8064"
@@ -237,6 +245,10 @@
select ARM_HAS_SG_CHAIN
select MSM_KRAIT_WFE_FIXUP
select MSM_ULTRASOUND_A
+ select GENERIC_TIME_VSYSCALL
+ select USE_USER_ACCESSIBLE_TIMERS
+ select ARM_USE_USER_ACCESSIBLE_TIMERS
+ select MSM_USE_USER_ACCESSIBLE_TIMERS
config ARCH_MSM8974
bool "MSM8974"
@@ -349,6 +361,9 @@
select SPARSE_IRQ
select MSM_MULTIMEDIA_USE_ION
select MSM_RPM_STATS_LOG
+ select MSM_QDSP6_APRV2
+ select MSM_QDSP6V2_CODECS
+ select MSM_AUDIO_QDSP6V2 if SND_SOC
config ARCH_MSM8910
bool "MSM8910"
@@ -2661,4 +2676,12 @@
if apps is not responding and holding lock with irqs disabled.
Modem will then generate an raise a FIQ on this line before sending
SMSM reset.
+
+config MSM_USE_USER_ACCESSIBLE_TIMERS
+ bool "Enables mapping an MSM timer counter page to user space."
+ depends on ARM_USE_USER_ACCESSIBLE_TIMERS
+ help
+ Enables MSM-specific user accessible timers via a shared
+ memory page containing the cycle counter.
+
endif
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 0c2f8ae..8690df8 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -398,6 +398,8 @@
obj-$(CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL) += wdog_debug.o
+obj-$(CONFIG_MSM_USE_USER_ACCESSIBLE_TIMERS) += timer_page.o
+
ifdef CONFIG_MSM_CPR
obj-$(CONFIG_DEBUG_FS) += msm_cpr-debug.o
endif
diff --git a/arch/arm/mach-msm/board-8930-display.c b/arch/arm/mach-msm/board-8930-display.c
index a0bfabf..7e477b1 100644
--- a/arch/arm/mach-msm/board-8930-display.c
+++ b/arch/arm/mach-msm/board-8930-display.c
@@ -740,11 +740,29 @@
return 0;
if (on) {
+ if (!(hdmi_msm_data.is_mhl_enabled)) {
+ rc = gpio_request(HDMI_MHL_MUX_GPIO, "MHL_HDMI_MUX");
+ if (rc < 0) {
+ pr_err("gpio hdmi_mhl mux req failed:%d\n",
+ rc);
+ return rc;
+ }
+ rc = gpio_direction_output(HDMI_MHL_MUX_GPIO, 1);
+ if (rc < 0) {
+ pr_err("set gpio hdmi_mhl dir failed:%d\n",
+ rc);
+ goto error0;
+ }
+ gpio_set_value(HDMI_MHL_MUX_GPIO, 1);
+ pr_debug("set gpio hdmi mhl mux %d to 1\n",
+ HDMI_MHL_MUX_GPIO);
+ }
+
rc = gpio_request(100, "HDMI_DDC_CLK");
if (rc) {
pr_err("'%s'(%d) gpio_request failed, rc=%d\n",
"HDMI_DDC_CLK", 100, rc);
- return rc;
+ goto error0;
}
rc = gpio_request(101, "HDMI_DDC_DATA");
if (rc) {
@@ -760,6 +778,8 @@
}
pr_debug("%s(on): success\n", __func__);
} else {
+ if (!(hdmi_msm_data.is_mhl_enabled))
+ gpio_free(HDMI_MHL_MUX_GPIO);
gpio_free(100);
gpio_free(101);
gpio_free(102);
@@ -773,6 +793,9 @@
gpio_free(101);
error1:
gpio_free(100);
+error0:
+ if (!(hdmi_msm_data.is_mhl_enabled))
+ gpio_free(HDMI_MHL_MUX_GPIO);
return rc;
}
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8038.c b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
index 727c4c6..c74dc26 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8038.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
@@ -490,7 +490,7 @@
/* ID a_on pd ss min_uV max_uV supply sys_uA freq fm ss_fm */
RPM_SMPS(S1, 0, 1, 1, 500000, 1150000, NULL, 100000, 4p80, AUTO, LPM),
RPM_SMPS(S2, 1, 1, 1, 1400000, 1400000, NULL, 100000, 1p60, AUTO, LPM),
- RPM_SMPS(S3, 0, 1, 1, 1150000, 1150000, NULL, 100000, 3p20, AUTO, LPM),
+ RPM_SMPS(S3, 0, 1, 1, 1150000, 1150000, NULL, 100000, 3p20, AUTO, AUTO),
RPM_SMPS(S4, 1, 1, 1, 1950000, 2200000, NULL, 100000, 1p60, AUTO, LPM),
/* ID a_on pd ss min_uV max_uV supply sys_uA init_ip */
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index 1b487fa..ece65d6 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -112,7 +112,6 @@
#define KS8851_IRQ_GPIO 90
#define HAP_SHIFT_LVL_OE_GPIO 47
-#define HDMI_MHL_MUX_GPIO 73
#define MHL_GPIO_INT 72
#define MHL_GPIO_RESET 71
#define MHL_GPIO_PWR_EN 5
diff --git a/arch/arm/mach-msm/board-8930.h b/arch/arm/mach-msm/board-8930.h
index 055576f..dbcfa9d 100644
--- a/arch/arm/mach-msm/board-8930.h
+++ b/arch/arm/mach-msm/board-8930.h
@@ -164,5 +164,7 @@
#define MSM_8930_GSBI10_QUP_I2C_BUS_ID 10
#define MSM_8930_GSBI12_QUP_I2C_BUS_ID 12
+#define HDMI_MHL_MUX_GPIO 73
+
extern struct msm_rtb_platform_data msm8930_rtb_pdata;
extern struct msm_cache_dump_platform_data msm8930_cache_dump_pdata;
diff --git a/arch/arm/mach-msm/board-8974-gpiomux.c b/arch/arm/mach-msm/board-8974-gpiomux.c
index 50b59e1..89ad4ef 100644
--- a/arch/arm/mach-msm/board-8974-gpiomux.c
+++ b/arch/arm/mach-msm/board-8974-gpiomux.c
@@ -152,6 +152,26 @@
};
+static struct gpiomux_setting mhl_suspend_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting mhl_active_1_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+ .dir = GPIOMUX_OUT_HIGH,
+};
+
+static struct gpiomux_setting mhl_active_2_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+
static struct gpiomux_setting hdmi_suspend_cfg = {
.func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_2MA,
@@ -170,6 +190,34 @@
.pull = GPIOMUX_PULL_DOWN,
};
+static struct msm_gpiomux_config msm_mhl_configs[] __initdata = {
+ {
+ /* mhl-sii8334 pwr */
+ .gpio = 12,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mhl_suspend_config,
+ [GPIOMUX_ACTIVE] = &mhl_active_1_cfg,
+ },
+ },
+ {
+ /* mhl-sii8334 intr */
+ .gpio = 82,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mhl_suspend_config,
+ [GPIOMUX_ACTIVE] = &mhl_active_1_cfg,
+ },
+ },
+ {
+ /* mhl-sii8334 reset */
+ .gpio = 8,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mhl_suspend_config,
+ [GPIOMUX_ACTIVE] = &mhl_active_2_cfg,
+ },
+ },
+};
+
+
static struct msm_gpiomux_config msm_hdmi_configs[] __initdata = {
{
.gpio = 31,
@@ -575,4 +623,5 @@
msm_gpiomux_install(msm_taiko_config, ARRAY_SIZE(msm_taiko_config));
msm_gpiomux_install(msm_hdmi_configs, ARRAY_SIZE(msm_hdmi_configs));
+ msm_gpiomux_install(msm_mhl_configs, ARRAY_SIZE(msm_mhl_configs));
}
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index 98a82b1..b092a53 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -66,168 +66,6 @@
return MEMTYPE_EBI1;
}
-static struct resource smd_resource[] = {
- {
- .name = "modem_smd_in",
- .start = 32 + 25, /* mss_sw_to_kpss_ipc_irq0 */
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "modem_smsm_in",
- .start = 32 + 26, /* mss_sw_to_kpss_ipc_irq1 */
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "adsp_smd_in",
- .start = 32 + 156, /* lpass_to_kpss_ipc_irq0 */
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "adsp_smsm_in",
- .start = 32 + 157, /* lpass_to_kpss_ipc_irq1 */
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "wcnss_smd_in",
- .start = 32 + 142, /* WcnssAppsSmdMedIrq */
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "wcnss_smsm_in",
- .start = 32 + 144, /* RivaAppsWlanSmsmIrq */
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "rpm_smd_in",
- .start = 32 + 168, /* rpm_to_kpss_ipc_irq4 */
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct smd_subsystem_config smd_config_list[] = {
- {
- .irq_config_id = SMD_MODEM,
- .subsys_name = "modem",
- .edge = SMD_APPS_MODEM,
-
- .smd_int.irq_name = "modem_smd_in",
- .smd_int.flags = IRQF_TRIGGER_RISING,
- .smd_int.irq_id = -1,
- .smd_int.device_name = "smd_dev",
- .smd_int.dev_id = 0,
- .smd_int.out_bit_pos = 1 << 12,
- .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
- .smd_int.out_offset = 0x8,
-
- .smsm_int.irq_name = "modem_smsm_in",
- .smsm_int.flags = IRQF_TRIGGER_RISING,
- .smsm_int.irq_id = -1,
- .smsm_int.device_name = "smsm_dev",
- .smsm_int.dev_id = 0,
- .smsm_int.out_bit_pos = 1 << 13,
- .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
- .smsm_int.out_offset = 0x8,
- },
- {
- .irq_config_id = SMD_Q6,
- .subsys_name = "adsp",
- .edge = SMD_APPS_QDSP,
-
- .smd_int.irq_name = "adsp_smd_in",
- .smd_int.flags = IRQF_TRIGGER_RISING,
- .smd_int.irq_id = -1,
- .smd_int.device_name = "smd_dev",
- .smd_int.dev_id = 0,
- .smd_int.out_bit_pos = 1 << 8,
- .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
- .smd_int.out_offset = 0x8,
-
- .smsm_int.irq_name = "adsp_smsm_in",
- .smsm_int.flags = IRQF_TRIGGER_RISING,
- .smsm_int.irq_id = -1,
- .smsm_int.device_name = "smsm_dev",
- .smsm_int.dev_id = 0,
- .smsm_int.out_bit_pos = 1 << 9,
- .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
- .smsm_int.out_offset = 0x8,
- },
- {
- .irq_config_id = SMD_WCNSS,
- .subsys_name = "wcnss",
- .edge = SMD_APPS_WCNSS,
-
- .smd_int.irq_name = "wcnss_smd_in",
- .smd_int.flags = IRQF_TRIGGER_RISING,
- .smd_int.irq_id = -1,
- .smd_int.device_name = "smd_dev",
- .smd_int.dev_id = 0,
- .smd_int.out_bit_pos = 1 << 17,
- .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
- .smd_int.out_offset = 0x8,
-
- .smsm_int.irq_name = "wcnss_smsm_in",
- .smsm_int.flags = IRQF_TRIGGER_RISING,
- .smsm_int.irq_id = -1,
- .smsm_int.device_name = "smsm_dev",
- .smsm_int.dev_id = 0,
- .smsm_int.out_bit_pos = 1 << 19,
- .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
- .smsm_int.out_offset = 0x8,
- },
- {
- .irq_config_id = SMD_RPM,
- .subsys_name = NULL, /* do not use PIL to load RPM */
- .edge = SMD_APPS_RPM,
-
- .smd_int.irq_name = "rpm_smd_in",
- .smd_int.flags = IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
- .smd_int.irq_id = -1,
- .smd_int.device_name = "smd_dev",
- .smd_int.dev_id = 0,
- .smd_int.out_bit_pos = 1 << 0,
- .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
- .smd_int.out_offset = 0x8,
-
- .smsm_int.irq_name = NULL, /* RPM does not support SMSM */
- .smsm_int.flags = 0,
- .smsm_int.irq_id = 0,
- .smsm_int.device_name = NULL,
- .smsm_int.dev_id = 0,
- .smsm_int.out_bit_pos = 0,
- .smsm_int.out_base = NULL,
- .smsm_int.out_offset = 0,
- },
-};
-
-static struct smd_smem_regions aux_smem_areas[] = {
- {
- .phys_addr = (void *)(0xfc428000),
- .size = 0x4000,
- },
-};
-
-static struct smd_subsystem_restart_config smd_ssr_cfg = {
- .disable_smsm_reset_handshake = 1,
-};
-
-static struct smd_platform smd_platform_data = {
- .num_ss_configs = ARRAY_SIZE(smd_config_list),
- .smd_ss_configs = smd_config_list,
- .smd_ssr_config = &smd_ssr_cfg,
- .num_smem_areas = ARRAY_SIZE(aux_smem_areas),
- .smd_smem_areas = aux_smem_areas,
-};
-
-struct platform_device msm_device_smd_8974 = {
- .name = "msm_smd",
- .id = -1,
- .resource = smd_resource,
- .num_resources = ARRAY_SIZE(smd_resource),
- .dev = {
- .platform_data = &smd_platform_data,
- }
-};
-
static struct reserve_info msm8974_reserve_info __initdata = {
.memtype_reserve_table = msm8974_reserve_table,
.paddr_to_memtype = msm8974_paddr_to_memtype,
@@ -426,11 +264,6 @@
ARRAY_SIZE(msm_bus_8974_devices));
};
-void __init msm8974_add_devices(void)
-{
- platform_device_register(&msm_device_smd_8974);
-}
-
/*
* Used to satisfy dependencies for devices that need to be
* run early or in a particular order. Most likely your device doesn't fall
@@ -536,7 +369,6 @@
regulator_has_full_constraints();
of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
- msm8974_add_devices();
msm8974_add_drivers();
}
diff --git a/arch/arm/mach-msm/board-9625-gpiomux.c b/arch/arm/mach-msm/board-9625-gpiomux.c
index 8f07ff0..9102875 100644
--- a/arch/arm/mach-msm/board-9625-gpiomux.c
+++ b/arch/arm/mach-msm/board-9625-gpiomux.c
@@ -93,6 +93,72 @@
},
};
+static struct gpiomux_setting mi2s_active_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting mi2s_suspend_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting codec_reset = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_6MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
+};
+
+static struct msm_gpiomux_config mdm9625_mi2s_configs[] __initdata = {
+ {
+ .gpio = 12, /* mi2s ws */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+ [GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+ },
+ },
+ {
+ .gpio = 15, /* mi2s sclk */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+ [GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+ },
+ },
+ {
+ .gpio = 14, /* mi2s dout */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+ [GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+ },
+ },
+ {
+ .gpio = 13, /* mi2s din */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+ [GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+ },
+ },
+ {
+ .gpio = 71, /* mi2s mclk */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+ [GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+ },
+ },
+};
+
+static struct msm_gpiomux_config mdm9625_cdc_reset_config[] __initdata = {
+ {
+ .gpio = 22, /* SYS_RST_N */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &codec_reset,
+ },
+ }
+};
+
static struct gpiomux_setting sdc3_clk_active_cfg = {
.func = GPIOMUX_FUNC_1,
.drv = GPIOMUX_DRV_8MA,
@@ -207,4 +273,8 @@
msm_gpiomux_install(sdc3_configs, ARRAY_SIZE(sdc3_configs));
msm_gpiomux_install(wlan_ath6kl_configs,
ARRAY_SIZE(wlan_ath6kl_configs));
+ msm_gpiomux_install(mdm9625_mi2s_configs,
+ ARRAY_SIZE(mdm9625_mi2s_configs));
+ msm_gpiomux_install(mdm9625_cdc_reset_config,
+ ARRAY_SIZE(mdm9625_cdc_reset_config));
}
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index 054e70c..57b4bd3 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -161,6 +161,12 @@
}
#endif
+/*
+ * Function to program the global registers of an IOMMU securely.
+ * This should only be called on IOMMUs for which kernel programming
+ * of global registers is not possible
+ */
+int msm_iommu_sec_program_iommu(int sec_id);
static inline int msm_soc_version_supports_iommu_v1(void)
{
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
index d34536d..88cb94a 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
@@ -14,7 +14,7 @@
#define _AUDIO_ACDB_H
#include <linux/msm_audio_acdb.h>
-#ifdef CONFIG_ARCH_MSM8974
+#if defined CONFIG_ARCH_MSM8974 || defined CONFIG_ARCH_MSM9625
#include <sound/q6adm-v2.h>
#else
#include <sound/q6adm.h>
diff --git a/arch/arm/mach-msm/qdsp6v2/q6audio_common.h b/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
index e4291e7..3bc8454 100644
--- a/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
+++ b/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
@@ -15,7 +15,7 @@
#ifndef __Q6_AUDIO_COMMON_H__
#define __Q6_AUDIO_COMMON_H__
-#ifdef CONFIG_ARCH_MSM8974
+#if defined(CONFIG_ARCH_MSM8974) || defined(CONFIG_ARCH_MSM9625)
#include <sound/apr_audio-v2.h>
#include <sound/q6asm-v2.h>
#else
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index b1dd1db..7427899 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -36,6 +36,8 @@
#include <linux/notifier.h>
#include <linux/sort.h>
#include <linux/suspend.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
#include <mach/msm_smd.h>
#include <mach/msm_iomap.h>
#include <mach/system.h>
@@ -3502,6 +3504,295 @@
return err_ret;
}
+static int __devinit parse_smd_devicetree(struct device_node *node,
+ void *irq_out_base)
+{
+ uint32_t edge;
+ char *key;
+ int ret;
+ uint32_t irq_offset;
+ uint32_t irq_bitmask;
+ uint32_t irq_line;
+ unsigned long irq_flags = IRQF_TRIGGER_RISING;
+ const char *pilstr;
+ struct interrupt_config_item *private_irq;
+
+ key = "qcom,smd-edge";
+ ret = of_property_read_u32(node, key, &edge);
+ if (ret)
+ goto missing_key;
+ SMD_DBG("%s: %s = %d", __func__, key, edge);
+
+ key = "qcom,smd-irq-offset";
+ ret = of_property_read_u32(node, key, &irq_offset);
+ if (ret)
+ goto missing_key;
+ SMD_DBG("%s: %s = %x", __func__, key, irq_offset);
+
+ key = "qcom,smd-irq-bitmask";
+ ret = of_property_read_u32(node, key, &irq_bitmask);
+ if (ret)
+ goto missing_key;
+ SMD_DBG("%s: %s = %x", __func__, key, irq_bitmask);
+
+ key = "interrupts";
+ irq_line = irq_of_parse_and_map(node, 0);
+ if (!irq_line)
+ goto missing_key;
+ SMD_DBG("%s: %s = %d", __func__, key, irq_line);
+
+ key = "qcom,pil-string";
+ pilstr = of_get_property(node, key, NULL);
+ if (pilstr)
+ SMD_DBG("%s: %s = %s", __func__, key, pilstr);
+
+ key = "qcom,irq-no-suspend";
+ ret = of_property_read_bool(node, key);
+ if (ret)
+ irq_flags |= IRQF_NO_SUSPEND;
+
+ private_irq = &private_intr_config[edge_to_pids[edge].remote_pid].smd;
+ private_irq->out_bit_pos = irq_bitmask;
+ private_irq->out_offset = irq_offset;
+ private_irq->out_base = irq_out_base;
+ private_irq->irq_id = irq_line;
+
+ ret = request_irq(irq_line,
+ private_irq->irq_handler,
+ irq_flags,
+ "smd_dev",
+ NULL);
+ if (ret < 0) {
+ pr_err("%s: request_irq() failed on %d\n", __func__, irq_line);
+ return ret;
+ } else {
+ ret = enable_irq_wake(irq_line);
+ if (ret < 0)
+ pr_err("%s: enable_irq_wake() failed on %d\n", __func__,
+ irq_line);
+ }
+
+ if (pilstr)
+ strlcpy(edge_to_pids[edge].subsys_name, pilstr,
+ SMD_MAX_CH_NAME_LEN);
+
+ return 0;
+
+missing_key:
+ pr_err("%s: missing key: %s", __func__, key);
+ return -ENODEV;
+}
+
+static int __devinit parse_smsm_devicetree(struct device_node *node,
+ void *irq_out_base)
+{
+ uint32_t edge;
+ char *key;
+ int ret;
+ uint32_t irq_offset;
+ uint32_t irq_bitmask;
+ uint32_t irq_line;
+ struct interrupt_config_item *private_irq;
+
+ key = "qcom,smsm-edge";
+ ret = of_property_read_u32(node, key, &edge);
+ if (ret)
+ goto missing_key;
+ SMD_DBG("%s: %s = %d", __func__, key, edge);
+
+ key = "qcom,smsm-irq-offset";
+ ret = of_property_read_u32(node, key, &irq_offset);
+ if (ret)
+ goto missing_key;
+ SMD_DBG("%s: %s = %x", __func__, key, irq_offset);
+
+ key = "qcom,smsm-irq-bitmask";
+ ret = of_property_read_u32(node, key, &irq_bitmask);
+ if (ret)
+ goto missing_key;
+ SMD_DBG("%s: %s = %x", __func__, key, irq_bitmask);
+
+ key = "interrupts";
+ irq_line = irq_of_parse_and_map(node, 0);
+ if (!irq_line)
+ goto missing_key;
+ SMD_DBG("%s: %s = %d", __func__, key, irq_line);
+
+ private_irq = &private_intr_config[edge_to_pids[edge].remote_pid].smsm;
+ private_irq->out_bit_pos = irq_bitmask;
+ private_irq->out_offset = irq_offset;
+ private_irq->out_base = irq_out_base;
+ private_irq->irq_id = irq_line;
+
+ ret = request_irq(irq_line,
+ private_irq->irq_handler,
+ IRQF_TRIGGER_RISING,
+ "smsm_dev",
+ NULL);
+ if (ret < 0) {
+ pr_err("%s: request_irq() failed on %d\n", __func__, irq_line);
+ return ret;
+ } else {
+ ret = enable_irq_wake(irq_line);
+ if (ret < 0)
+ pr_err("%s: enable_irq_wake() failed on %d\n", __func__,
+ irq_line);
+ }
+
+ return 0;
+
+missing_key:
+ pr_err("%s: missing key: %s", __func__, key);
+ return -ENODEV;
+}
+
+static void __devinit unparse_smd_devicetree(struct device_node *node)
+{
+ uint32_t irq_line;
+
+ irq_line = irq_of_parse_and_map(node, 0);
+
+ free_irq(irq_line, NULL);
+}
+
+static void __devinit unparse_smsm_devicetree(struct device_node *node)
+{
+ uint32_t irq_line;
+
+ irq_line = irq_of_parse_and_map(node, 0);
+
+ free_irq(irq_line, NULL);
+}
+
+static int __devinit smd_core_devicetree_init(struct platform_device *pdev)
+{
+ char *key;
+ struct resource *r;
+ void *irq_out_base;
+ void *aux_mem_base;
+ uint32_t aux_mem_size;
+ int temp_string_size = 11; /* max 3 digit count */
+ char temp_string[temp_string_size];
+ int count;
+ struct device_node *node;
+ int ret;
+ const char *compatible;
+ int subnode_num = 0;
+
+ disable_smsm_reset_handshake = 1;
+
+ key = "irq-reg-base";
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
+ if (!r) {
+ pr_err("%s: missing '%s'\n", __func__, key);
+ return -ENODEV;
+ }
+ irq_out_base = (void *)(r->start);
+ SMD_DBG("%s: %s = %p", __func__, key, irq_out_base);
+
+ count = 1;
+ while (1) {
+ scnprintf(temp_string, temp_string_size, "aux-mem%d", count);
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ temp_string);
+ if (!r)
+ break;
+
+ ++num_smem_areas;
+ ++count;
+ if (count > 999) {
+ pr_err("%s: max num aux mem regions reached\n",
+ __func__);
+ break;
+ }
+ }
+
+ if (num_smem_areas) {
+ smem_areas = kmalloc(sizeof(struct smem_area) * num_smem_areas,
+ GFP_KERNEL);
+ if (!smem_areas) {
+ pr_err("%s: smem areas kmalloc failed\n", __func__);
+ num_smem_areas = 0;
+ return -ENOMEM;
+ }
+ count = 1;
+ while (1) {
+ scnprintf(temp_string, temp_string_size, "aux-mem%d",
+ count);
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ temp_string);
+ if (!r)
+ break;
+ aux_mem_base = (void *)(r->start);
+ aux_mem_size = (uint32_t)(resource_size(r));
+ SMD_DBG("%s: %s = %p %x", __func__, temp_string,
+ aux_mem_base, aux_mem_size);
+ smem_areas[count - 1].phys_addr = aux_mem_base;
+ smem_areas[count - 1].size = aux_mem_size;
+ smem_areas[count - 1].virt_addr = ioremap_nocache(
+ (unsigned long)(smem_areas[count-1].phys_addr),
+ smem_areas[count - 1].size);
+ if (!smem_areas[count - 1].virt_addr) {
+ pr_err("%s: ioremap_nocache() of addr:%p size: %x\n",
+ __func__,
+ smem_areas[count - 1].phys_addr,
+ smem_areas[count - 1].size);
+ ret = -ENOMEM;
+ goto free_smem_areas;
+ }
+
+ ++count;
+ if (count > 999) {
+ pr_err("%s: max num aux mem regions reached\n",
+ __func__);
+ break;
+ }
+ }
+ sort(smem_areas, num_smem_areas,
+ sizeof(struct smem_area),
+ sort_cmp_func, NULL);
+ }
+
+ for_each_child_of_node(pdev->dev.of_node, node) {
+ compatible = of_get_property(node, "compatible", NULL);
+ if (!strcmp(compatible, "qcom,smd")) {
+ ret = parse_smd_devicetree(node, irq_out_base);
+ if (ret)
+ goto rollback_subnodes;
+ } else if (!strcmp(compatible, "qcom,smsm")) {
+ ret = parse_smsm_devicetree(node, irq_out_base);
+ if (ret)
+ goto rollback_subnodes;
+ } else {
+ pr_err("%s: invalid child node named: %s\n", __func__,
+ compatible);
+ ret = -ENODEV;
+ goto rollback_subnodes;
+ }
+ ++subnode_num;
+ }
+
+ return 0;
+
+rollback_subnodes:
+ count = 0;
+ for_each_child_of_node(pdev->dev.of_node, node) {
+ if (count >= subnode_num)
+ break;
+ ++count;
+ compatible = of_get_property(node, "compatible", NULL);
+ if (!strcmp(compatible, "qcom,smd"))
+ unparse_smd_devicetree(node);
+ else
+ unparse_smsm_devicetree(node);
+ }
+free_smem_areas:
+ num_smem_areas = 0;
+ kfree(smem_areas);
+ smem_areas = NULL;
+ return ret;
+}
+
static int __devinit msm_smd_probe(struct platform_device *pdev)
{
int ret;
@@ -3522,8 +3813,12 @@
if (pdev) {
if (pdev->dev.of_node) {
- pr_err("SMD: Device tree not currently supported\n");
- return -ENODEV;
+ ret = smd_core_devicetree_init(pdev);
+ if (ret) {
+ pr_err("%s: device tree init failed\n",
+ __func__);
+ return ret;
+ }
} else if (pdev->dev.platform_data) {
ret = smd_core_platform_init(pdev);
if (ret) {
@@ -3600,11 +3895,17 @@
}
late_initcall(modem_restart_late_init);
+static struct of_device_id msm_smem_match_table[] = {
+ { .compatible = "qcom,smem" },
+ {},
+};
+
static struct platform_driver msm_smd_driver = {
.probe = msm_smd_probe,
.driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
+ .of_match_table = msm_smem_match_table,
},
};
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index b61604a..3af066d 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -23,12 +23,14 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/percpu.h>
+#include <linux/mm.h>
#include <asm/localtimer.h>
#include <asm/mach/time.h>
#include <asm/hardware/gic.h>
#include <asm/sched_clock.h>
#include <asm/smp_plat.h>
+#include <asm/user_accessible_timer.h>
#include <mach/msm_iomap.h>
#include <mach/irqs.h>
#include <mach/socinfo.h>
@@ -1161,6 +1163,16 @@
}
msm_sched_clock_init();
+ if (use_user_accessible_timers()) {
+ if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_apq8064()) {
+ struct msm_clock *gtclock = &msm_clocks[MSM_CLOCK_GPT];
+ void __iomem *addr = gtclock->regbase +
+ TIMER_COUNT_VAL + global_timer_offset;
+ setup_user_timer_offset(virt_to_phys(addr)&0xfff);
+ set_user_accessible_timer_flag(true);
+ }
+ }
+
#ifdef ARCH_HAS_READ_CURRENT_TIMER
if (is_smp()) {
__raw_writel(1,
diff --git a/arch/arm/mach-msm/timer_page.c b/arch/arm/mach-msm/timer_page.c
new file mode 100644
index 0000000..24d2a35
--- /dev/null
+++ b/arch/arm/mach-msm/timer_page.c
@@ -0,0 +1,36 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mm.h>
+#include <linux/export.h>
+#include <asm/user_accessible_timer.h>
+#include "mach/socinfo.h"
+#include "mach/msm_iomap.h"
+
+#include "timer.h"
+
+inline int get_timer_page_address(void)
+{
+ if (!use_user_accessible_timers())
+ return ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE;
+
+ if (cpu_is_msm8960())
+ return MSM8960_TMR0_PHYS;
+ else if (cpu_is_msm8930())
+ return MSM8930_TMR0_PHYS;
+ else if (cpu_is_apq8064())
+ return APQ8064_TMR0_PHYS;
+ else
+ return ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE;
+}
+EXPORT_SYMBOL(get_timer_page_address);
+
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 57f41ca..e2cd0120 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -771,6 +771,9 @@
printk(KERN_NOTICE "Virtual kernel memory layout:\n"
" vector : 0x%08lx - 0x%08lx (%4ld kB)\n"
+#ifdef CONFIG_ARM_USE_USER_ACCESSIBLE_TIMERS
+ " timers : 0x%08lx - 0x%08lx (%4ld kB)\n"
+#endif
#ifdef CONFIG_HAVE_TCM
" DTCM : 0x%08lx - 0x%08lx (%4ld kB)\n"
" ITCM : 0x%08lx - 0x%08lx (%4ld kB)\n"
@@ -791,6 +794,11 @@
MLK(UL(CONFIG_VECTORS_BASE), UL(CONFIG_VECTORS_BASE) +
(PAGE_SIZE)),
+#ifdef CONFIG_ARM_USE_USER_ACCESSIBLE_TIMERS
+ MLK(UL(CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE),
+ UL(CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE)
+ + (PAGE_SIZE)),
+#endif
#ifdef CONFIG_HAVE_TCM
MLK(DTCM_OFFSET, (unsigned long) dtcm_end),
MLK(ITCM_OFFSET, (unsigned long) itcm_end),
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index bae23b0..1cb6cba 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -33,6 +33,8 @@
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
+#include <asm/user_accessible_timer.h>
+
#include "mm.h"
/*
@@ -309,6 +311,13 @@
.prot_l1 = PMD_TYPE_TABLE,
.domain = DOMAIN_KERNEL,
},
+ [MT_DEVICE_USER_ACCESSIBLE] = {
+ .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED |
+ L_PTE_SHARED | L_PTE_USER | L_PTE_RDONLY,
+ .prot_l1 = PMD_TYPE_TABLE,
+ .prot_sect = PROT_SECT_DEVICE | PMD_SECT_S,
+ .domain = DOMAIN_IO,
+ },
};
const struct mem_type *get_mem_type(unsigned int type)
@@ -764,7 +773,9 @@
const struct mem_type *type;
pgd_t *pgd;
- if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) {
+ if ((md->virtual != vectors_base() &&
+ md->virtual != get_user_accessible_timers_base()) &&
+ md->virtual < TASK_SIZE) {
printk(KERN_WARNING "BUG: not creating mapping for 0x%08llx"
" at 0x%08lx in user region\n",
(long long)__pfn_to_phys((u64)md->pfn), md->virtual);
@@ -1203,6 +1214,20 @@
mdesc->map_io();
fill_pmd_gaps();
+ if (use_user_accessible_timers()) {
+ /*
+ * Generate a mapping for the timer page.
+ */
+ int page_addr = get_timer_page_address();
+ if (page_addr != ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE) {
+ map.pfn = __phys_to_pfn(page_addr);
+ map.virtual = CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE;
+ map.length = PAGE_SIZE;
+ map.type = MT_DEVICE_USER_ACCESSIBLE;
+ create_mapping(&map, false);
+ }
+ }
+
/*
* Finally flush the caches and tlb to ensure that we're in a
* consistent state wrt the writebuffer. This also ensures that
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 28d0565..de3cf52 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -29,6 +29,7 @@
#define IN_BUF_SIZE 16384
#define MAX_IN_BUF_SIZE 32768
#define MAX_SYNC_OBJ_NAME_SIZE 32
+#define UINT32_MAX UINT_MAX
/* Size of the buffer used for deframing a packet
reveived from the PC tool*/
#define HDLC_MAX 4096
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 19c6ed2..7b17ce4 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -358,7 +358,7 @@
}
void diag_add_reg(int j, struct bindpkt_params *params,
- int *success, int *count_entries)
+ int *success, unsigned int *count_entries)
{
*success = 1;
driver->table[j].cmd_code = params->cmd_code;
@@ -399,79 +399,153 @@
long diagchar_ioctl(struct file *filp,
unsigned int iocmd, unsigned long ioarg)
{
- int i, j, count_entries = 0, temp;
- int success = -1;
+ int i, j, temp, success = -1, status;
+ unsigned int count_entries = 0, interim_count = 0;
void *temp_buf;
uint16_t support_list = 0;
- struct diag_dci_client_tbl *params =
- kzalloc(sizeof(struct diag_dci_client_tbl), GFP_KERNEL);
+ struct diag_dci_client_tbl *dci_params;
struct diag_dci_health_stats stats;
- int status;
if (iocmd == DIAG_IOCTL_COMMAND_REG) {
- struct bindpkt_params_per_process *pkt_params =
- (struct bindpkt_params_per_process *) ioarg;
+ struct bindpkt_params_per_process pkt_params;
+ struct bindpkt_params *params;
+ struct bindpkt_params *head_params;
+ if (copy_from_user(&pkt_params, (void *)ioarg,
+ sizeof(struct bindpkt_params_per_process))) {
+ return -EFAULT;
+ }
+ if ((UINT32_MAX/sizeof(struct bindpkt_params)) <
+ pkt_params.count) {
+ pr_warning("diag: integer overflow while multiply\n");
+ return -EFAULT;
+ }
+ params = kzalloc(pkt_params.count*sizeof(
+ struct bindpkt_params), GFP_KERNEL);
+ if (!params) {
+ pr_err("diag: unable to alloc memory\n");
+ return -ENOMEM;
+ } else
+ head_params = params;
+
+ if (copy_from_user(params, pkt_params.params,
+ pkt_params.count*sizeof(struct bindpkt_params))) {
+ kfree(head_params);
+ return -EFAULT;
+ }
mutex_lock(&driver->diagchar_mutex);
for (i = 0; i < diag_max_reg; i++) {
if (driver->table[i].process_id == 0) {
- diag_add_reg(i, pkt_params->params,
- &success, &count_entries);
- if (pkt_params->count > count_entries) {
- pkt_params->params++;
+ diag_add_reg(i, params, &success,
+ &count_entries);
+ if (pkt_params.count > count_entries) {
+ params++;
} else {
mutex_unlock(&driver->diagchar_mutex);
+ kfree(head_params);
return success;
}
}
}
if (i < diag_threshold_reg) {
/* Increase table size by amount required */
- diag_max_reg += pkt_params->count -
+ if (pkt_params.count >= count_entries) {
+ interim_count = pkt_params.count -
count_entries;
+ } else {
+ pr_warning("diag: error in params count\n");
+ kfree(head_params);
+ mutex_unlock(&driver->diagchar_mutex);
+ return -EFAULT;
+ }
+ if (UINT32_MAX - diag_max_reg >=
+ interim_count) {
+ diag_max_reg += interim_count;
+ } else {
+ pr_warning("diag: Integer overflow\n");
+ kfree(head_params);
+ mutex_unlock(&driver->diagchar_mutex);
+ return -EFAULT;
+ }
/* Make sure size doesnt go beyond threshold */
if (diag_max_reg > diag_threshold_reg) {
diag_max_reg = diag_threshold_reg;
pr_info("diag: best case memory allocation\n");
}
+ if (UINT32_MAX/sizeof(struct diag_master_table) <
+ diag_max_reg) {
+ pr_warning("diag: integer overflow\n");
+ kfree(head_params);
+ mutex_unlock(&driver->diagchar_mutex);
+ return -EFAULT;
+ }
temp_buf = krealloc(driver->table,
diag_max_reg*sizeof(struct
diag_master_table), GFP_KERNEL);
if (!temp_buf) {
- diag_max_reg -= pkt_params->count -
- count_entries;
- pr_alert("diag: Insufficient memory for reg.");
+ pr_alert("diag: Insufficient memory for reg.\n");
mutex_unlock(&driver->diagchar_mutex);
+
+ if (pkt_params.count >= count_entries) {
+ interim_count = pkt_params.count -
+ count_entries;
+ } else {
+ pr_warning("diag: params count error\n");
+ mutex_unlock(&driver->diagchar_mutex);
+ kfree(head_params);
+ return -EFAULT;
+ }
+ if (diag_max_reg >= interim_count) {
+ diag_max_reg -= interim_count;
+ } else {
+ pr_warning("diag: Integer underflow\n");
+ mutex_unlock(&driver->diagchar_mutex);
+ kfree(head_params);
+ return -EFAULT;
+ }
+ kfree(head_params);
return 0;
} else {
driver->table = temp_buf;
}
for (j = i; j < diag_max_reg; j++) {
- diag_add_reg(j, pkt_params->params,
- &success, &count_entries);
- if (pkt_params->count > count_entries) {
- pkt_params->params++;
+ diag_add_reg(j, params, &success,
+ &count_entries);
+ if (pkt_params.count > count_entries) {
+ params++;
} else {
mutex_unlock(&driver->diagchar_mutex);
+ kfree(head_params);
return success;
}
}
+ kfree(head_params);
mutex_unlock(&driver->diagchar_mutex);
} else {
mutex_unlock(&driver->diagchar_mutex);
+ kfree(head_params);
pr_err("Max size reached, Pkt Registration failed for"
" Process %d", current->tgid);
}
success = 0;
} else if (iocmd == DIAG_IOCTL_GET_DELAYED_RSP_ID) {
- struct diagpkt_delay_params *delay_params =
- (struct diagpkt_delay_params *) ioarg;
-
- if ((delay_params->rsp_ptr) &&
- (delay_params->size == sizeof(delayed_rsp_id)) &&
- (delay_params->num_bytes_ptr)) {
- *((uint16_t *)delay_params->rsp_ptr) =
- DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
- *(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);
+ struct diagpkt_delay_params delay_params;
+ uint16_t interim_rsp_id;
+ int interim_size;
+ if (copy_from_user(&delay_params, (void *)ioarg,
+ sizeof(struct diagpkt_delay_params)))
+ return -EFAULT;
+ if ((delay_params.rsp_ptr) &&
+ (delay_params.size == sizeof(delayed_rsp_id)) &&
+ (delay_params.num_bytes_ptr)) {
+ interim_rsp_id = DIAGPKT_NEXT_DELAYED_RSP_ID(
+ delayed_rsp_id);
+ if (copy_to_user((void *)delay_params.rsp_ptr,
+ &interim_rsp_id, sizeof(uint16_t)))
+ return -EFAULT;
+ interim_size = sizeof(delayed_rsp_id);
+ if (copy_to_user((void *)delay_params.num_bytes_ptr,
+ &interim_size, sizeof(int)))
+ return -EFAULT;
success = 0;
}
} else if (iocmd == DIAG_IOCTL_DCI_REG) {
@@ -479,7 +553,13 @@
return DIAG_DCI_NO_REG;
if (driver->num_dci_client >= MAX_DCI_CLIENTS)
return DIAG_DCI_NO_REG;
- if (copy_from_user(params, (void *)ioarg,
+ dci_params = kzalloc(sizeof(struct diag_dci_client_tbl),
+ GFP_KERNEL);
+ if (dci_params == NULL) {
+ pr_err("diag: unable to alloc memory\n");
+ return -ENOMEM;
+ }
+ if (copy_from_user(dci_params, (void *)ioarg,
sizeof(struct diag_dci_client_tbl)))
return -EFAULT;
mutex_lock(&driver->dci_mutex);
@@ -492,9 +572,9 @@
if (driver->dci_client_tbl[i].client == NULL) {
driver->dci_client_tbl[i].client = current;
driver->dci_client_tbl[i].list =
- params->list;
+ dci_params->list;
driver->dci_client_tbl[i].signal_type =
- params->signal_type;
+ dci_params->signal_type;
create_dci_log_mask_tbl(driver->
dci_client_tbl[i].dci_log_mask);
create_dci_event_mask_tbl(driver->
@@ -512,6 +592,7 @@
}
}
mutex_unlock(&driver->dci_mutex);
+ kfree(dci_params);
return driver->dci_client_id;
} else if (iocmd == DIAG_IOCTL_DCI_DEINIT) {
success = -1;
@@ -536,25 +617,29 @@
} else if (iocmd == DIAG_IOCTL_DCI_SUPPORT) {
if (driver->ch_dci)
support_list = support_list | DIAG_CON_MPSS;
- *(uint16_t *)ioarg = support_list;
+ if (copy_to_user((void *)ioarg, &support_list,
+ sizeof(uint16_t)))
+ return -EFAULT;
return DIAG_DCI_NO_ERROR;
} else if (iocmd == DIAG_IOCTL_DCI_HEALTH_STATS) {
if (copy_from_user(&stats, (void *)ioarg,
sizeof(struct diag_dci_health_stats)))
return -EFAULT;
for (i = 0; i < MAX_DCI_CLIENTS; i++) {
- params = &(driver->dci_client_tbl[i]);
- if (params->client &&
- params->client->tgid == current->tgid) {
- stats.dropped_logs = params->dropped_logs;
- stats.dropped_events = params->dropped_events;
- stats.received_logs = params->received_logs;
- stats.received_events = params->received_events;
+ dci_params = &(driver->dci_client_tbl[i]);
+ if (dci_params->client &&
+ dci_params->client->tgid == current->tgid) {
+ stats.dropped_logs = dci_params->dropped_logs;
+ stats.dropped_events =
+ dci_params->dropped_events;
+ stats.received_logs = dci_params->received_logs;
+ stats.received_events =
+ dci_params->received_events;
if (stats.reset_status) {
- params->dropped_logs = 0;
- params->dropped_events = 0;
- params->received_logs = 0;
- params->received_events = 0;
+ dci_params->dropped_logs = 0;
+ dci_params->dropped_events = 0;
+ dci_params->received_logs = 0;
+ dci_params->received_events = 0;
}
break;
}
@@ -567,7 +652,7 @@
for (i = 0; i < driver->num_clients; i++)
if (driver->client_map[i].pid == current->tgid)
break;
- if (i == -1)
+ if (i == driver->num_clients)
return -EINVAL;
driver->data_ready[i] |= DEINIT_TYPE;
wake_up_interruptible(&driver->wait_q);
@@ -1068,7 +1153,7 @@
struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
void *buf_copy = NULL;
- int payload_size;
+ unsigned int payload_size;
#ifdef CONFIG_DIAG_OVER_USB
if (((driver->logging_mode == USB_MODE) && (!driver->usb_connected)) ||
(driver->logging_mode == NO_LOGGING_MODE)) {
@@ -1079,8 +1164,17 @@
/* Get the packet type F3/log/event/Pkt response */
err = copy_from_user((&pkt_type), buf, 4);
/* First 4 bytes indicate the type of payload - ignore these */
+ if (count < 4) {
+ pr_err("diag: Client sending short data\n");
+ return -EBADMSG;
+ }
payload_size = count - 4;
-
+ if (payload_size > USER_SPACE_DATA) {
+ pr_err("diag: Dropping packet, packet payload size crosses 8KB limit. Current payload size %d\n",
+ payload_size);
+ driver->dropped_count++;
+ return -EBADMSG;
+ }
if (pkt_type == DCI_DATA_TYPE) {
err = copy_from_user(driver->user_space_data, buf + 4,
payload_size);
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index 20f84d6..8699178 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -616,18 +616,15 @@
if (end < start)
goto out;
- down_read(&mm->mmap_sem);
vma = find_vma(mm, start);
if (vma && vma->vm_start < end) {
if (start < vma->vm_start)
- goto out_up;
+ goto out;
if (end > vma->vm_end)
- goto out_up;
+ goto out;
ret = 0;
}
-out_up:
- up_read(&mm->mmap_sem);
out:
return ret;
}
@@ -645,20 +642,12 @@
unsigned long start, end;
struct ion_handle *handle = NULL;
int ret;
+ struct mm_struct *mm = current->active_mm;
if (copy_from_user(&data, (void __user *)arg,
sizeof(struct ion_flush_data)))
return -EFAULT;
- start = (unsigned long) data.vaddr;
- end = (unsigned long) data.vaddr + data.length;
-
- if (check_vaddr_bounds(start, end)) {
- pr_err("%s: virtual address %p is out of bounds\n",
- __func__, data.vaddr);
- return -EINVAL;
- }
-
if (!data.handle) {
handle = ion_import_dma_buf(client, data.fd);
if (IS_ERR(handle)) {
@@ -668,11 +657,27 @@
}
}
+ down_read(&mm->mmap_sem);
+
+ start = (unsigned long) data.vaddr;
+ end = (unsigned long) data.vaddr + data.length;
+
+ if (check_vaddr_bounds(start, end)) {
+ up_read(&mm->mmap_sem);
+ pr_err("%s: virtual address %p is out of bounds\n",
+ __func__, data.vaddr);
+ if (!data.handle)
+ ion_free(client, handle);
+ return -EINVAL;
+ }
+
ret = ion_do_cache_op(client,
data.handle ? data.handle : handle,
data.vaddr, data.offset, data.length,
cmd);
+ up_read(&mm->mmap_sem);
+
if (!data.handle)
ion_free(client, handle);
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index ca2ad19..c040bf3 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -193,6 +193,52 @@
}
EXPORT_SYMBOL(kgsl_cancel_events);
+int kgsl_memfree_hist_init(void)
+{
+ void *base;
+
+ base = kzalloc(KGSL_MEMFREE_HIST_SIZE, GFP_KERNEL);
+ kgsl_driver.memfree_hist.base_hist_rb = base;
+ if (base == NULL)
+ return -ENOMEM;
+ kgsl_driver.memfree_hist.size = KGSL_MEMFREE_HIST_SIZE;
+ kgsl_driver.memfree_hist.wptr = base;
+ return 0;
+}
+
+void kgsl_memfree_hist_exit(void)
+{
+ kfree(kgsl_driver.memfree_hist.base_hist_rb);
+ kgsl_driver.memfree_hist.base_hist_rb = NULL;
+}
+
+void kgsl_memfree_hist_set_event(unsigned int pid, unsigned int gpuaddr,
+ unsigned int size, int flags)
+{
+ struct kgsl_memfree_hist_elem *p;
+
+ void *base = kgsl_driver.memfree_hist.base_hist_rb;
+ int rbsize = kgsl_driver.memfree_hist.size;
+
+ if (base == NULL)
+ return;
+
+ mutex_lock(&kgsl_driver.memfree_hist_mutex);
+ p = kgsl_driver.memfree_hist.wptr;
+ p->pid = pid;
+ p->gpuaddr = gpuaddr;
+ p->size = size;
+ p->flags = flags;
+
+ kgsl_driver.memfree_hist.wptr++;
+ if ((void *)kgsl_driver.memfree_hist.wptr >= base+rbsize) {
+ kgsl_driver.memfree_hist.wptr =
+ (struct kgsl_memfree_hist_elem *)base;
+ }
+ mutex_unlock(&kgsl_driver.memfree_hist_mutex);
+}
+
+
/* kgsl_get_mem_entry - get the mem_entry structure for the specified object
* @device - Pointer to the device structure
* @ptbase - the pagetable base of the object
@@ -1359,6 +1405,13 @@
if (entry) {
trace_kgsl_mem_free(entry);
+
+ kgsl_memfree_hist_set_event(
+ entry->priv->pid,
+ entry->memdesc.gpuaddr,
+ entry->memdesc.size,
+ entry->memdesc.flags);
+
kgsl_mem_entry_detach_process(entry);
} else {
KGSL_CORE_ERR("invalid gpuaddr %08x\n", param->gpuaddr);
@@ -2338,6 +2391,8 @@
.process_mutex = __MUTEX_INITIALIZER(kgsl_driver.process_mutex),
.ptlock = __SPIN_LOCK_UNLOCKED(kgsl_driver.ptlock),
.devlock = __MUTEX_INITIALIZER(kgsl_driver.devlock),
+ .memfree_hist_mutex =
+ __MUTEX_INITIALIZER(kgsl_driver.memfree_hist_mutex),
};
EXPORT_SYMBOL(kgsl_driver);
@@ -2668,6 +2723,7 @@
kgsl_driver.class = NULL;
}
+ kgsl_memfree_hist_exit();
unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX);
}
@@ -2739,6 +2795,9 @@
goto err;
}
+ if (kgsl_memfree_hist_init())
+ KGSL_CORE_ERR("failed to init memfree_hist");
+
return 0;
err:
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index 17a5b67..d22cb6d 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -71,6 +71,23 @@
#define KGSL_STATS_ADD(_size, _stat, _max) \
do { _stat += (_size); if (_stat > _max) _max = _stat; } while (0)
+
+#define KGSL_MEMFREE_HIST_SIZE ((int)(PAGE_SIZE * 2))
+
+struct kgsl_memfree_hist_elem {
+ unsigned int pid;
+ unsigned int gpuaddr;
+ unsigned int size;
+ unsigned int flags;
+};
+
+struct kgsl_memfree_hist {
+ void *base_hist_rb;
+ unsigned int size;
+ struct kgsl_memfree_hist_elem *wptr;
+};
+
+
struct kgsl_device;
struct kgsl_driver {
@@ -98,6 +115,9 @@
void *ptpool;
+ struct mutex memfree_hist_mutex;
+ struct kgsl_memfree_hist memfree_hist;
+
struct {
unsigned int vmalloc;
unsigned int vmalloc_max;
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index f731cfc..07a5ff4 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -106,6 +106,52 @@
KGSL_DEBUGFS_LOG(mem_log);
KGSL_DEBUGFS_LOG(pwr_log);
+static int memfree_hist_print(struct seq_file *s, void *unused)
+{
+ void *base = kgsl_driver.memfree_hist.base_hist_rb;
+
+ struct kgsl_memfree_hist_elem *wptr = kgsl_driver.memfree_hist.wptr;
+ struct kgsl_memfree_hist_elem *p;
+ char str[16];
+
+ seq_printf(s, "%8s %8s %8s %11s\n",
+ "pid", "gpuaddr", "size", "flags");
+
+ mutex_lock(&kgsl_driver.memfree_hist_mutex);
+ p = wptr;
+ for (;;) {
+ kgsl_get_memory_usage(str, sizeof(str), p->flags);
+ /*
+ * if the ring buffer is not filled up yet
+ * all its empty elems have size==0
+ * just skip them ...
+ */
+ if (p->size)
+ seq_printf(s, "%8d %08x %8d %11s\n",
+ p->pid, p->gpuaddr, p->size, str);
+ p++;
+ if ((void *)p >= base + kgsl_driver.memfree_hist.size)
+ p = (struct kgsl_memfree_hist_elem *) base;
+
+ if (p == kgsl_driver.memfree_hist.wptr)
+ break;
+ }
+ mutex_unlock(&kgsl_driver.memfree_hist_mutex);
+ return 0;
+}
+
+static int memfree_hist_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, memfree_hist_print, inode->i_private);
+}
+
+static const struct file_operations memfree_hist_fops = {
+ .open = memfree_hist_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
void kgsl_device_debugfs_init(struct kgsl_device *device)
{
if (kgsl_debugfs_dir && !IS_ERR(kgsl_debugfs_dir))
@@ -131,6 +177,8 @@
&mem_log_fops);
debugfs_create_file("log_level_pwr", 0644, device->d_debugfs, device,
&pwr_log_fops);
+ debugfs_create_file("memfree_history", 0444, device->d_debugfs, device,
+ &memfree_hist_fops);
/* Create postmortem dump control files */
diff --git a/drivers/iommu/msm_iommu-v2.c b/drivers/iommu/msm_iommu-v2.c
index 9d88fdd..425eb8a 100644
--- a/drivers/iommu/msm_iommu-v2.c
+++ b/drivers/iommu/msm_iommu-v2.c
@@ -148,6 +148,9 @@
return ret;
}
+/*
+ * May only be called for non-secure iommus
+ */
static void __reset_iommu(void __iomem *base)
{
int i, smt_size;
@@ -170,6 +173,9 @@
mb();
}
+/*
+ * May only be called for non-secure iommus
+ */
static void __program_iommu(void __iomem *base,
struct msm_iommu_bfb_settings *bfb_settings)
{
@@ -223,14 +229,14 @@
static void __program_context(void __iomem *base, int ctx, int ncb,
phys_addr_t pgtable, int redirect,
- u32 *sids, int len)
+ u32 *sids, int len, bool is_secure)
{
unsigned int prrr, nmrr;
unsigned int pn;
int i, j, found, num = 0, smt_size;
__reset_context(base, ctx);
- smt_size = GET_IDR0_NUMSMRG(base);
+
pn = pgtable >> CB_TTBR0_ADDR_SHIFT;
SET_TTBCR(base, ctx, 0);
SET_CB_TTBR0_ADDR(base, ctx, pn);
@@ -266,41 +272,44 @@
SET_CB_TTBR0_RGN(base, ctx, 1); /* WB, WA */
}
- /* Program the M2V tables for this context */
- for (i = 0; i < len / sizeof(*sids); i++) {
- for (; num < smt_size; num++)
- if (GET_SMR_VALID(base, num) == 0)
- break;
- BUG_ON(num >= smt_size);
+ if (!is_secure) {
+ smt_size = GET_IDR0_NUMSMRG(base);
+ /* Program the M2V tables for this context */
+ for (i = 0; i < len / sizeof(*sids); i++) {
+ for (; num < smt_size; num++)
+ if (GET_SMR_VALID(base, num) == 0)
+ break;
+ BUG_ON(num >= smt_size);
- SET_SMR_VALID(base, num, 1);
- SET_SMR_MASK(base, num, 0);
- SET_SMR_ID(base, num, sids[i]);
+ SET_SMR_VALID(base, num, 1);
+ SET_SMR_MASK(base, num, 0);
+ SET_SMR_ID(base, num, sids[i]);
- SET_S2CR_N(base, num, 0);
- SET_S2CR_CBNDX(base, num, ctx);
- SET_S2CR_MEMATTR(base, num, 0x0A);
- /* Set security bit override to be Non-secure */
- SET_S2CR_NSCFG(base, num, 3);
+ SET_S2CR_N(base, num, 0);
+ SET_S2CR_CBNDX(base, num, ctx);
+ SET_S2CR_MEMATTR(base, num, 0x0A);
+ /* Set security bit override to be Non-secure */
+ SET_S2CR_NSCFG(base, num, 3);
+ }
+ SET_CBAR_N(base, ctx, 0);
+
+ /* Stage 1 Context with Stage 2 bypass */
+ SET_CBAR_TYPE(base, ctx, 1);
+
+ /* Route page faults to the non-secure interrupt */
+ SET_CBAR_IRPTNDX(base, ctx, 1);
+
+ /* Set VMID to non-secure HLOS */
+ SET_CBAR_VMID(base, ctx, 3);
+
+ /* Bypass is treated as inner-shareable */
+ SET_CBAR_BPSHCFG(base, ctx, 2);
+
+ /* Do not downgrade memory attributes */
+ SET_CBAR_MEMATTR(base, ctx, 0x0A);
+
}
- SET_CBAR_N(base, ctx, 0);
-
- /* Stage 1 Context with Stage 2 bypass */
- SET_CBAR_TYPE(base, ctx, 1);
-
- /* Route page faults to the non-secure interrupt */
- SET_CBAR_IRPTNDX(base, ctx, 1);
-
- /* Set VMID to non-secure HLOS */
- SET_CBAR_VMID(base, ctx, 3);
-
- /* Bypass is treated as inner-shareable */
- SET_CBAR_BPSHCFG(base, ctx, 2);
-
- /* Do not downgrade memory attributes */
- SET_CBAR_MEMATTR(base, ctx, 0x0A);
-
/* Find if this page table is used elsewhere, and re-use ASID */
found = 0;
for (i = 0; i < ncb; i++)
@@ -399,6 +408,7 @@
struct msm_iommu_ctx_drvdata *ctx_drvdata;
struct msm_iommu_ctx_drvdata *tmp_drvdata;
int ret;
+ int is_secure;
mutex_lock(&msm_iommu_lock);
@@ -426,6 +436,8 @@
goto fail;
}
+ is_secure = iommu_drvdata->sec_id != -1;
+
ret = regulator_enable(iommu_drvdata->gdsc);
if (ret)
goto fail;
@@ -436,13 +448,25 @@
goto fail;
}
- if (!msm_iommu_ctx_attached(dev->parent))
- __program_iommu(iommu_drvdata->base,
+ if (!msm_iommu_ctx_attached(dev->parent)) {
+ if (!is_secure) {
+ __program_iommu(iommu_drvdata->base,
iommu_drvdata->bfb_settings);
+ } else {
+ ret = msm_iommu_sec_program_iommu(
+ iommu_drvdata->sec_id);
+ if (ret) {
+ regulator_disable(iommu_drvdata->gdsc);
+ __disable_clocks(iommu_drvdata);
+ goto fail;
+ }
+ }
+ }
__program_context(iommu_drvdata->base, ctx_drvdata->num,
iommu_drvdata->ncb, __pa(priv->pt.fl_table),
- priv->pt.redirect, ctx_drvdata->sids, ctx_drvdata->nsid);
+ priv->pt.redirect, ctx_drvdata->sids, ctx_drvdata->nsid,
+ is_secure);
__disable_clocks(iommu_drvdata);
list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
@@ -460,6 +484,7 @@
struct msm_iommu_drvdata *iommu_drvdata;
struct msm_iommu_ctx_drvdata *ctx_drvdata;
int ret;
+ int is_secure;
mutex_lock(&msm_iommu_lock);
priv = domain->priv;
@@ -475,11 +500,14 @@
if (ret)
goto fail;
+ is_secure = iommu_drvdata->sec_id != -1;
+
SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num,
GET_CB_CONTEXTIDR_ASID(iommu_drvdata->base, ctx_drvdata->num));
__reset_context(iommu_drvdata->base, ctx_drvdata->num);
- __release_smg(iommu_drvdata->base, ctx_drvdata->num);
+ if (!is_secure)
+ __release_smg(iommu_drvdata->base, ctx_drvdata->num);
__disable_clocks(iommu_drvdata);
diff --git a/drivers/iommu/msm_iommu_sec.c b/drivers/iommu/msm_iommu_sec.c
index a89c4a8..72ec4a6 100644
--- a/drivers/iommu/msm_iommu_sec.c
+++ b/drivers/iommu/msm_iommu_sec.c
@@ -128,7 +128,7 @@
return ret;
}
-static int msm_iommu_sec_program_iommu(int sec_id)
+int msm_iommu_sec_program_iommu(int sec_id)
{
struct msm_scm_sec_cfg {
unsigned int id;
diff --git a/drivers/media/video/msm_vidc/msm_smem.c b/drivers/media/video/msm_vidc/msm_smem.c
index 3dd2193..83f33a1 100644
--- a/drivers/media/video/msm_vidc/msm_smem.c
+++ b/drivers/media/video/msm_vidc/msm_smem.c
@@ -24,7 +24,7 @@
static int get_device_address(struct ion_client *clnt,
struct ion_handle *hndl, int domain_num, int partition_num,
unsigned long align, unsigned long *iova,
- unsigned long *buffer_size)
+ unsigned long *buffer_size, int flags)
{
int rc;
if (!iova || !buffer_size || !hndl || !clnt) {
@@ -36,25 +36,39 @@
align = 4096;
dprintk(VIDC_DBG, "domain: %d, partition: %d\n",
domain_num, partition_num);
+ if (flags & SMEM_SECURE) {
+ if (flags & SMEM_INPUT)
+ rc = msm_ion_secure_buffer(clnt, hndl, 0x1, 0);
+ else
+ rc = msm_ion_secure_buffer(clnt, hndl, 0x2, 0);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to secure memory\n");
+ goto mem_secure_failed;
+ }
+ }
rc = ion_map_iommu(clnt, hndl, domain_num, partition_num, align,
0, iova, buffer_size, 0, 0);
if (rc)
dprintk(VIDC_ERR,
"ion_map_iommu failed(%d).domain: %d,partition: %d\n",
rc, domain_num, partition_num);
-
+mem_secure_failed:
return rc;
}
static void put_device_address(struct ion_client *clnt,
- struct ion_handle *hndl, int domain_num, int partition_num)
+ struct ion_handle *hndl, int domain_num, int partition_num, int flags)
{
ion_unmap_iommu(clnt, hndl, domain_num, partition_num);
+ if (flags & SMEM_SECURE) {
+ if (msm_ion_unsecure_buffer(clnt, hndl))
+ dprintk(VIDC_ERR, "Failed to unsecure memory\n");
+ }
}
static int ion_user_to_kernel(struct smem_client *client,
int fd, u32 offset, int domain, int partition,
- struct msm_smem *mem)
+ struct msm_smem *mem, int flags)
{
struct ion_handle *hndl;
unsigned long iova = 0;
@@ -70,8 +84,9 @@
mem->kvaddr = NULL;
mem->domain = domain;
mem->partition_num = partition;
+ mem->flags = flags;
rc = get_device_address(client->clnt, hndl, mem->domain,
- mem->partition_num, 4096, &iova, &buffer_size);
+ mem->partition_num, 4096, &iova, &buffer_size, flags);
if (rc) {
dprintk(VIDC_ERR, "Failed to get device address: %d\n", rc);
goto fail_device_address;
@@ -101,11 +116,16 @@
unsigned long ionflags = 0;
unsigned long heap_mask = 0;
int rc = 0;
- if (flags == SMEM_CACHED)
+ if (flags & SMEM_CACHED)
ionflags = ION_SET_CACHED(ionflags);
else
ionflags = ION_SET_UNCACHED(ionflags);
+ if (flags & SMEM_SECURE) {
+ ionflags |= ION_SECURE;
+ size = (size + 0xfffff) & (~0xfffff);
+ }
+
heap_mask = ION_HEAP(ION_CP_MM_HEAP_ID);
if (align < 4096)
align = 4096;
@@ -124,6 +144,7 @@
mem->smem_priv = hndl;
mem->domain = domain;
mem->partition_num = partition;
+ mem->flags = flags;
if (map_kernel) {
mem->kvaddr = ion_map_kernel(client->clnt, hndl);
if (!mem->kvaddr) {
@@ -136,7 +157,7 @@
mem->kvaddr = NULL;
rc = get_device_address(client->clnt, hndl, mem->domain,
- mem->partition_num, align, &iova, &buffer_size);
+ mem->partition_num, align, &iova, &buffer_size, flags);
if (rc) {
dprintk(VIDC_ERR, "Failed to get device address: %d\n",
rc);
@@ -160,7 +181,8 @@
{
if (mem->device_addr)
put_device_address(client->clnt,
- mem->smem_priv, mem->domain, mem->partition_num);
+ mem->smem_priv, mem->domain,
+ mem->partition_num, mem->flags);
if (mem->kvaddr)
ion_unmap_kernel(client->clnt, mem->smem_priv);
if (mem->smem_priv)
@@ -182,7 +204,7 @@
}
struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset,
- int domain, int partition)
+ int domain, int partition, int flags)
{
struct smem_client *client = clt;
int rc = 0;
@@ -199,7 +221,7 @@
switch (client->mem_type) {
case SMEM_ION:
rc = ion_user_to_kernel(clt, fd, offset,
- domain, partition, mem);
+ domain, partition, mem, flags);
break;
default:
dprintk(VIDC_ERR, "Mem type not supported\n");
diff --git a/drivers/media/video/msm_vidc/msm_smem.h b/drivers/media/video/msm_vidc/msm_smem.h
index c109abd..8241fdd 100644
--- a/drivers/media/video/msm_vidc/msm_smem.h
+++ b/drivers/media/video/msm_vidc/msm_smem.h
@@ -20,9 +20,10 @@
SMEM_ION,
};
-enum smem_cache_prop {
- SMEM_CACHED,
- SMEM_UNCACHED,
+enum smem_prop {
+ SMEM_CACHED = 0x1,
+ SMEM_SECURE = 0x2,
+ SMEM_INPUT = 0x4,
};
struct msm_smem {
@@ -32,6 +33,7 @@
unsigned long device_addr;
int domain;
int partition_num;
+ int flags;
void *smem_priv;
};
@@ -41,6 +43,6 @@
void msm_smem_free(void *clt, struct msm_smem *mem);
void msm_smem_delete_client(void *clt);
struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset, int
- domain, int partition);
+ domain, int partition, int flags);
int msm_smem_clean_invalidate(void *clt, struct msm_smem *mem);
#endif
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index 912fad4..4f2373e 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -756,6 +756,8 @@
struct msm_v4l2_vid_inst *v4l2_inst;
int plane = 0;
int i, rc = 0;
+ int smem_flags = 0;
+ int domain;
vidc_inst = get_vidc_inst(file, fh);
v4l2_inst = get_v4l2_inst(file, fh);
if (!v4l2_inst->mem_client) {
@@ -776,6 +778,7 @@
goto exit;
}
for (i = 0; i < b->length; ++i) {
+ smem_flags = 0;
if (EXTRADATA_IDX(b->length) &&
(i == EXTRADATA_IDX(b->length)) &&
!b->m.planes[i].length) {
@@ -792,8 +795,22 @@
kfree(binfo);
goto exit;
}
+ if ((vidc_inst->mode == VIDC_SECURE)
+ && (!EXTRADATA_IDX(b->length)
+ || (i != EXTRADATA_IDX(b->length)))) {
+ smem_flags |= SMEM_SECURE;
+ domain =
+ vidc_inst->core->resources.io_map[CP_MAP].domain;
+ } else
+ domain =
+ vidc_inst->core->resources.io_map[NS_MAP].domain;
+
+ if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ smem_flags |= SMEM_INPUT;
+
temp = get_same_fd_buffer(&v4l2_inst->registered_bufs,
b->m.planes[i].reserved[0], &plane);
+
if (temp) {
binfo->type = b->type;
binfo->fd[i] = b->m.planes[i].reserved[0];
@@ -807,8 +824,7 @@
handle = msm_smem_user_to_kernel(v4l2_inst->mem_client,
b->m.planes[i].reserved[0],
b->m.planes[i].reserved[1],
- vidc_inst->core->resources.io_map[NS_MAP].domain,
- 0);
+ domain, 0, smem_flags);
if (!handle) {
dprintk(VIDC_ERR,
"Failed to get device buffer address\n");
@@ -1119,9 +1135,11 @@
- SHARED_QSIZE;
partition[1].size = SHARED_QSIZE;
layout.npartitions = 2;
+ layout.is_secure = 0;
} else {
partition[0].size = io_map[i].addr_range[1];
layout.npartitions = 1;
+ layout.is_secure = 1;
}
layout.partitions = &partition[0];
layout.client_name = io_map[i].name;
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index 22063d4..c4bfaf4 100644
--- a/drivers/media/video/msm_vidc/msm_vdec.c
+++ b/drivers/media/video/msm_vidc/msm_vdec.c
@@ -156,7 +156,15 @@
.minimum = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE,
.maximum = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_ENABLE,
.default_value = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE,
- .step = 1,
+ },
+ {
+ .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,
},
@@ -518,6 +526,9 @@
fmt->get_frame_size(i,
f->fmt.pix_mp.height,
f->fmt.pix_mp.width);
+ inst->bufq[OUTPUT_PORT].
+ vb2_bufq.plane_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
}
} else {
f->fmt.pix_mp.plane_fmt[0].sizeimage =
@@ -527,6 +538,11 @@
f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage =
inst->buff_req.buffer[HAL_BUFFER_EXTRADATA_OUTPUT].buffer_size;
}
+ for (i = 0; i < fmt->num_planes; ++i)
+ inst->bufq[CAPTURE_PORT].
+ vb2_bufq.plane_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
+
}
} else {
dprintk(VIDC_ERR,
@@ -624,6 +640,10 @@
}
}
f->fmt.pix_mp.num_planes = fmt->num_planes;
+ for (i = 0; i < fmt->num_planes; ++i) {
+ inst->bufq[CAPTURE_PORT].vb2_bufq.plane_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ }
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
inst->prop.width = f->fmt.pix_mp.width;
inst->prop.height = f->fmt.pix_mp.height;
@@ -652,6 +672,10 @@
fmt->get_frame_size(0, f->fmt.pix_mp.height,
f->fmt.pix_mp.width);
f->fmt.pix_mp.num_planes = fmt->num_planes;
+ for (i = 0; i < fmt->num_planes; ++i) {
+ inst->bufq[OUTPUT_PORT].vb2_bufq.plane_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ }
}
err_invalid_fmt:
return rc;
@@ -933,7 +957,8 @@
case V4L2_DEC_CMD_STOP:
rc = msm_comm_release_scratch_buffers(inst);
if (rc)
- pr_err("Failed to release scratch buffers: %d\n", rc);
+ dprintk(VIDC_ERR,
+ "Failed to release scratch buffers: %d\n", rc);
rc = msm_comm_release_persist_buffers(inst);
if (rc)
pr_err("Failed to release persist buffers: %d\n", rc);
@@ -1002,17 +1027,8 @@
void *pdata;
struct msm_vidc_inst *inst = container_of(ctrl->handler,
struct msm_vidc_inst, ctrl_handler);
- rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
-
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to move inst: %p to start done state\n", inst);
- goto failed_open_done;
- }
-
control.id = ctrl->id;
control.value = ctrl->val;
-
switch (control.id) {
case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT:
property_id =
@@ -1067,10 +1083,20 @@
hal_property.enable = control.value;
pdata = &hal_property;
break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_SECURE:
+ inst->mode = VIDC_SECURE;
+ dprintk(VIDC_DBG, "Setting secure mode to :%d\n", inst->mode);
+ break;
default:
break;
- }
+ }
if (property_id) {
+ rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to move inst: %p to start done state\n", inst);
+ goto failed_open_done;
+ }
dprintk(VIDC_DBG,
"Control: HAL property=%d,ctrl_id=%d,ctrl_value=%d\n",
property_id,
@@ -1082,9 +1108,7 @@
}
if (rc)
dprintk(VIDC_ERR, "Failed to set hal property for framesize\n");
-
failed_open_done:
-
return rc;
}
static int msm_vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
diff --git a/drivers/media/video/msm_vidc/msm_venc.c b/drivers/media/video/msm_vidc/msm_venc.c
index d53da9e..d01841d 100644
--- a/drivers/media/video/msm_vidc/msm_venc.c
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -23,7 +23,6 @@
#define DEFAULT_WIDTH 1280
#define MIN_NUM_OUTPUT_BUFFERS 4
#define MAX_NUM_OUTPUT_BUFFERS 8
-#define MAX_INPUT_BUFFERS 32
#define MIN_BIT_RATE 64000
#define MAX_BIT_RATE 160000000
#define DEFAULT_BIT_RATE 64000
@@ -594,11 +593,11 @@
spin_lock_irqsave(&inst->lock, flags);
*num_buffers = inst->buff_req.buffer[0].buffer_count_actual =
max(*num_buffers, inst->buff_req.buffer[0].
- buffer_count_actual);
+ buffer_count_actual);
spin_unlock_irqrestore(&inst->lock, flags);
property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL;
new_buf_count.buffer_type = HAL_BUFFER_INPUT;
- new_buf_count.buffer_count_actual = MAX_INPUT_BUFFERS;
+ new_buf_count.buffer_count_actual = *num_buffers;
rc = vidc_hal_session_set_property(inst->session,
property_id, &new_buf_count);
dprintk(VIDC_DBG, "size = %d, alignment = %d, count = %d\n",
diff --git a/drivers/media/video/msm_vidc/msm_vidc.c b/drivers/media/video/msm_vidc/msm_vidc.c
index 1d0124f..64897c7 100644
--- a/drivers/media/video/msm_vidc/msm_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_vidc.c
@@ -413,7 +413,7 @@
inst = kzalloc(sizeof(*inst), GFP_KERNEL);
if (!inst) {
- pr_err("Failed to allocate memory\n") ;
+ dprintk(VIDC_ERR, "Failed to allocate memory\n");
rc = -ENOMEM;
goto err_invalid_core;
}
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index 9c81d40..dff3272 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -18,6 +18,7 @@
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
#include <mach/subsystem_restart.h>
+#include <mach/scm.h>
#include "msm_vidc_common.h"
#include "vidc_hal_api.h"
@@ -50,6 +51,18 @@
__mbs;\
})
+#define TZBSP_MEM_PROTECT_VIDEO_VAR 0x8
+struct tzbsp_memprot {
+ u32 cp_start;
+ u32 cp_size;
+ u32 cp_nonpixel_start;
+ u32 cp_nonpixel_size;
+};
+
+struct tzbsp_resp {
+ int ret;
+};
+
static const u32 bus_table[] = {
0,
36000,
@@ -140,6 +153,31 @@
return rc;
}
+static int protect_cp_mem(struct msm_vidc_core *core)
+{
+ struct tzbsp_memprot memprot;
+ unsigned int resp = 0;
+ int rc = 0;
+ struct msm_vidc_iommu_info *io_map = core->resources.io_map;
+ if (!io_map) {
+ dprintk(VIDC_ERR, "invalid params: %p\n", io_map);
+ return -EINVAL;
+ }
+ memprot.cp_start = 0x0;
+ memprot.cp_size = io_map[CP_MAP].addr_range[0] +
+ io_map[CP_MAP].addr_range[1];
+ memprot.cp_nonpixel_start = 0;
+ memprot.cp_nonpixel_size = 0;
+
+ rc = scm_call(SCM_SVC_CP, TZBSP_MEM_PROTECT_VIDEO_VAR, &memprot,
+ sizeof(memprot), &resp, sizeof(resp));
+ if (rc)
+ dprintk(VIDC_ERR,
+ "Failed to protect memory , rc is :%d, response : %d\n",
+ rc, resp);
+ return rc;
+}
+
struct msm_vidc_core *get_vidc_core(int core_id)
{
struct msm_vidc_core *core;
@@ -924,13 +962,16 @@
rc = -ENOMEM;
goto fail_subsystem_get;
}
-
rc = msm_comm_enable_clks(core);
if (rc) {
dprintk(VIDC_ERR, "Failed to enable clocks: %d\n", rc);
goto fail_enable_clks;
}
-
+ rc = protect_cp_mem(core);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to protect memory\n");
+ goto fail_iommu_attach;
+ }
rc = msm_comm_iommu_attach(core);
if (rc) {
dprintk(VIDC_ERR, "Failed to attach iommu");
@@ -953,10 +994,10 @@
return;
}
if (core->resources.fw.cookie) {
- subsystem_put(core->resources.fw.cookie);
- core->resources.fw.cookie = NULL;
msm_comm_iommu_detach(core);
msm_comm_disable_clks(core);
+ subsystem_put(core->resources.fw.cookie);
+ core->resources.fw.cookie = NULL;
}
}
@@ -1841,6 +1882,8 @@
struct internal_buf *binfo;
struct vidc_buffer_addr_info buffer_info;
unsigned long flags;
+ int domain;
+ unsigned long smem_flags = 0;
struct hal_buffer_requirements *scratch_buf =
&inst->buff_req.buffer[HAL_BUFFER_INTERNAL_SCRATCH];
int i;
@@ -1850,14 +1893,18 @@
scratch_buf->buffer_size);
if (msm_comm_release_scratch_buffers(inst))
dprintk(VIDC_WARN, "Failed to release scratch buffers\n");
+ if (inst->mode == VIDC_SECURE) {
+ domain = inst->core->resources.io_map[CP_MAP].domain;
+ smem_flags |= SMEM_SECURE;
+ } else
+ domain = inst->core->resources.io_map[NS_MAP].domain;
if (scratch_buf->buffer_size) {
for (i = 0; i < scratch_buf->buffer_count_actual;
i++) {
handle = msm_smem_alloc(inst->mem_client,
- scratch_buf->buffer_size, 1, SMEM_UNCACHED,
- inst->core->resources.io_map[NS_MAP].domain,
- 0, 0);
+ scratch_buf->buffer_size, 1, smem_flags,
+ domain, 0, 0);
if (!handle) {
dprintk(VIDC_ERR,
"Failed to allocate scratch memory\n");
@@ -1903,6 +1950,8 @@
struct internal_buf *binfo;
struct vidc_buffer_addr_info buffer_info;
unsigned long flags;
+ unsigned long smem_flags = 0;
+ int domain;
struct hal_buffer_requirements *persist_buf =
&inst->buff_req.buffer[HAL_BUFFER_INTERNAL_PERSIST];
int i;
@@ -1916,12 +1965,17 @@
return rc;
}
+ if (inst->mode == VIDC_SECURE) {
+ domain = inst->core->resources.io_map[CP_MAP].domain;
+ flags |= SMEM_SECURE;
+ } else
+ domain = inst->core->resources.io_map[NS_MAP].domain;
+
if (persist_buf->buffer_size) {
for (i = 0; i < persist_buf->buffer_count_actual; i++) {
handle = msm_smem_alloc(inst->mem_client,
- persist_buf->buffer_size, 1, SMEM_UNCACHED,
- inst->core->resources.io_map[NS_MAP].domain,
- 0, 0);
+ persist_buf->buffer_size, 1, smem_flags,
+ domain, 0, 0);
if (!handle) {
dprintk(VIDC_ERR,
"Failed to allocate persist memory\n");
diff --git a/drivers/media/video/msm_vidc/msm_vidc_internal.h b/drivers/media/video/msm_vidc/msm_vidc_internal.h
index 8e1a99e..5b2cced 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_internal.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_internal.h
@@ -210,6 +210,11 @@
bool ssr_in_progress;
};
+enum msm_vidc_mode {
+ VIDC_NON_SECURE,
+ VIDC_SECURE,
+};
+
struct msm_vidc_core {
struct list_head list;
struct mutex sync_lock;
@@ -260,6 +265,7 @@
void *priv;
struct msm_vidc_debug debug;
struct buf_count count;
+ enum msm_vidc_mode mode;
};
extern struct msm_vidc_drv *vidc_driver;
diff --git a/drivers/media/video/msm_vidc/vidc_hal.c b/drivers/media/video/msm_vidc/vidc_hal.c
index 2a3752f..f44be4d 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.c
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -541,7 +541,7 @@
mem_addr = &dev->mem_addr;
rc = vidc_hal_alloc((void *) mem_addr,
dev->hal_client, uc_size, 1,
- SMEM_UNCACHED, domain);
+ 0, domain);
if (rc) {
dprintk(VIDC_ERR, "iface_q_table_alloc_fail");
return -ENOMEM;
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index c9c4e70..46015b0 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -35,6 +35,7 @@
#include <linux/elf.h>
#include <linux/firmware.h>
#include <linux/freezer.h>
+#include <linux/scatterlist.h>
#include <mach/board.h>
#include <mach/msm_bus.h>
#include <mach/msm_bus_board.h>
@@ -54,6 +55,8 @@
#define QSEE_CE_CLK_100MHZ 100000000
#define QSEE_CE_CLK_50MHZ 50000000
+#define QSEECOM_MAX_SG_ENTRY 10
+
enum qseecom_command_scm_resp_type {
QSEOS_APP_ID = 0xEE01,
QSEOS_LISTENER_ID
@@ -247,6 +250,11 @@
struct clk *ce_core_src_clk;
struct clk *ce_bus_clk;
+struct qseecom_sg_entry {
+ uint32_t phys_addr;
+ uint32_t len;
+};
+
/* Function proto types */
static int qsee_vote_for_clock(int32_t);
static void qsee_disable_clock_vote(int32_t);
@@ -1093,13 +1101,11 @@
{
struct ion_handle *ihandle;
char *field;
- uint32_t *update;
- ion_phys_addr_t pa;
int ret = 0;
int i = 0;
- uint32_t length;
for (i = 0; i < MAX_ION_FD; i++) {
+ struct sg_table *sg_ptr = NULL;
if (req->ifd_data[i].fd > 0) {
/* Get the handle of the shared fd */
ihandle = ion_import_dma_buf(qseecom.ion_clnt,
@@ -1110,20 +1116,51 @@
}
field = (char *) req->cmd_req_buf +
req->ifd_data[i].cmd_buf_offset;
- update = (uint32_t *) field;
/* Populate the cmd data structure with the phys_addr */
- ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &length);
- if (ret)
- return -ENOMEM;
-
- *update = (uint32_t)pa;
+ sg_ptr = ion_sg_table(qseecom.ion_clnt, ihandle);
+ if (sg_ptr == NULL) {
+ pr_err("IOn client could not retrieve sg table\n");
+ goto err;
+ }
+ if (sg_ptr->nents == 0) {
+ pr_err("Num of scattered entries is 0\n");
+ goto err;
+ }
+ if (sg_ptr->nents > QSEECOM_MAX_SG_ENTRY) {
+ pr_err("Num of scattered entries");
+ pr_err(" (%d) is greater than max supported %d\n",
+ sg_ptr->nents, QSEECOM_MAX_SG_ENTRY);
+ goto err;
+ }
+ if (sg_ptr->nents == 1) {
+ uint32_t *update;
+ update = (uint32_t *) field;
+ *update = (uint32_t)sg_dma_address(sg_ptr->sgl);
+ } else {
+ struct qseecom_sg_entry *update;
+ struct scatterlist *sg;
+ int j = 0;
+ update = (struct qseecom_sg_entry *) field;
+ sg = sg_ptr->sgl;
+ for (j = 0; j < sg_ptr->nents; j++) {
+ update->phys_addr = (uint32_t)
+ sg_dma_address(sg);
+ update->len = (uint32_t)sg->length;
+ update++;
+ sg = sg_next(sg);
+ }
+ }
/* Deallocate the handle */
if (!IS_ERR_OR_NULL(ihandle))
ion_free(qseecom.ion_clnt, ihandle);
}
}
return ret;
+err:
+ if (!IS_ERR_OR_NULL(ihandle))
+ ion_free(qseecom.ion_clnt, ihandle);
+ return -ENOMEM;
}
static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index b34b069..e7a3741 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -23,8 +23,9 @@
#include <linux/scatterlist.h>
#include <linux/io.h>
#include <linux/gpio.h>
-#include <linux/pm_runtime.h>
#include <linux/mmc/sdhci-pci-data.h>
+#include <linux/sfi.h>
+#include <linux/pm_runtime.h>
#include "sdhci.h"
@@ -1451,6 +1452,8 @@
int i;
struct sdhci_pci_chip *chip;
+ sdhci_pci_runtime_pm_forbid(&pdev->dev);
+
chip = pci_get_drvdata(pdev);
if (chip) {
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index 55a164f..425d092 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -672,6 +672,7 @@
}
#ifdef CONFIG_PM_SLEEP
+
static int sdhci_s3c_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@@ -712,6 +713,13 @@
#define SDHCI_S3C_PMOPS (&sdhci_s3c_pmops)
+static const struct dev_pm_ops sdhci_s3c_pmops = {
+ .suspend = sdhci_s3c_suspend,
+ .resume = sdhci_s3c_resume,
+};
+
+#define SDHCI_S3C_PMOPS (&sdhci_s3c_pmops)
+
#else
#define SDHCI_S3C_PMOPS NULL
#endif
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 6df3410..6451d62 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -21,6 +21,7 @@
#include <linux/slab.h>
#include <linux/scatterlist.h>
#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
#include <linux/leds.h>
@@ -42,14 +43,29 @@
#define MAX_TUNING_LOOP 40
static unsigned int debug_quirks = 0;
+static unsigned int debug_quirks2;
static void sdhci_finish_data(struct sdhci_host *);
static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
static void sdhci_finish_command(struct sdhci_host *);
-static int sdhci_execute_tuning(struct mmc_host *mmc);
+static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
static void sdhci_tuning_timer(unsigned long data);
+#ifdef CONFIG_PM_RUNTIME
+static int sdhci_runtime_pm_get(struct sdhci_host *host);
+static int sdhci_runtime_pm_put(struct sdhci_host *host);
+#else
+static inline int sdhci_runtime_pm_get(struct sdhci_host *host)
+{
+ return 0;
+}
+static inline int sdhci_runtime_pm_put(struct sdhci_host *host)
+{
+ return 0;
+}
+#endif
+
static void sdhci_dumpregs(struct sdhci_host *host)
{
printk(KERN_DEBUG DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
@@ -134,6 +150,9 @@
(host->mmc->caps & MMC_CAP_NONREMOVABLE))
return;
+ if (host->quirks2 & SDHCI_QUIRK2_OWN_CARD_DETECTION)
+ return;
+
if (enable)
sdhci_unmask_irqs(host, irqs);
else
@@ -249,11 +268,14 @@
spin_lock_irqsave(&host->lock, flags);
+ if (host->runtime_suspended)
+ goto out;
+
if (brightness == LED_OFF)
sdhci_deactivate_led(host);
else
sdhci_activate_led(host);
-
+out:
spin_unlock_irqrestore(&host->lock, flags);
}
#endif
@@ -653,9 +675,7 @@
break;
}
- if (count >= 0xF) {
- printk(KERN_WARNING "%s: Too large timeout requested for CMD%d!\n",
- mmc_hostname(host->mmc), cmd->opcode);
+ if (count >= 0xF)
count = 0xE;
return count;
@@ -992,7 +1012,8 @@
flags |= SDHCI_CMD_INDEX;
/* CMD19 is special in that the Data Present Select should be set */
- if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
+ if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK ||
+ cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
flags |= SDHCI_CMD_DATA;
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
@@ -1208,6 +1229,8 @@
host = mmc_priv(mmc);
+ sdhci_runtime_pm_get(host);
+
spin_lock_irqsave(&host->lock, flags);
WARN_ON(host->mrq != NULL);
@@ -1251,7 +1274,7 @@
if ((host->flags & SDHCI_NEEDS_RETUNING) &&
!(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
spin_unlock_irqrestore(&host->lock, flags);
- sdhci_execute_tuning(mmc);
+ sdhci_execute_tuning(mmc, mrq->cmd->opcode);
spin_lock_irqsave(&host->lock, flags);
/* Restore original mmc_request structure */
@@ -1268,14 +1291,11 @@
spin_unlock_irqrestore(&host->lock, flags);
}
-static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
{
- struct sdhci_host *host;
unsigned long flags;
u8 ctrl;
- host = mmc_priv(mmc);
-
spin_lock_irqsave(&host->lock, flags);
if (host->flags & SDHCI_DEVICE_DEAD)
@@ -1338,7 +1358,8 @@
unsigned int clock;
/* In case of UHS-I modes, set High Speed Enable */
- if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
+ if ((ios->timing == MMC_TIMING_MMC_HS200) ||
+ (ios->timing == MMC_TIMING_UHS_SDR50) ||
(ios->timing == MMC_TIMING_UHS_SDR104) ||
(ios->timing == MMC_TIMING_UHS_DDR50) ||
(ios->timing == MMC_TIMING_UHS_SDR25))
@@ -1391,7 +1412,9 @@
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/* Select Bus Speed Mode for host */
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
- if (ios->timing == MMC_TIMING_UHS_SDR12)
+ if (ios->timing == MMC_TIMING_MMC_HS200)
+ ctrl_2 |= SDHCI_CTRL_HS_SDR200;
+ else if (ios->timing == MMC_TIMING_UHS_SDR12)
ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
else if (ios->timing == MMC_TIMING_UHS_SDR25)
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
@@ -1424,7 +1447,16 @@
spin_unlock_irqrestore(&host->lock, flags);
}
-static int check_ro(struct sdhci_host *host)
+static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ sdhci_runtime_pm_get(host);
+ sdhci_do_set_ios(host, ios);
+ sdhci_runtime_pm_put(host);
+}
+
+static int sdhci_check_ro(struct sdhci_host *host)
{
unsigned long flags;
int is_readonly;
@@ -1448,19 +1480,16 @@
#define SAMPLE_COUNT 5
-static int sdhci_get_ro(struct mmc_host *mmc)
+static int sdhci_do_get_ro(struct sdhci_host *host)
{
- struct sdhci_host *host;
int i, ro_count;
- host = mmc_priv(mmc);
-
if (!(host->quirks & SDHCI_QUIRK_UNSTABLE_RO_DETECT))
- return check_ro(host);
+ return sdhci_check_ro(host);
ro_count = 0;
for (i = 0; i < SAMPLE_COUNT; i++) {
- if (check_ro(host)) {
+ if (sdhci_check_ro(host)) {
if (++ro_count > SAMPLE_COUNT / 2)
return 1;
}
@@ -1469,38 +1498,64 @@
return 0;
}
-static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+static void sdhci_hw_reset(struct mmc_host *mmc)
{
- struct sdhci_host *host;
- unsigned long flags;
+ struct sdhci_host *host = mmc_priv(mmc);
- host = mmc_priv(mmc);
+ if (host->ops && host->ops->hw_reset)
+ host->ops->hw_reset(host);
+}
- spin_lock_irqsave(&host->lock, flags);
+static int sdhci_get_ro(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ int ret;
+ sdhci_runtime_pm_get(host);
+ ret = sdhci_do_get_ro(host);
+ sdhci_runtime_pm_put(host);
+ return ret;
+}
+
+static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)
+{
if (host->flags & SDHCI_DEVICE_DEAD)
goto out;
if (enable)
+ host->flags |= SDHCI_SDIO_IRQ_ENABLED;
+ else
+ host->flags &= ~SDHCI_SDIO_IRQ_ENABLED;
+
+ /* SDIO IRQ will be enabled as appropriate in runtime resume */
+ if (host->runtime_suspended)
+ goto out;
+
+ if (enable)
sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);
else
sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);
out:
mmiowb();
+}
+static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_enable_sdio_irq_nolock(host, enable);
spin_unlock_irqrestore(&host->lock, flags);
}
-static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
- struct mmc_ios *ios)
+static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
+ struct mmc_ios *ios)
{
- struct sdhci_host *host;
u8 pwr;
u16 clk, ctrl;
u32 present_state;
- host = mmc_priv(mmc);
-
/*
* Signal Voltage Switching is only applicable for Host Controllers
* v3.00 and above.
@@ -1593,7 +1648,21 @@
return 0;
}
-static int sdhci_execute_tuning(struct mmc_host *mmc)
+static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
+ struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ int err;
+
+ if (host->version < SDHCI_SPEC_300)
+ return 0;
+ sdhci_runtime_pm_get(host);
+ err = sdhci_do_start_signal_voltage_switch(host, ios);
+ sdhci_runtime_pm_put(host);
+ return err;
+}
+
+static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host;
u16 ctrl;
@@ -1601,26 +1670,35 @@
int tuning_loop_counter = MAX_TUNING_LOOP;
unsigned long timeout;
int err = 0;
+ bool requires_tuning_nonuhs = false;
host = mmc_priv(mmc);
+ sdhci_runtime_pm_get(host);
disable_irq(host->irq);
spin_lock(&host->lock);
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/*
- * Host Controller needs tuning only in case of SDR104 mode
- * and for SDR50 mode when Use Tuning for SDR50 is set in
+ * The Host Controller needs tuning only in case of SDR104 mode
+ * and for SDR50 mode when Use Tuning for SDR50 is set in the
* Capabilities register.
+ * If the Host Controller supports the HS200 mode then the
+ * tuning function has to be executed.
*/
+ if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
+ (host->flags & SDHCI_SDR50_NEEDS_TUNING ||
+ host->flags & SDHCI_HS200_NEEDS_TUNING))
+ requires_tuning_nonuhs = true;
+
if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
- (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
- (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
+ requires_tuning_nonuhs)
ctrl |= SDHCI_CTRL_EXEC_TUNING;
else {
spin_unlock(&host->lock);
enable_irq(host->irq);
+ sdhci_runtime_pm_put(host);
return 0;
}
@@ -1646,12 +1724,12 @@
timeout = 150;
do {
struct mmc_command cmd = {0};
- struct mmc_request mrq = {0};
+ struct mmc_request mrq = {NULL};
if (!tuning_loop_counter && !timeout)
break;
- cmd.opcode = MMC_SEND_TUNING_BLOCK;
+ cmd.opcode = opcode;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
cmd.retries = 0;
@@ -1666,7 +1744,17 @@
* block to the Host Controller. So we set the block size
* to 64 here.
*/
- sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
+ if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200) {
+ if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128),
+ SDHCI_BLOCK_SIZE);
+ else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4)
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
+ SDHCI_BLOCK_SIZE);
+ } else {
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
+ SDHCI_BLOCK_SIZE);
+ }
/*
* The tuning block is sent by the card to the host controller.
@@ -1764,18 +1852,16 @@
sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
spin_unlock(&host->lock);
enable_irq(host->irq);
+ sdhci_runtime_pm_put(host);
return err;
}
-static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
+static void sdhci_do_enable_preset_value(struct sdhci_host *host, bool enable)
{
- struct sdhci_host *host;
u16 ctrl;
unsigned long flags;
- host = mmc_priv(mmc);
-
/* Host Controller v3.00 defines preset value registers */
if (host->version < SDHCI_SPEC_300)
return;
@@ -1791,18 +1877,30 @@
if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ host->flags |= SDHCI_PV_ENABLED;
} else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ host->flags &= ~SDHCI_PV_ENABLED;
}
spin_unlock_irqrestore(&host->lock, flags);
}
+static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ sdhci_runtime_pm_get(host);
+ sdhci_do_enable_preset_value(host, enable);
+ sdhci_runtime_pm_put(host);
+}
+
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.set_ios = sdhci_set_ios,
.get_ro = sdhci_get_ro,
+ .hw_reset = sdhci_hw_reset,
.enable_sdio_irq = sdhci_enable_sdio_irq,
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
.execute_tuning = sdhci_execute_tuning,
@@ -1824,19 +1922,19 @@
spin_lock_irqsave(&host->lock, flags);
- if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
- if (host->mrq) {
- printk(KERN_ERR "%s: Card removed during transfer!\n",
- mmc_hostname(host->mmc));
- printk(KERN_ERR "%s: Resetting controller.\n",
- mmc_hostname(host->mmc));
+ /* Check host->mrq first in case we are runtime suspended */
+ if (host->mrq &&
+ !(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+ printk(KERN_ERR "%s: Card removed during transfer!\n",
+ mmc_hostname(host->mmc));
+ printk(KERN_ERR "%s: Resetting controller.\n",
+ mmc_hostname(host->mmc));
- sdhci_reset(host, SDHCI_RESET_CMD);
- sdhci_reset(host, SDHCI_RESET_DATA);
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
- host->mrq->cmd->error = -ENOMEDIUM;
- tasklet_schedule(&host->finish_tasklet);
- }
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
}
spin_unlock_irqrestore(&host->lock, flags);
@@ -1852,14 +1950,16 @@
host = (struct sdhci_host*)param;
+ spin_lock_irqsave(&host->lock, flags);
+
/*
* If this tasklet gets rescheduled while running, it will
* be run again afterwards but without any active request.
*/
- if (!host->mrq)
+ if (!host->mrq) {
+ spin_unlock_irqrestore(&host->lock, flags);
return;
-
- spin_lock_irqsave(&host->lock, flags);
+ }
del_timer(&host->timer);
@@ -1903,6 +2003,7 @@
spin_unlock_irqrestore(&host->lock, flags);
mmc_request_done(host->mmc, mrq);
+ sdhci_runtime_pm_put(host);
}
static void sdhci_timeout_timer(unsigned long data)
@@ -2036,12 +2137,14 @@
static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
{
+ u32 command;
BUG_ON(intmask == 0);
/* CMD19 generates _only_ Buffer Read Ready interrupt */
if (intmask & SDHCI_INT_DATA_AVAIL) {
- if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) ==
- MMC_SEND_TUNING_BLOCK) {
+ command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
+ if (command == MMC_SEND_TUNING_BLOCK ||
+ command == MMC_SEND_TUNING_BLOCK_HS200) {
host->tuning_done = 1;
wake_up(&host->buf_ready_int);
return;
@@ -2140,6 +2243,13 @@
spin_lock(&host->lock);
+ if (host->runtime_suspended) {
+ spin_unlock(&host->lock);
+ printk(KERN_WARNING "%s: got irq while runtime suspended\n",
+ mmc_hostname(host->mmc));
+ return IRQ_HANDLED;
+ }
+
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
if (!intmask || intmask == 0xffffffff) {
@@ -2226,7 +2336,7 @@
#ifdef CONFIG_PM
-int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
+int sdhci_suspend_host(struct sdhci_host *host)
{
int ret;
@@ -2266,7 +2376,6 @@
return ret;
}
-
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma)
host->ops->enable_dma(host);
@@ -2317,6 +2426,90 @@
#endif /* CONFIG_PM */
+#ifdef CONFIG_PM_RUNTIME
+
+static int sdhci_runtime_pm_get(struct sdhci_host *host)
+{
+ return pm_runtime_get_sync(host->mmc->parent);
+}
+
+static int sdhci_runtime_pm_put(struct sdhci_host *host)
+{
+ pm_runtime_mark_last_busy(host->mmc->parent);
+ return pm_runtime_put_autosuspend(host->mmc->parent);
+}
+
+int sdhci_runtime_suspend_host(struct sdhci_host *host)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ /* Disable tuning since we are suspending */
+ if (host->version >= SDHCI_SPEC_300 &&
+ host->tuning_mode == SDHCI_TUNING_MODE_1) {
+ del_timer_sync(&host->tuning_timer);
+ host->flags &= ~SDHCI_NEEDS_RETUNING;
+ }
+
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ synchronize_irq(host->irq);
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->runtime_suspended = true;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
+
+int sdhci_runtime_resume_host(struct sdhci_host *host)
+{
+ unsigned long flags;
+ int ret = 0, host_flags = host->flags;
+
+ if (host_flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
+ if (host->ops->enable_dma)
+ host->ops->enable_dma(host);
+ }
+
+ sdhci_init(host, 0);
+
+ /* Force clock and power re-program */
+ host->pwr = 0;
+ host->clock = 0;
+ sdhci_do_set_ios(host, &host->mmc->ios);
+
+ sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios);
+ if (host_flags & SDHCI_PV_ENABLED)
+ sdhci_do_enable_preset_value(host, true);
+
+ /* Set the re-tuning expiration flag */
+ if ((host->version >= SDHCI_SPEC_300) && host->tuning_count &&
+ (host->tuning_mode == SDHCI_TUNING_MODE_1))
+ host->flags |= SDHCI_NEEDS_RETUNING;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ host->runtime_suspended = false;
+
+ /* Enable SDIO IRQ */
+ if ((host->flags & SDHCI_SDIO_IRQ_ENABLED))
+ sdhci_enable_sdio_irq_nolock(host, true);
+
+ /* Enable Card Detection */
+ sdhci_enable_card_detection(host);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host);
+
+#endif
+
/*****************************************************************************\
* *
* Device allocation/registration *
@@ -2359,6 +2552,8 @@
if (debug_quirks)
host->quirks = debug_quirks;
+ if (debug_quirks2)
+ host->quirks2 = debug_quirks2;
sdhci_reset(host, SDHCI_RESET_ALL);
@@ -2569,10 +2764,14 @@
if (caps[1] & SDHCI_SUPPORT_DDR50)
mmc->caps |= MMC_CAP_UHS_DDR50;
- /* Does the host needs tuning for SDR50? */
+ /* Does the host need tuning for SDR50? */
if (caps[1] & SDHCI_USE_SDR50_TUNING)
host->flags |= SDHCI_SDR50_NEEDS_TUNING;
+ /* Does the host need tuning for HS200? */
+ if (mmc->caps2 & MMC_CAP2_HS200)
+ host->flags |= SDHCI_HS200_NEEDS_TUNING;
+
/* Driver Type(s) (A, C, D) supported by the host */
if (caps[1] & SDHCI_DRIVER_TYPE_A)
mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
@@ -2893,9 +3092,11 @@
module_exit(sdhci_drv_exit);
module_param(debug_quirks, uint, 0444);
+module_param(debug_quirks2, uint, 0444);
MODULE_AUTHOR("Pierre Ossman <pierre@ossman.eu>");
MODULE_DESCRIPTION("Secure Digital Host Controller Interface core driver");
MODULE_LICENSE("GPL");
MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
+MODULE_PARM_DESC(debug_quirks2, "Force certain other quirks.");
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 0c070d6..1955ff4 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -21,6 +21,8 @@
#include <linux/of_device.h>
#include <linux/power_supply.h>
#include <linux/spmi.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/mfd/pm8xxx/batterydata-lib.h>
@@ -38,6 +40,8 @@
#define BMS1_OCV_USE_HIGH_LIMIT_THR0 0x4A
#define BMS1_OCV_USE_HIGH_LIMIT_THR1 0x4B
#define BMS1_OCV_USE_LIMIT_CTL 0x4C
+/* Delay control */
+#define BMS1_S1_DELAY_CTL 0x5A
/* CC interrupt threshold */
#define BMS1_CC_THR0 0x7A
#define BMS1_CC_THR1 0x7B
@@ -76,6 +80,8 @@
#define IAVG_START 600
#define SOC_ZERO 0xFF
+#define IAVG_SAMPLES 16
+
#define QPNP_BMS_DEV_NAME "qcom,qpnp-bms"
struct soc_params {
@@ -96,6 +102,7 @@
struct qpnp_bms_chip {
struct device *dev;
struct power_supply bms_psy;
+ struct power_supply *batt_psy;
struct spmi_device *spmi;
u16 base;
@@ -125,6 +132,7 @@
struct mutex bms_output_lock;
struct mutex last_ocv_uv_mutex;
+ struct mutex soc_invalidation_mutex;
unsigned int start_percent;
unsigned int end_percent;
@@ -137,6 +145,27 @@
int low_soc_calculate_soc_ms;
int calculate_soc_ms;
+ uint16_t ocv_reading_at_100;
+ int64_t cc_reading_at_100;
+ uint16_t prev_last_good_ocv_raw;
+ int last_ocv_uv;
+ int last_cc_uah;
+ unsigned long tm_sec;
+ bool first_time_calc_soc;
+ bool first_time_calc_uuc;
+ int pon_ocv_uv;
+
+ int iavg_samples_ma[IAVG_SAMPLES];
+ int iavg_index;
+ int iavg_num_samples;
+ struct timespec t_soc_queried;
+ int last_soc;
+ int last_soc_est;
+
+ int charge_time_us;
+ int catch_up_time_us;
+ struct single_row_lut *adjusted_fcc_temp_lut;
+
unsigned int vadc_v0625;
unsigned int vadc_v1250;
@@ -307,8 +336,7 @@
return VBATT_MUL_FACTOR * reading_uv;
}
- numerator = ((s64)reading_uv - chip->vadc_v0625)
- * VADC_CALIB_UV;
+ numerator = ((s64)reading_uv - chip->vadc_v0625) * VADC_CALIB_UV;
denominator = (s64)chip->vadc_v1250 - chip->vadc_v0625;
if (denominator == 0)
return reading_uv * VBATT_MUL_FACTOR;
@@ -319,7 +347,13 @@
static inline int convert_vbatt_raw_to_uv(struct qpnp_bms_chip *chip,
uint16_t reading)
{
- return adjust_vbatt_reading(chip, vadc_reading_to_uv(reading));
+ int uv;
+
+ uv = vadc_reading_to_uv(reading);
+ pr_debug("%u raw converted into %d uv\n", reading, uv);
+ uv = adjust_vbatt_reading(chip, uv);
+ pr_debug("adjusted into %d uv\n", uv);
+ return uv;
}
#define CC_READING_RESOLUTION_N 542535
@@ -331,16 +365,16 @@
}
#define QPNP_ADC_GAIN_NV 17857LL
-static s64 cc_adjust_for_gain(s64 uv, s64 gain)
+static s64 cc_adjust_for_gain(s64 uv, uint16_t gain)
{
s64 result_uv;
pr_debug("adjusting_uv = %lld\n", uv);
- pr_debug("adjusting by factor: %lld/%lld = %lld%%\n",
+ pr_debug("adjusting by factor: %lld/%hu = %lld%%\n",
QPNP_ADC_GAIN_NV, gain,
- div_s64(QPNP_ADC_GAIN_NV * 100LL, gain));
+ div_s64(QPNP_ADC_GAIN_NV * 100LL, (s64)gain));
- result_uv = div_s64(uv * QPNP_ADC_GAIN_NV, gain);
+ result_uv = div_s64(uv * QPNP_ADC_GAIN_NV, (s64)gain);
pr_debug("result_uv = %lld\n", result_uv);
return result_uv;
}
@@ -348,7 +382,11 @@
static int convert_vsense_to_uv(struct qpnp_bms_chip *chip,
int16_t reading)
{
- return cc_adjust_for_gain(cc_reading_to_uv(reading), QPNP_ADC_GAIN_NV);
+ struct qpnp_iadc_calib calibration;
+
+ qpnp_iadc_get_gain_and_offset(&calibration);
+ return cc_adjust_for_gain(cc_reading_to_uv(reading),
+ calibration.gain_raw);
}
static int read_vsense_avg(struct qpnp_bms_chip *chip, int *result_uv)
@@ -407,18 +445,944 @@
return 0;
}
+#define CC_36_BIT_MASK 0xFFFFFFFFFLL
+
+static int read_cc_raw(struct qpnp_bms_chip *chip, int64_t *reading)
+{
+ int64_t raw_reading;
+ int rc;
+
+ rc = qpnp_read_wrapper(chip, (u8 *)&raw_reading,
+ chip->base + BMS1_CC_DATA0, 5);
+ if (rc) {
+ pr_err("Error reading cc: rc = %d\n", rc);
+ return -ENXIO;
+ }
+
+ raw_reading = raw_reading & CC_36_BIT_MASK;
+ /* convert 36 bit signed value into 64 signed value */
+ *reading = (raw_reading >> 35) == 0LL ?
+ raw_reading : ((-1LL ^ CC_36_BIT_MASK) | raw_reading);
+ pr_debug("before conversion: %llx, after conversion: %llx\n",
+ raw_reading, *reading);
+
+ return 0;
+}
+
+static int calib_vadc(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ struct qpnp_vadc_result result;
+
+ rc = qpnp_vadc_read(REF_625MV, &result);
+ if (rc) {
+ pr_debug("vadc read failed with rc = %d\n", rc);
+ return rc;
+ }
+ chip->vadc_v0625 = result.physical;
+
+ rc = qpnp_vadc_read(REF_125V, &result);
+ if (rc) {
+ pr_debug("vadc read failed with rc = %d\n", rc);
+ return rc;
+ }
+ chip->vadc_v1250 = result.physical;
+ pr_debug("vadc calib: 0625 = %d, 1250 = %d\n",
+ chip->vadc_v0625, chip->vadc_v1250);
+ return 0;
+}
+
+static void convert_and_store_ocv(struct qpnp_bms_chip *chip,
+ struct raw_soc_params *raw)
+{
+ int rc;
+
+ pr_debug("prev_last_good_ocv_raw = %d, last_good_ocv_raw = %d\n",
+ chip->prev_last_good_ocv_raw,
+ raw->last_good_ocv_raw);
+ rc = calib_vadc(chip);
+ if (rc)
+ pr_err("Vadc reference voltage read failed, rc = %d\n", rc);
+ chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
+ raw->last_good_ocv_uv = convert_vbatt_raw_to_uv(chip,
+ raw->last_good_ocv_raw);
+ chip->last_ocv_uv = raw->last_good_ocv_uv;
+ pr_debug("last_good_ocv_uv = %d\n", raw->last_good_ocv_uv);
+}
+
static int read_soc_params_raw(struct qpnp_bms_chip *chip,
struct raw_soc_params *raw)
{
- /* TODO add real reads */
+ int rc;
+
+ mutex_lock(&chip->bms_output_lock);
+ lock_output_data(chip);
+
+ rc = qpnp_read_wrapper(chip, (u8 *)&raw->last_good_ocv_raw,
+ chip->base + BMS1_OCV_FOR_SOC_DATA0, 2);
+ if (rc) {
+ pr_err("Error reading ocv: rc = %d\n", rc);
+ return -ENXIO;
+ }
+
+ rc = read_cc_raw(chip, &raw->cc);
+ if (rc) {
+ pr_err("Failed to read raw cc data, rc = %d\n", rc);
+ return rc;
+ }
+
+ unlock_output_data(chip);
+ mutex_unlock(&chip->bms_output_lock);
+
+ if (chip->prev_last_good_ocv_raw == 0) {
+ convert_and_store_ocv(chip, raw);
+ pr_debug("PON_OCV_UV = %d\n", chip->last_ocv_uv);
+ } else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
+ convert_and_store_ocv(chip, raw);
+ /* forget the old cc value upon ocv */
+ chip->last_cc_uah = 0;
+ } else {
+ raw->last_good_ocv_uv = chip->last_ocv_uv;
+ }
+
+ /* fake a high OCV if done charging */
+ if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) {
+ chip->ocv_reading_at_100 = 0;
+ chip->cc_reading_at_100 = 0;
+ } else {
+ /*
+ * force 100% ocv by selecting the highest voltage the
+ * battery could ever reach
+ */
+ raw->last_good_ocv_uv = chip->max_voltage_uv;
+ chip->last_ocv_uv = chip->max_voltage_uv;
+ }
+ pr_debug("last_good_ocv_raw= 0x%x, last_good_ocv_uv= %duV\n",
+ raw->last_good_ocv_raw, raw->last_good_ocv_uv);
+ pr_debug("cc_raw= 0x%llx\n", raw->cc);
return 0;
}
+static int calculate_pc(struct qpnp_bms_chip *chip, int ocv_uv,
+ int batt_temp)
+{
+ int pc;
+
+ pc = interpolate_pc(chip->pc_temp_ocv_lut,
+ batt_temp / 10, ocv_uv / 1000);
+ pr_debug("pc = %u %% for ocv = %d uv batt_temp = %d\n",
+ pc, ocv_uv, batt_temp);
+ /* Multiply the initial FCC value by the scale factor. */
+ return pc;
+}
+
+static int calculate_fcc(struct qpnp_bms_chip *chip, int batt_temp)
+{
+ int fcc_uah;
+
+ if (chip->adjusted_fcc_temp_lut == NULL) {
+ /* interpolate_fcc returns a mv value. */
+ fcc_uah = interpolate_fcc(chip->fcc_temp_lut,
+ batt_temp) * 1000;
+ pr_debug("fcc = %d uAh\n", fcc_uah);
+ return fcc_uah;
+ } else {
+ return 1000 * interpolate_fcc(chip->adjusted_fcc_temp_lut,
+ batt_temp);
+ }
+}
+
+/* calculate remaining charge at the time of ocv */
+static int calculate_ocv_charge(struct qpnp_bms_chip *chip,
+ struct raw_soc_params *raw,
+ int fcc_uah,
+ int batt_temp)
+{
+ int ocv_uv, pc;
+
+ ocv_uv = raw->last_good_ocv_uv;
+ pc = calculate_pc(chip, ocv_uv, batt_temp);
+ pr_debug("ocv_uv = %d pc = %d\n", ocv_uv, pc);
+ return (fcc_uah * pc) / 100;
+}
+
+#define CC_RESOLUTION_N 542535
+#define CC_RESOLUTION_D 100000
+
+static s64 cc_to_uv(s64 cc)
+{
+ return div_s64(cc * CC_RESOLUTION_N, CC_RESOLUTION_D);
+}
+
+#define CC_READING_TICKS 56
+#define SLEEP_CLK_HZ 32764
+#define SECONDS_PER_HOUR 3600
+
+static s64 cc_uv_to_nvh(s64 cc_uv)
+{
+ return div_s64(cc_uv * CC_READING_TICKS * 1000,
+ SLEEP_CLK_HZ * SECONDS_PER_HOUR);
+}
+
+/**
+ * calculate_cc-
+ * @chip: the bms chip pointer
+ * @cc: the cc reading from bms h/w
+ * @val: return value
+ * @coulomb_counter: adjusted coulomb counter for 100%
+ *
+ * RETURNS: in val pointer coulomb counter based charger in uAh
+ * (micro Amp hour)
+ */
+static int calculate_cc(struct qpnp_bms_chip *chip, int64_t cc)
+{
+ int64_t cc_voltage_uv, cc_nvh, cc_uah;
+ struct qpnp_iadc_calib calibration;
+
+ qpnp_iadc_get_gain_and_offset(&calibration);
+ cc_voltage_uv = cc;
+ cc_voltage_uv -= chip->cc_reading_at_100;
+ pr_debug("cc = %lld. after subtracting 0x%llx cc = %lld\n",
+ cc, chip->cc_reading_at_100,
+ cc_voltage_uv);
+ cc_voltage_uv = cc_to_uv(cc_voltage_uv);
+ cc_voltage_uv = cc_adjust_for_gain(cc_voltage_uv, calibration.gain_raw);
+ pr_debug("cc_voltage_uv = %lld uv\n", cc_voltage_uv);
+ cc_nvh = cc_uv_to_nvh(cc_voltage_uv);
+ pr_debug("cc_nvh = %lld nano_volt_hour\n", cc_nvh);
+ cc_uah = div_s64(cc_nvh, chip->r_sense_mohm);
+ /* cc_raw had 4 bits of extra precision.
+ By now it should be within 32 bit range */
+ return (int)cc_uah;
+}
+
+static int get_rbatt(struct qpnp_bms_chip *chip,
+ int soc_rbatt_mohm, int batt_temp)
+{
+ int rbatt_mohm, scalefactor;
+
+ rbatt_mohm = chip->default_rbatt_mohm;
+ pr_debug("rbatt before scaling = %d\n", rbatt_mohm);
+ if (chip->rbatt_sf_lut == NULL) {
+ pr_debug("RBATT = %d\n", rbatt_mohm);
+ return rbatt_mohm;
+ }
+ /* Convert the batt_temp to DegC from deciDegC */
+ batt_temp = batt_temp / 10;
+ scalefactor = interpolate_scalingfactor(chip->rbatt_sf_lut,
+ batt_temp, soc_rbatt_mohm);
+ pr_debug("rbatt sf = %d for batt_temp = %d, soc_rbatt = %d\n",
+ scalefactor, batt_temp, soc_rbatt_mohm);
+ rbatt_mohm = (rbatt_mohm * scalefactor) / 100;
+
+ rbatt_mohm += chip->r_conn_mohm;
+ pr_debug("adding r_conn_mohm = %d rbatt = %d\n",
+ chip->r_conn_mohm, rbatt_mohm);
+
+ pr_debug("RBATT = %d\n", rbatt_mohm);
+ return rbatt_mohm;
+}
+
+static void calculate_iavg(struct qpnp_bms_chip *chip, int cc_uah,
+ int *iavg_ua)
+{
+ int delta_cc_uah, delta_time_s, rc;
+ struct rtc_time tm;
+ struct rtc_device *rtc;
+ unsigned long now_tm_sec = 0;
+
+ rc = 0;
+ /* if anything fails report the previous iavg_ua */
+ *iavg_ua = chip->prev_iavg_ua;
+
+ rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+ if (rtc == NULL) {
+ pr_err("%s: unable to open rtc device (%s)\n",
+ __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
+ goto out;
+ }
+
+ rc = rtc_read_time(rtc, &tm);
+ if (rc) {
+ pr_err("Error reading rtc device (%s) : %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto out;
+ }
+
+ rc = rtc_valid_tm(&tm);
+ if (rc) {
+ pr_err("Invalid RTC time (%s): %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto out;
+ }
+ rtc_tm_to_time(&tm, &now_tm_sec);
+
+ if (chip->tm_sec == 0) {
+ get_battery_current(chip, iavg_ua);
+ goto out;
+ }
+
+ delta_time_s = (now_tm_sec - chip->tm_sec);
+
+ /* use the previous iavg if called within 15 seconds */
+ if (delta_time_s < 15) {
+ *iavg_ua = chip->prev_iavg_ua;
+ goto out;
+ }
+
+ delta_cc_uah = cc_uah - chip->last_cc_uah;
+
+ *iavg_ua = div_s64((s64)delta_cc_uah * 3600, delta_time_s);
+
+ pr_debug("tm_sec = %ld, now_tm_sec = %ld delta_s = %d delta_cc = %d iavg_ua = %d\n",
+ chip->tm_sec, now_tm_sec,
+ delta_time_s, delta_cc_uah, (int)*iavg_ua);
+
+out:
+ /* remember the iavg */
+ chip->prev_iavg_ua = *iavg_ua;
+
+ /* remember cc_uah */
+ chip->last_cc_uah = cc_uah;
+
+ /* remember this time */
+ chip->tm_sec = now_tm_sec;
+}
+
+static int calculate_termination_uuc(struct qpnp_bms_chip *chip,
+ struct soc_params *params,
+ int batt_temp, int uuc_iavg_ma,
+ int *ret_pc_unusable)
+{
+ int unusable_uv, pc_unusable, uuc_uah;
+ int i = 0;
+ int ocv_mv;
+ int batt_temp_degc = batt_temp / 10;
+ int rbatt_mohm;
+ int delta_uv;
+ int prev_delta_uv = 0;
+ int prev_rbatt_mohm = 0;
+ int uuc_rbatt_mohm;
+
+ for (i = 0; i <= 100; i++) {
+ ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut,
+ batt_temp_degc, i);
+ rbatt_mohm = get_rbatt(chip, i, batt_temp);
+ unusable_uv = (rbatt_mohm * uuc_iavg_ma)
+ + (chip->v_cutoff_uv);
+ delta_uv = ocv_mv * 1000 - unusable_uv;
+
+ pr_debug("soc = %d ocv = %d rbat = %d u_uv = %d delta_v = %d\n",
+ i, ocv_mv, rbatt_mohm, unusable_uv, delta_uv);
+
+ if (delta_uv > 0)
+ break;
+
+ prev_delta_uv = delta_uv;
+ prev_rbatt_mohm = rbatt_mohm;
+ }
+
+ uuc_rbatt_mohm = linear_interpolate(rbatt_mohm, delta_uv,
+ prev_rbatt_mohm, prev_delta_uv,
+ 0);
+
+ unusable_uv = (uuc_rbatt_mohm * uuc_iavg_ma) + (chip->v_cutoff_uv);
+
+ pc_unusable = calculate_pc(chip, unusable_uv, batt_temp);
+ uuc_uah = (params->fcc_uah * pc_unusable) / 100;
+ pr_debug("For uuc_iavg_ma = %d, unusable_rbatt = %d unusable_uv = %d unusable_pc = %d uuc = %d\n",
+ uuc_iavg_ma,
+ uuc_rbatt_mohm, unusable_uv,
+ pc_unusable, uuc_uah);
+ *ret_pc_unusable = pc_unusable;
+ return uuc_uah;
+}
+
+static int adjust_uuc(struct qpnp_bms_chip *chip,
+ struct soc_params *params,
+ int new_pc_unusable,
+ int new_uuc_uah,
+ int batt_temp)
+{
+ int new_unusable_mv, new_iavg_ma;
+ int batt_temp_degc = batt_temp / 10;
+
+ if (chip->prev_pc_unusable == -EINVAL
+ || abs(chip->prev_pc_unusable - new_pc_unusable) <= 1) {
+ chip->prev_pc_unusable = new_pc_unusable;
+ return new_uuc_uah;
+ }
+
+ /* the uuc is trying to change more than 1% restrict it */
+ if (new_pc_unusable > chip->prev_pc_unusable)
+ chip->prev_pc_unusable++;
+ else
+ chip->prev_pc_unusable--;
+
+ new_uuc_uah = (params->fcc_uah * chip->prev_pc_unusable) / 100;
+
+ /* also find update the iavg_ma accordingly */
+ new_unusable_mv = interpolate_ocv(chip->pc_temp_ocv_lut,
+ batt_temp_degc, chip->prev_pc_unusable);
+ if (new_unusable_mv < chip->v_cutoff_uv/1000)
+ new_unusable_mv = chip->v_cutoff_uv/1000;
+
+ new_iavg_ma = (new_unusable_mv * 1000 - chip->v_cutoff_uv)
+ / params->rbatt;
+ if (new_iavg_ma == 0)
+ new_iavg_ma = 1;
+ chip->prev_uuc_iavg_ma = new_iavg_ma;
+ pr_debug("Restricting UUC to %d (%d%%) unusable_mv = %d iavg_ma = %d\n",
+ new_uuc_uah, chip->prev_pc_unusable,
+ new_unusable_mv, new_iavg_ma);
+
+ return new_uuc_uah;
+}
+
+#define CHARGING_IAVG_MA 250
+#define MIN_SECONDS_FOR_VALID_SAMPLE 20
+static int calculate_unusable_charge_uah(struct qpnp_bms_chip *chip,
+ struct soc_params *params,
+ int batt_temp)
+{
+ int uuc_uah_iavg;
+ int i;
+ int uuc_iavg_ma = params->iavg_ua / 1000;
+ int pc_unusable;
+
+ /*
+ * if called first time, fill all the samples with
+ * the shutdown_iavg_ma
+ */
+ if (chip->first_time_calc_uuc && chip->shutdown_iavg_ma != 0) {
+ pr_debug("Using shutdown_iavg_ma = %d in all samples\n",
+ chip->shutdown_iavg_ma);
+ for (i = 0; i < IAVG_SAMPLES; i++)
+ chip->iavg_samples_ma[i] = chip->shutdown_iavg_ma;
+
+ chip->iavg_index = 0;
+ chip->iavg_num_samples = IAVG_SAMPLES;
+ }
+
+ /*
+ * if charging use a nominal avg current to keep
+ * a reasonable UUC while charging
+ */
+ if (uuc_iavg_ma < 0)
+ uuc_iavg_ma = CHARGING_IAVG_MA;
+ chip->iavg_samples_ma[chip->iavg_index] = uuc_iavg_ma;
+ chip->iavg_index = (chip->iavg_index + 1) % IAVG_SAMPLES;
+ chip->iavg_num_samples++;
+ if (chip->iavg_num_samples >= IAVG_SAMPLES)
+ chip->iavg_num_samples = IAVG_SAMPLES;
+
+ /* now that this sample is added calcualte the average */
+ uuc_iavg_ma = 0;
+ if (chip->iavg_num_samples != 0) {
+ for (i = 0; i < chip->iavg_num_samples; i++) {
+ pr_debug("iavg_samples_ma[%d] = %d\n", i,
+ chip->iavg_samples_ma[i]);
+ uuc_iavg_ma += chip->iavg_samples_ma[i];
+ }
+
+ uuc_iavg_ma = DIV_ROUND_CLOSEST(uuc_iavg_ma,
+ chip->iavg_num_samples);
+ }
+
+ uuc_uah_iavg = calculate_termination_uuc(chip, params, uuc_iavg_ma,
+ batt_temp, &pc_unusable);
+ pr_debug("uuc_iavg_ma = %d uuc with iavg = %d\n",
+ uuc_iavg_ma, uuc_uah_iavg);
+
+ chip->prev_uuc_iavg_ma = uuc_iavg_ma;
+ /* restrict the uuc such that it can increase only by one percent */
+ uuc_uah_iavg = adjust_uuc(chip, params, pc_unusable,
+ uuc_uah_iavg, batt_temp);
+
+ chip->first_time_calc_uuc = 0;
+ return uuc_uah_iavg;
+}
+
+static void find_ocv_for_soc(struct qpnp_bms_chip *chip,
+ struct soc_params *params,
+ int batt_temp,
+ int shutdown_soc,
+ int *ret_ocv_uv)
+{
+ s64 ocv_charge_uah;
+ int pc, new_pc;
+ int batt_temp_degc = batt_temp / 10;
+ int ocv_uv;
+
+ ocv_charge_uah = (s64)shutdown_soc
+ * (params->fcc_uah - params->uuc_uah);
+ ocv_charge_uah = div_s64(ocv_charge_uah, 100)
+ + params->cc_uah + params->uuc_uah;
+ pc = DIV_ROUND_CLOSEST((int)ocv_charge_uah * 100, params->fcc_uah);
+ pc = clamp(pc, 0, 100);
+
+ ocv_uv = interpolate_ocv(chip->pc_temp_ocv_lut, batt_temp_degc, pc);
+
+ pr_debug("s_soc = %d, fcc = %d uuc = %d rc = %d, pc = %d, ocv mv = %d\n",
+ shutdown_soc, params->fcc_uah,
+ params->uuc_uah, (int)ocv_charge_uah,
+ pc, ocv_uv);
+ new_pc = interpolate_pc(chip->pc_temp_ocv_lut, batt_temp_degc, ocv_uv);
+ pr_debug("test revlookup pc = %d for ocv = %d\n", new_pc, ocv_uv);
+
+ while (abs(new_pc - pc) > 1) {
+ int delta_mv = 5;
+
+ if (new_pc > pc)
+ delta_mv = -1 * delta_mv;
+
+ ocv_uv = ocv_uv + delta_mv;
+ new_pc = interpolate_pc(chip->pc_temp_ocv_lut,
+ batt_temp_degc, ocv_uv);
+ pr_debug("test revlookup pc = %d for ocv = %d\n",
+ new_pc, ocv_uv);
+ }
+
+ *ret_ocv_uv = ocv_uv * 1000;
+ params->ocv_charge_uah = (int)ocv_charge_uah;
+}
+
+static void calculate_soc_params(struct qpnp_bms_chip *chip,
+ struct raw_soc_params *raw,
+ struct soc_params *params,
+ int batt_temp)
+{
+ int soc_rbatt;
+
+ params->fcc_uah = calculate_fcc(chip, batt_temp);
+ pr_debug("FCC = %uuAh batt_temp = %d\n", params->fcc_uah, batt_temp);
+
+ /* calculate remainging charge */
+ params->ocv_charge_uah = calculate_ocv_charge(
+ chip, raw,
+ params->fcc_uah,
+ batt_temp);
+ pr_debug("ocv_charge_uah = %uuAh\n", params->ocv_charge_uah);
+
+ /* calculate cc micro_volt_hour */
+ params->cc_uah = calculate_cc(chip, raw->cc);
+ pr_debug("cc_uah = %duAh raw->cc = %llx cc = %lld after subtracting %llx\n",
+ params->cc_uah, raw->cc,
+ (int64_t)raw->cc - chip->cc_reading_at_100,
+ chip->cc_reading_at_100);
+
+ soc_rbatt = ((params->ocv_charge_uah - params->cc_uah) * 100)
+ / params->fcc_uah;
+ if (soc_rbatt < 0)
+ soc_rbatt = 0;
+ params->rbatt = get_rbatt(chip, soc_rbatt, batt_temp);
+
+ calculate_iavg(chip, params->cc_uah, ¶ms->iavg_ua);
+
+ params->uuc_uah = calculate_unusable_charge_uah(chip, params,
+ batt_temp);
+ pr_debug("UUC = %uuAh\n", params->uuc_uah);
+}
+
+static bool is_shutdown_soc_within_limits(struct qpnp_bms_chip *chip, int soc)
+{
+ if (chip->shutdown_soc_invalid) {
+ pr_debug("NOT forcing shutdown soc = %d\n", chip->shutdown_soc);
+ return 0;
+ }
+
+ if (abs(chip->shutdown_soc - soc) > chip->shutdown_soc_valid_limit) {
+ pr_debug("rejecting shutdown soc = %d, soc = %d limit = %d\n",
+ chip->shutdown_soc, soc,
+ chip->shutdown_soc_valid_limit);
+ chip->shutdown_soc_invalid = 1;
+ return 0;
+ }
+
+ return 1;
+}
+
+#define BMS_OVERRIDE_MODE_EN_BIT BIT(7)
+#define EN_VBAT_BIT BIT(0)
+#define OVERRIDE_MODE_DELAY_MS 20
+static int override_mode_batt_v_and_i(
+ struct qpnp_bms_chip *chip, int *ibat_ua, int *vbat_uv)
+{
+ int16_t vsense_raw, vbat_raw;
+ int vsense_uv, rc;
+ u8 delay;
+
+ mutex_lock(&chip->bms_output_lock);
+
+ delay = 0x00;
+ rc = qpnp_write_wrapper(chip, &delay,
+ chip->base + BMS1_S1_DELAY_CTL, 1);
+ if (rc)
+ pr_err("unable to write into BMS1_S1_DELAY, rc: %d\n", rc);
+
+ rc = qpnp_masked_write(chip, BMS1_MODE_CTL,
+ BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT,
+ BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT);
+ if (rc)
+ pr_err("unable to write into BMS1_MODE_CTL, rc: %d\n", rc);
+
+ msleep(OVERRIDE_MODE_DELAY_MS);
+
+ lock_output_data(chip);
+ qpnp_read_wrapper(chip, (u8 *)&vsense_raw,
+ chip->base + BMS1_VSENSE_AVG_DATA0, 2);
+ qpnp_read_wrapper(chip, (u8 *)&vbat_raw,
+ chip->base + BMS1_VBAT_AVG_DATA0, 2);
+ unlock_output_data(chip);
+
+ rc = qpnp_masked_write(chip, BMS1_MODE_CTL,
+ BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT, 0);
+
+ delay = 0x0B;
+ rc = qpnp_write_wrapper(chip, &delay,
+ chip->base + BMS1_S1_DELAY_CTL, 1);
+ if (rc)
+ pr_err("unable to write into BMS1_S1_DELAY, rc: %d\n", rc);
+
+ mutex_unlock(&chip->bms_output_lock);
+
+ *vbat_uv = convert_vbatt_raw_to_uv(chip, vbat_raw);
+ vsense_uv = convert_vsense_to_uv(chip, vsense_raw);
+ *ibat_ua = vsense_uv * 1000 / (int)chip->r_sense_mohm;
+
+ pr_debug("vsense_raw = 0x%x vbat_raw = 0x%x ibat_ua = %d vbat_uv = %d\n",
+ (uint16_t)vsense_raw, (uint16_t)vbat_raw,
+ *ibat_ua, *vbat_uv);
+ return 0;
+}
+
+static int get_simultaneous_batt_v_and_i(
+ struct qpnp_bms_chip *chip,
+ int *ibat_ua, int *vbat_uv)
+{
+ int rc;
+ union power_supply_propval ret = {0,};
+
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (chip->batt_psy) {
+ /* if battery has been registered, use the status property */
+ chip->batt_psy->get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ } else {
+ /* default to using separate vbat/ibat if unregistered */
+ ret.intval = POWER_SUPPLY_STATUS_FULL;
+ }
+
+ if (ret.intval == POWER_SUPPLY_STATUS_FULL) {
+ pr_debug("batfet is open using separate vbat and ibat meas\n");
+ rc = get_battery_voltage(vbat_uv);
+ if (rc < 0) {
+ pr_err("adc vbat failed err = %d\n", rc);
+ return rc;
+ }
+ rc = get_battery_current(chip, ibat_ua);
+ if (rc < 0) {
+ pr_err("bms ibat failed err = %d\n", rc);
+ return rc;
+ }
+ } else {
+ return override_mode_batt_v_and_i(chip, ibat_ua, vbat_uv);
+ }
+
+ return 0;
+}
+
+static int bound_soc(int soc)
+{
+ soc = max(0, soc);
+ soc = min(100, soc);
+ return soc;
+}
+
+static int charging_adjustments(struct qpnp_bms_chip *chip,
+ struct soc_params *params, int soc,
+ int vbat_uv, int ibat_ua, int batt_temp)
+{
+ int chg_soc;
+
+ if (chip->soc_at_cv == -EINVAL) {
+ /* In constant current charging return the calc soc */
+ if (vbat_uv <= chip->max_voltage_uv)
+ pr_debug("CC CHG SOC %d\n", soc);
+
+ /* Note the CC to CV point */
+ if (vbat_uv >= chip->max_voltage_uv) {
+ chip->soc_at_cv = soc;
+ chip->prev_chg_soc = soc;
+ chip->ibat_at_cv_ua = ibat_ua;
+ pr_debug("CC_TO_CV ibat_ua = %d CHG SOC %d\n",
+ ibat_ua, soc);
+ }
+ return soc;
+ }
+
+ /*
+ * battery is in CV phase - begin liner inerpolation of soc based on
+ * battery charge current
+ */
+
+ /*
+ * if voltage lessened (possibly because of a system load)
+ * keep reporting the prev chg soc
+ */
+ if (vbat_uv <= chip->max_voltage_uv) {
+ pr_debug("vbat %d < max = %d CC CHG SOC %d\n",
+ vbat_uv, chip->max_voltage_uv, chip->prev_chg_soc);
+ return chip->prev_chg_soc;
+ }
+
+ chg_soc = linear_interpolate(chip->soc_at_cv, chip->ibat_at_cv_ua,
+ 100, -100000,
+ ibat_ua);
+
+ /* always report a higher soc */
+ if (chg_soc > chip->prev_chg_soc) {
+ int new_ocv_uv;
+
+ chip->prev_chg_soc = chg_soc;
+
+ find_ocv_for_soc(chip, params, batt_temp, chg_soc, &new_ocv_uv);
+ chip->last_ocv_uv = new_ocv_uv;
+ pr_debug("CC CHG ADJ OCV = %d CHG SOC %d\n",
+ new_ocv_uv,
+ chip->prev_chg_soc);
+ }
+
+ pr_debug("Reporting CHG SOC %d\n", chip->prev_chg_soc);
+ return chip->prev_chg_soc;
+}
+
+static int adjust_soc(struct qpnp_bms_chip *chip, struct soc_params *params,
+ int soc, int batt_temp)
+{
+ int ibat_ua = 0, vbat_uv = 0;
+ int ocv_est_uv = 0, soc_est = 0, pc_est = 0, pc = 0;
+ int delta_ocv_uv = 0;
+ int n = 0;
+ int rc_new_uah = 0;
+ int pc_new = 0;
+ int soc_new = 0;
+ int slope = 0;
+ int rc = 0;
+ int delta_ocv_uv_limit = 0;
+
+ rc = get_simultaneous_batt_v_and_i(chip, &ibat_ua, &vbat_uv);
+ if (rc < 0) {
+ pr_err("simultaneous vbat ibat failed err = %d\n", rc);
+ goto out;
+ }
+
+ delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000);
+
+ ocv_est_uv = vbat_uv + (ibat_ua * params->rbatt)/1000;
+ pc_est = calculate_pc(chip, ocv_est_uv, batt_temp);
+ soc_est = div_s64((s64)params->fcc_uah * pc_est - params->uuc_uah*100,
+ (s64)params->fcc_uah - params->uuc_uah);
+ soc_est = bound_soc(soc_est);
+
+ if (ibat_ua < 0) {
+ soc = charging_adjustments(chip, params, soc, vbat_uv, ibat_ua,
+ batt_temp);
+ goto out;
+ }
+
+ /*
+ * do not adjust
+ * if soc is same as what bms calculated
+ * if soc_est is between 45 and 25, this is the flat portion of the
+ * curve where soc_est is not so accurate. We generally don't want to
+ * adjust when soc_est is inaccurate except for the cases when soc is
+ * way far off (higher than 50 or lesser than 20).
+ * Also don't adjust soc if it is above 90 becuase it might be pulled
+ * low and cause a bad user experience
+ */
+ if (soc_est == soc
+ || (is_between(45, chip->adjust_soc_low_threshold, soc_est)
+ && is_between(50, chip->adjust_soc_low_threshold - 5, soc))
+ || soc >= 90)
+ goto out;
+
+ if (chip->last_soc_est == -EINVAL)
+ chip->last_soc_est = soc;
+
+ n = min(200, max(1 , soc + soc_est + chip->last_soc_est));
+ chip->last_soc_est = soc_est;
+
+ pc = calculate_pc(chip, chip->last_ocv_uv, batt_temp);
+ if (pc > 0) {
+ pc_new = calculate_pc(chip,
+ chip->last_ocv_uv - (++slope * 1000),
+ batt_temp);
+ while (pc_new == pc) {
+ /* start taking 10mV steps */
+ slope = slope + 10;
+ pc_new = calculate_pc(chip,
+ chip->last_ocv_uv - (slope * 1000),
+ batt_temp);
+ }
+ } else {
+ /*
+ * pc is already at the lowest point,
+ * assume 1 millivolt translates to 1% pc
+ */
+ pc = 1;
+ pc_new = 0;
+ slope = 1;
+ }
+
+ delta_ocv_uv = div_s64((soc - soc_est) * (s64)slope * 1000,
+ n * (pc - pc_new));
+
+ if (abs(delta_ocv_uv) > delta_ocv_uv_limit) {
+ pr_debug("limiting delta ocv %d limit = %d\n", delta_ocv_uv,
+ delta_ocv_uv_limit);
+
+ if (delta_ocv_uv > 0)
+ delta_ocv_uv = delta_ocv_uv_limit;
+ else
+ delta_ocv_uv = -1 * delta_ocv_uv_limit;
+ pr_debug("new delta ocv = %d\n", delta_ocv_uv);
+ }
+
+ chip->last_ocv_uv -= delta_ocv_uv;
+
+ if (chip->last_ocv_uv >= chip->max_voltage_uv)
+ chip->last_ocv_uv = chip->max_voltage_uv;
+
+ /* calculate the soc based on this new ocv */
+ pc_new = calculate_pc(chip, chip->last_ocv_uv, batt_temp);
+ rc_new_uah = (params->fcc_uah * pc_new) / 100;
+ soc_new = (rc_new_uah - params->cc_uah - params->uuc_uah)*100
+ / (params->fcc_uah - params->uuc_uah);
+ soc_new = bound_soc(soc_new);
+
+ /*
+ * if soc_new is ZERO force it higher so that phone doesnt report soc=0
+ * soc = 0 should happen only when soc_est == 0
+ */
+ if (soc_new == 0 && soc_est != 0)
+ soc_new = 1;
+
+ soc = soc_new;
+
+out:
+ pr_debug("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, pc_new = %d, soc_new = %d, rbatt = %d, slope = %d\n",
+ ibat_ua, vbat_uv, ocv_est_uv, pc_est,
+ soc_est, n, delta_ocv_uv, chip->last_ocv_uv,
+ pc_new, soc_new, params->rbatt, slope);
+
+ return soc;
+}
+
static int calculate_state_of_charge(struct qpnp_bms_chip *chip,
struct raw_soc_params *raw,
int batt_temp)
{
- chip->calculated_soc = 50;
+ int soc, new_ocv_uv;
+ int shutdown_soc, new_calculated_soc, remaining_usable_charge_uah;
+ struct soc_params params;
+
+ calculate_soc_params(chip, raw, ¶ms, batt_temp);
+ /* calculate remaining usable charge */
+ remaining_usable_charge_uah = params.ocv_charge_uah
+ - params.cc_uah
+ - params.uuc_uah;
+
+ pr_debug("RUC = %duAh\n", remaining_usable_charge_uah);
+ if (params.fcc_uah - params.uuc_uah <= 0) {
+ pr_warn("FCC = %duAh, UUC = %duAh forcing soc = 0\n",
+ params.fcc_uah,
+ params.uuc_uah);
+ soc = 0;
+ } else {
+ soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
+ (params.fcc_uah
+ - params.uuc_uah));
+ }
+
+ if (chip->first_time_calc_soc && soc < 0) {
+ /*
+ * first time calcualtion and the pon ocv is too low resulting
+ * in a bad soc. Adjust ocv to get 0 soc
+ */
+ pr_debug("soc is %d, adjusting pon ocv to make it 0\n", soc);
+ find_ocv_for_soc(chip, ¶ms, batt_temp, 0, &new_ocv_uv);
+ chip->last_ocv_uv = new_ocv_uv;
+
+ remaining_usable_charge_uah = params.ocv_charge_uah
+ - params.cc_uah
+ - params.uuc_uah;
+
+ soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
+ (params.fcc_uah
+ - params.uuc_uah));
+ pr_debug("DONE for O soc is %d, pon ocv adjusted to %duV\n",
+ soc, chip->last_ocv_uv);
+ }
+
+ if (soc > 100)
+ soc = 100;
+
+ if (soc < 0) {
+ pr_err("bad rem_usb_chg = %d rem_chg %d, cc_uah %d, unusb_chg %d\n",
+ remaining_usable_charge_uah,
+ params.ocv_charge_uah,
+ params.cc_uah, params.uuc_uah);
+
+ pr_err("for bad rem_usb_chg last_ocv_uv = %d batt_temp = %d fcc = %d soc =%d\n",
+ chip->last_ocv_uv, batt_temp,
+ params.fcc_uah, soc);
+ soc = 0;
+ }
+
+ mutex_lock(&chip->soc_invalidation_mutex);
+ shutdown_soc = chip->shutdown_soc;
+
+ if (chip->first_time_calc_soc && soc != shutdown_soc
+ && is_shutdown_soc_within_limits(chip, soc)) {
+ /*
+ * soc for the first time - use shutdown soc
+ * to adjust pon ocv since it is a small percent away from
+ * the real soc
+ */
+ pr_debug("soc = %d before forcing shutdown_soc = %d\n",
+ soc, shutdown_soc);
+ find_ocv_for_soc(chip, ¶ms, batt_temp,
+ shutdown_soc, &new_ocv_uv);
+ chip->pon_ocv_uv = chip->last_ocv_uv;
+ chip->last_ocv_uv = new_ocv_uv;
+
+ remaining_usable_charge_uah = params.ocv_charge_uah
+ - params.cc_uah
+ - params.uuc_uah;
+
+ soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
+ (params.fcc_uah
+ - params.uuc_uah));
+
+ pr_debug("DONE for shutdown_soc = %d soc is %d, adjusted ocv to %duV\n",
+ shutdown_soc, soc, chip->last_ocv_uv);
+ }
+ mutex_unlock(&chip->soc_invalidation_mutex);
+
+ pr_debug("SOC before adjustment = %d\n", soc);
+ new_calculated_soc = adjust_soc(chip, ¶ms, soc, batt_temp);
+
+ if (new_calculated_soc != chip->calculated_soc
+ && chip->bms_psy.name != NULL) {
+ power_supply_changed(&chip->bms_psy);
+ pr_debug("power supply changed\n");
+ }
+
+ chip->calculated_soc = new_calculated_soc;
+ pr_debug("Set calculated SOC = %d\n", chip->calculated_soc);
+ chip->first_time_calc_soc = 0;
return chip->calculated_soc;
}
@@ -472,6 +1436,168 @@
(chip->calculate_soc_ms)));
}
+static void backup_soc_and_iavg(struct qpnp_bms_chip *chip, int batt_temp,
+ int soc)
+{
+ u8 temp;
+ int rc;
+ int iavg_ma = chip->prev_uuc_iavg_ma;
+
+ if (iavg_ma > IAVG_START)
+ temp = (iavg_ma - IAVG_START) / IAVG_STEP_SIZE_MA;
+ else
+ temp = 0;
+
+ rc = qpnp_write_wrapper(chip, &temp,
+ chip->base + IAVG_STORAGE_REG, 1);
+
+ if (soc == 0)
+ temp = SOC_ZERO;
+ else
+ temp = soc;
+
+ /* don't store soc if temperature is below 5degC */
+ if (batt_temp > IGNORE_SOC_TEMP_DECIDEG)
+ rc = qpnp_write_wrapper(chip, &temp,
+ chip->base + SOC_STORAGE_REG, 1);
+}
+
+#define SOC_CATCHUP_SEC_MAX 600
+#define SOC_CATCHUP_SEC_PER_PERCENT 60
+#define MAX_CATCHUP_SOC (SOC_CATCHUP_SEC_MAX/SOC_CATCHUP_SEC_PER_PERCENT)
+static int scale_soc_while_chg(struct qpnp_bms_chip *chip,
+ int delta_time_us, int new_soc, int prev_soc)
+{
+ int chg_time_sec;
+ int catch_up_sec;
+ int scaled_soc;
+ int numerator;
+
+ /*
+ * The device must be charging for reporting a higher soc, if
+ * not ignore this soc and continue reporting the prev_soc.
+ * Also don't report a high value immediately slowly scale the
+ * value from prev_soc to the new soc based on a charge time
+ * weighted average
+ */
+
+ /* if not charging, return last soc */
+ if (chip->start_percent == -EINVAL)
+ return prev_soc;
+
+ chg_time_sec = DIV_ROUND_UP(chip->charge_time_us, USEC_PER_SEC);
+ catch_up_sec = DIV_ROUND_UP(chip->catch_up_time_us, USEC_PER_SEC);
+ pr_debug("cts= %d catch_up_sec = %d\n", chg_time_sec, catch_up_sec);
+
+ /*
+ * if charging for more than catch_up time, simply return
+ * new soc
+ */
+ if (chg_time_sec > catch_up_sec)
+ return new_soc;
+
+ numerator = (catch_up_sec - chg_time_sec) * prev_soc
+ + chg_time_sec * new_soc;
+ scaled_soc = numerator / catch_up_sec;
+
+ pr_debug("cts = %d new_soc = %d prev_soc = %d scaled_soc = %d\n",
+ chg_time_sec, new_soc, prev_soc, scaled_soc);
+
+ return scaled_soc;
+}
+
+/*
+ * bms_fake_battery is set in setups where a battery emulator is used instead
+ * of a real battery. This makes the bms driver report a different/fake value
+ * regardless of the calculated state of charge.
+ */
+static int bms_fake_battery = -EINVAL;
+module_param(bms_fake_battery, int, 0644);
+
+static int report_state_of_charge(struct qpnp_bms_chip *chip)
+{
+ int soc;
+ int delta_time_us;
+ struct timespec now;
+ struct qpnp_vadc_result result;
+ int batt_temp;
+ int rc;
+
+ if (bms_fake_battery != -EINVAL) {
+ pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery);
+ return bms_fake_battery;
+ }
+
+ soc = chip->calculated_soc;
+
+ rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result);
+
+ if (rc) {
+ pr_err("error reading adc channel = %d, rc = %d\n",
+ LR_MUX1_BATT_THERM, rc);
+ return rc;
+ }
+ pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+ result.measurement);
+ batt_temp = (int)result.physical;
+
+ do_posix_clock_monotonic_gettime(&now);
+ if (chip->t_soc_queried.tv_sec != 0) {
+ delta_time_us
+ = (now.tv_sec - chip->t_soc_queried.tv_sec) * USEC_PER_SEC
+ + (now.tv_nsec - chip->t_soc_queried.tv_nsec) / 1000;
+ } else {
+ /* calculation for the first time */
+ delta_time_us = 0;
+ }
+
+ /*
+ * account for charge time - limit it to SOC_CATCHUP_SEC to
+ * avoid overflows when charging continues for extended periods
+ */
+ if (chip->start_percent != -EINVAL) {
+ if (chip->charge_time_us == 0) {
+ /*
+ * calculating soc for the first time
+ * after start of chg. Initialize catchup time
+ */
+ if (abs(soc - chip->last_soc) < MAX_CATCHUP_SOC)
+ chip->catch_up_time_us =
+ (soc - chip->last_soc)
+ * SOC_CATCHUP_SEC_PER_PERCENT
+ * USEC_PER_SEC;
+ else
+ chip->catch_up_time_us =
+ SOC_CATCHUP_SEC_MAX * USEC_PER_SEC;
+
+ if (chip->catch_up_time_us < 0)
+ chip->catch_up_time_us = 0;
+ }
+
+ /* add charge time */
+ if (chip->charge_time_us < SOC_CATCHUP_SEC_MAX * USEC_PER_SEC)
+ chip->charge_time_us += delta_time_us;
+
+ /* end catchup if calculated soc and last soc are same */
+ if (chip->last_soc == soc)
+ chip->catch_up_time_us = 0;
+ }
+
+ /* last_soc < soc ... scale and catch up */
+ if (chip->last_soc != -EINVAL && chip->last_soc < soc && soc != 100)
+ soc = scale_soc_while_chg(chip, delta_time_us,
+ soc, chip->last_soc);
+
+ pr_debug("last_soc = %d, calculated_soc = %d, soc = %d\n",
+ chip->last_soc, chip->calculated_soc, soc);
+ chip->last_soc = soc;
+ backup_soc_and_iavg(chip, batt_temp, chip->last_soc);
+ pr_debug("Reported SOC = %d\n", chip->last_soc);
+ chip->t_soc_queried = now;
+
+ return chip->last_soc;
+}
+
static int calculate_soc_from_voltage(struct qpnp_bms_chip *chip)
{
int voltage_range_uv, voltage_remaining_uv, voltage_based_soc;
@@ -491,8 +1617,8 @@
{
if (use_voltage_soc)
return calculate_soc_from_voltage(chip);
-
- return chip->calculated_soc;
+ else
+ return report_state_of_charge(chip);
}
/* Returns instantaneous current in uA */
@@ -775,7 +1901,11 @@
chip->prev_pc_unusable = -EINVAL;
chip->soc_at_cv = -EINVAL;
chip->calculated_soc = -EINVAL;
+ chip->last_soc = -EINVAL;
chip->last_vbat_read_uv = -EINVAL;
+ chip->last_soc_est = -EINVAL;
+ chip->first_time_calc_soc = 1;
+ chip->first_time_calc_uuc = 1;
}
static int __devinit
@@ -797,6 +1927,7 @@
mutex_init(&chip->bms_output_lock);
mutex_init(&chip->last_ocv_uv_mutex);
+ mutex_init(&chip->soc_invalidation_mutex);
bms_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
if (!bms_resource) {
diff --git a/drivers/video/msm/mdp4_overlay_writeback.c b/drivers/video/msm/mdp4_overlay_writeback.c
index 18c6635..6c2b1f6 100644
--- a/drivers/video/msm/mdp4_overlay_writeback.c
+++ b/drivers/video/msm/mdp4_overlay_writeback.c
@@ -171,6 +171,8 @@
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
int ret = 0;
+ int undx;
+ struct vsync_update *vp;
pr_debug("%s+:\n", __func__);
@@ -189,6 +191,16 @@
mdp4_overlay_pipe_free(pipe);
vctrl->base_pipe = NULL;
+ undx = vctrl->update_ndx;
+ vp = &vctrl->vlist[undx];
+ if (vp->update_cnt) {
+ /*
+ * pipe's iommu will be freed at next overlay play
+ * and iommu_drop statistic will be increased by one
+ */
+ vp->update_cnt = 0; /* empty queue */
+ }
+
ret = panel_next_off(pdev);
mdp_clk_ctrl(1);
@@ -253,6 +265,12 @@
mdp4_mixer_stage_up(pipe, 0);
mdp4_overlayproc_cfg(pipe);
+
+ if (hdmi_prim_display)
+ outpdw(MDP_BASE + 0x100F4, 0x01);
+ else
+ outpdw(MDP_BASE + 0x100F4, 0x02);
+
/* MDP cmd block disable */
mdp_clk_ctrl(0);
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 45d7a3e..43daaf2 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -675,18 +675,18 @@
};
static const uint32_t msg_bld_masks_22[] = {
- MSG_LVL_HIGH,
- MSG_LVL_HIGH,
- MSG_LVL_HIGH,
- MSG_LVL_HIGH,
- MSG_LVL_HIGH,
- MSG_LVL_HIGH,
- MSG_LVL_HIGH,
- MSG_LVL_HIGH,
- MSG_LVL_HIGH,
- MSG_LVL_HIGH,
- MSG_LVL_HIGH,
- MSG_LVL_HIGH
+ MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW
};
/* LOG CODES */
diff --git a/include/linux/mm.h b/include/linux/mm.h
index ddfb7c5..48268f0 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1561,6 +1561,32 @@
#define in_gate_area(mm, addr) ({(void)mm; in_gate_area_no_mm(addr);})
#endif /* __HAVE_ARCH_GATE_AREA */
+#ifdef CONFIG_USE_USER_ACCESSIBLE_TIMERS
+static inline int use_user_accessible_timers(void) { return 1; }
+extern int in_user_timers_area(struct mm_struct *mm, unsigned long addr);
+extern struct vm_area_struct *get_user_timers_vma(struct mm_struct *mm);
+extern int get_user_timer_page(struct vm_area_struct *vma,
+ struct mm_struct *mm, unsigned long start, unsigned int gup_flags,
+ struct page **pages, int idx, int *goto_next_page);
+#else
+static inline int use_user_accessible_timers(void) { return 0; }
+static inline int in_user_timers_area(struct mm_struct *mm, unsigned long addr)
+{
+ return 0;
+}
+static inline struct vm_area_struct *get_user_timers_vma(struct mm_struct *mm)
+{
+ return NULL;
+}
+static inline int get_user_timer_page(struct vm_area_struct *vma,
+ struct mm_struct *mm, unsigned long start, unsigned int gup_flags,
+ struct page **pages, int idx, int *goto_next_page)
+{
+ *goto_next_page = 0;
+ return 0;
+}
+#endif
+
int drop_caches_sysctl_handler(struct ctl_table *, int,
void __user *, size_t *, loff_t *);
unsigned long shrink_slab(struct shrink_control *shrink,
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index e9051e1..c5b492b 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -91,6 +91,7 @@
unsigned int quirks2; /* More deviations from spec. */
#define SDHCI_QUIRK2_HOST_OFF_CARD_ON (1<<0)
+#define SDHCI_QUIRK2_OWN_CARD_DETECTION (1<<1)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 41ff312..8f86fce 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1810,6 +1810,8 @@
V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE = 0,
V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_ENABLE = 1
};
+#define V4L2_CID_MPEG_VIDC_VIDEO_SECURE (V4L2_CID_MPEG_MSM_VIDC_BASE+24)
+
/* Camera class control IDs */
#define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900)
#define V4L2_CID_CAMERA_CLASS (V4L2_CTRL_CLASS_CAMERA | 1)
diff --git a/mm/Kconfig b/mm/Kconfig
index 4cde97f..bbab5a6 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -390,3 +390,14 @@
the memory corresponding to the hole to be removed using memblock-
remove.
+config USE_USER_ACCESSIBLE_TIMERS
+ bool "Enables timers accessible from userspace"
+ depends on MMU
+ help
+ User-accessible timers allow the kernel to map kernel timer
+ registers to a userspace accessible page, to allow faster
+ access to time information. This flag will enable the
+ interface code in the main kernel. However, there are
+ architecture-specific code that will need to be enabled
+ separately.
+
diff --git a/mm/memory.c b/mm/memory.c
index 6105f47..174fcaa 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1726,6 +1726,19 @@
goto next_page;
}
+ if (use_user_accessible_timers()) {
+ if (!vma && in_user_timers_area(mm, start)) {
+ int goto_next_page = 0;
+ int user_timer_ret = get_user_timer_page(vma,
+ mm, start, gup_flags, pages, i,
+ &goto_next_page);
+ if (goto_next_page)
+ goto next_page;
+ else
+ return user_timer_ret;
+ }
+ }
+
if (!vma ||
(vma->vm_flags & (VM_IO | VM_PFNMAP)) ||
!(vm_flags & vma->vm_flags))
diff --git a/mm/mlock.c b/mm/mlock.c
index ef726e8..38c77ab 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -229,7 +229,9 @@
if (!((vma->vm_flags & (VM_DONTEXPAND | VM_RESERVED)) ||
is_vm_hugetlb_page(vma) ||
- vma == get_gate_vma(current->mm))) {
+ vma == get_gate_vma(current->mm) ||
+ ((use_user_accessible_timers() &&
+ (vma == get_user_timers_vma(current->mm)))))) {
__mlock_vma_pages_range(vma, start, end, NULL);
@@ -324,7 +326,9 @@
int lock = !!(newflags & VM_LOCKED);
if (newflags == vma->vm_flags || (vma->vm_flags & VM_SPECIAL) ||
- is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm))
+ is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm) ||
+ ((use_user_accessible_timers()) &&
+ (vma == get_user_timers_vma(current->mm))))
goto out; /* don't set VM_LOCKED, don't count */
pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);