summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt4
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt2
-rw-r--r--arch/arm/boot/compressed/head.S2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi11
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-blsp.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi88
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dts29
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi52
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-pinctrl.dtsi64
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-rumi.dts26
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-sim.dts26
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi23
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon.dtsi58
-rw-r--r--arch/arm/boot/dts/qcom/msmtriton.dtsi42
-rw-r--r--arch/arm/configs/msmfalcon_defconfig1
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig2
-rw-r--r--arch/arm64/configs/msmcortex_defconfig2
-rw-r--r--drivers/clk/msm/clock-osm.c6
-rw-r--r--drivers/clk/qcom/clk-rcg2.c2
-rw-r--r--drivers/clk/qcom/gpucc-msmfalcon.c8
-rw-r--r--drivers/devfreq/governor_bw_vbif.c12
-rw-r--r--drivers/gpu/msm/adreno.c2
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.c45
-rw-r--r--drivers/gpu/msm/kgsl_pwrscale.c6
-rw-r--r--drivers/gpu/msm/kgsl_pwrscale.h4
-rw-r--r--drivers/input/misc/ots_pat9125/pat9125_linux_driver.c176
-rw-r--r--drivers/input/misc/ots_pat9125/pixart_ots.c5
-rw-r--r--drivers/input/misc/ots_pat9125/pixart_ots.h7
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.c236
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.h11
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx_update.c13
-rw-r--r--drivers/iommu/arm-smmu.c2
-rw-r--r--drivers/media/dvb-core/demux.h6
-rw-r--r--drivers/media/dvb-core/dmxdev.c51
-rw-r--r--drivers/media/dvb-core/dvb_demux.c16
-rw-r--r--drivers/media/dvb-core/dvb_net.c6
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp40.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp44.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp46.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c2
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c4
-rw-r--r--drivers/platform/msm/gsi/gsi.c21
-rw-r--r--drivers/platform/msm/gsi/gsi.h11
-rw-r--r--drivers/platform/msm/ipa/ipa_rm.c7
-rw-r--r--drivers/platform/msm/ipa/ipa_rm_i.h4
-rw-r--r--drivers/platform/msm/ipa/ipa_rm_resource.c15
-rw-r--r--drivers/platform/msm/ipa/ipa_rm_resource.h3
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_dp.c37
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_i.h1
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_utils.c7
-rw-r--r--drivers/power/power_supply_sysfs.c1
-rw-r--r--drivers/power/qcom-charger/bcl_peripheral.c2
-rw-r--r--drivers/power/qcom-charger/fg-core.h9
-rw-r--r--drivers/power/qcom-charger/fg-util.c11
-rw-r--r--drivers/power/qcom-charger/qpnp-fg-gen3.c67
-rw-r--r--drivers/power/qcom-charger/qpnp-smb2.c59
-rw-r--r--drivers/power/qcom-charger/smb-lib.c214
-rw-r--r--drivers/power/qcom-charger/smb-lib.h11
-rw-r--r--drivers/power/qcom-charger/smb-reg.h1
-rw-r--r--drivers/power/qcom-charger/smb1351-charger.c8
-rw-r--r--drivers/soc/qcom/Kconfig8
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/early_random.c63
-rw-r--r--drivers/soc/qcom/glink_spi_xprt.c19
-rw-r--r--drivers/soc/qcom/icnss.c57
-rw-r--r--drivers/soc/qcom/service-notifier.c2
-rw-r--r--drivers/thermal/cpu_cooling.c56
-rw-r--r--drivers/thermal/lmh_lite.c13
-rw-r--r--drivers/thermal/msm_lmh_dcvs.c76
-rw-r--r--drivers/thermal/msm_thermal.c2
-rw-r--r--drivers/tty/serial/msm_serial_hs.c1
-rw-r--r--drivers/usb/gadget/function/f_qc_rndis.c6
-rw-r--r--drivers/usb/pd/policy_engine.c187
-rw-r--r--include/linux/cpu_cooling.h16
-rw-r--r--include/linux/power_supply.h1
-rw-r--r--include/linux/sched.h15
-rw-r--r--include/linux/sched/core_ctl.h (renamed from kernel/sched/core_ctl.h)7
-rw-r--r--include/linux/sched/sysctl.h1
-rw-r--r--include/linux/types.h3
-rw-r--r--include/trace/events/sched.h63
-rw-r--r--init/main.c9
-rw-r--r--kernel/fork.c2
-rw-r--r--kernel/sched/core.c42
-rw-r--r--kernel/sched/core_ctl.c33
-rw-r--r--kernel/sched/debug.c1
-rw-r--r--kernel/sched/fair.c3
-rw-r--r--kernel/sched/hmp.c715
-rw-r--r--kernel/sched/sched.h31
-rw-r--r--kernel/smpboot.c2
-rw-r--r--kernel/sysctl.c8
-rw-r--r--sound/soc/codecs/wcd-spi.c89
-rw-r--r--sound/soc/codecs/wcd9335.c16
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.c39
-rw-r--r--sound/soc/msm/msmcobalt.c14
-rw-r--r--sound/usb/usb_audio_qmi_svc.c2
96 files changed, 2559 insertions, 599 deletions
diff --git a/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt b/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt
index 54ba2be39f0c..d9caa295cc6e 100644
--- a/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt
+++ b/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt
@@ -10,6 +10,8 @@ Required properties:
- reg : i2c slave address of the device.
- interrupt-parent : parent of interrupt.
- interrupts : interrupt to indicate motion of the rotating switch.
+ - vdd-supply : Power supply needed to power up the device.
+ - vld-supply : Power source required to power up I2C bus.
Optional properties:
- pixart,inverse-x : boolean, use this to invert the x data before sending it to input framework
@@ -44,6 +46,8 @@ Example:
reg = <0x75>;
interrupt-parent = <&msm_gpio>;
interrupts = <98 0x2008>;
+ vdd-supply = <&pm8110_l5>;
+ vld-supply = <&pm8110_l17>;
pixart,irq-gpio = <&msm_gpio 98 0x2008>;
pinctrl-names = "pmx_rot_switch_active",
"pmx_rot_switch_suspend",
diff --git a/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt b/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt
index 4c676fa66e62..ff8fb76166a3 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt
@@ -34,7 +34,7 @@ Optional properties:
It is a four tuple consisting of min x,
min y, max x and max y values.
- goodix,i2c-pull-up : To specify pull up is required.
- - goodix,no-force-update : To specify force update is allowed.
+ - goodix,force-update : To specify force update is allowed.
- goodix,enable-power-off : Power off touchscreen during suspend.
- goodix,button-map : Button map of key codes. The number of key codes
depend on panel.
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index ae85dcdcb7df..d2e43b053d9b 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -776,7 +776,7 @@ __armv7_mmu_cache_on:
orrne r0, r0, #1 @ MMU enabled
movne r1, #0xfffffffd @ domain 0 = client
bic r6, r6, #1 << 31 @ 32-bit translation system
- bic r6, r6, #3 << 0 @ use only ttbr0
+ bic r6, r6, #(7 << 0) | (1 << 4) @ use only ttbr0
mcrne p15, 0, r3, c2, c0, 0 @ load page table pointer
mcrne p15, 0, r0, c8, c7, 0 @ flush I,D TLBs
mcr p15, 0, r0, c7, c5, 4 @ ISB
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi
index a1d80075abe0..3681f3d34b0c 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi
@@ -85,14 +85,15 @@
asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
<&loopback>, <&compress>, <&hostless>,
<&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>,
- <&pcm_noirq>;
+ <&pcm_noirq>, <&cpe3>;
asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
"msm-pcm-dsp.2", "msm-voip-dsp",
"msm-pcm-voice", "msm-pcm-loopback",
"msm-compress-dsp", "msm-pcm-hostless",
"msm-pcm-afe", "msm-lsm-client",
"msm-pcm-routing", "msm-cpe-lsm",
- "msm-compr-dsp", "msm-pcm-dsp-noirq";
+ "msm-compr-dsp", "msm-pcm-dsp-noirq",
+ "msm-cpe-lsm.3";
asoc-cpu = <&dai_hdmi>, <&dai_dp>,
<&dai_mi2s0>, <&dai_mi2s1>,
<&dai_mi2s2>, <&dai_mi2s3>,
@@ -244,6 +245,12 @@
cpe: qcom,msm-cpe-lsm {
compatible = "qcom,msm-cpe-lsm";
+ qcom,msm-cpe-lsm-id = <1>;
+ };
+
+ cpe3: qcom,msm-cpe-lsm@3 {
+ compatible = "qcom,msm-cpe-lsm";
+ qcom,msm-cpe-lsm-id = <3>;
};
qcom,wcd-dsp-mgr {
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-blsp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-blsp.dtsi
index 929a079c64c3..a660ea06795e 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-blsp.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-blsp.dtsi
@@ -736,8 +736,10 @@
clocks = <&clock_gcc clk_gcc_blsp1_uart3_apps_clk>,
<&clock_gcc clk_gcc_blsp1_ahb_clk>;
pinctrl-names = "sleep", "default";
- pinctrl-0 = <&blsp1_uart3_sleep>;
- pinctrl-1 = <&blsp1_uart3_active>;
+ pinctrl-0 = <&blsp1_uart3_tx_sleep>, <&blsp1_uart3_rxcts_sleep>,
+ <&blsp1_uart3_rfr_sleep>;
+ pinctrl-1 = <&blsp1_uart3_tx_active>,
+ <&blsp1_uart3_rxcts_active>, <&blsp1_uart3_rfr_active>;
qcom,msm-bus,name = "buart3";
qcom,msm-bus,num-cases = <2>;
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
index 3975bc5d16f5..e5fd988dccce 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
@@ -1223,29 +1223,83 @@
};
};
- blsp1_uart3_active: blsp1_uart3_active {
- mux {
- pins = "gpio45", "gpio46", "gpio47", "gpio48";
- function = "blsp_uart3_a";
+ blsp1_uart3: blsp1_uart3 {
+ blsp1_uart3_tx_active: blsp1_uart3_tx_active {
+ mux {
+ pins = "gpio45";
+ function = "blsp_uart3_a";
+ };
+
+ config {
+ pins = "gpio45";
+ drive-strength = <2>;
+ bias-disable;
+ };
};
- config {
- pins = "gpio45", "gpio46", "gpio47", "gpio48";
- drive-strength = <2>;
- bias-disable;
+ blsp1_uart3_tx_sleep: blsp1_uart3_tx_sleep {
+ mux {
+ pins = "gpio45";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio45";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
};
- };
- blsp1_uart3_sleep: blsp1_uart3_sleep {
- mux {
- pins = "gpio45", "gpio46", "gpio47", "gpio48";
- function = "gpio";
+ blsp1_uart3_rxcts_active: blsp1_uart3_rxcts_active {
+ mux {
+ pins = "gpio46", "gpio47";
+ function = "blsp_uart3_a";
+ };
+
+ config {
+ pins = "gpio46", "gpio47";
+ drive-strength = <2>;
+ bias-disable;
+ };
};
- config {
- pins = "gpio45", "gpio46", "gpio47", "gpio48";
- drive-strength = <2>;
- bias-pull-up;
+ blsp1_uart3_rxcts_sleep: blsp1_uart3_rxcts_sleep {
+ mux {
+ pins = "gpio46", "gpio47";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio46", "gpio47";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+
+ blsp1_uart3_rfr_active: blsp1_uart3_rfr_active {
+ mux {
+ pins = "gpio48";
+ function = "blsp_uart3_a";
+ };
+
+ config {
+ pins = "gpio48";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart3_rfr_sleep: blsp1_uart3_rfr_sleep {
+ mux {
+ pins = "gpio48";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio48";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dts b/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dts
index e53912071502..ee6a58a41b4f 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dts
+++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dts
@@ -21,3 +21,32 @@
compatible = "qcom,msmcobalt-qrd", "qcom,msmcobalt", "qcom,qrd";
qcom,board-id = <0x02000b 0x80>;
};
+
+&soc {
+ sound-tavil {
+ qcom,model = "msmcobalt-qvr-tavil-snd-card";
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "MADINPUT", "MCLK",
+ "AMIC2", "MIC BIAS2",
+ "MIC BIAS2", "Headset Mic",
+ "DMIC0", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic0",
+ "DMIC1", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic1",
+ "DMIC2", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic2",
+ "DMIC4", "MIC BIAS4",
+ "MIC BIAS4", "Digital Mic4",
+ "SpkrLeft IN", "SPK1 OUT";
+
+ qcom,msm-mbhc-hphl-swh = <1>;
+ /delete-property/ qcom,us-euro-gpios;
+ /delete-property/ qcom,hph-en0-gpio;
+ /delete-property/ qcom,hph-en0-gpio;
+
+ qcom,wsa-max-devs = <1>;
+ qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0213>;
+ qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft";
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi
index 8016a3822a7f..0134093d5053 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi
@@ -326,18 +326,18 @@
1900800000>;
qcom,cpr-ro-scaling-factor =
- <4001 4019 3747 3758 3564 3480 2336
- 2247 3442 3147 2136 4156 4028 3030
- 3727 3198>,
- <4001 4019 3747 3758 3564 3480 2336
- 2247 3442 3147 2136 4156 4028 3030
- 3727 3198>,
- <3704 3601 3465 3567 3356 3473 2686
- 2773 3049 2932 2235 3816 3800 3097
- 2966 2808>,
- <2974 3092 3288 3329 2905 3096 3119
- 3225 2865 3140 2892 3592 3408 3576
- 1559 1392>;
+ <2595 2794 2577 2762 2471 2674 2199
+ 2553 3189 3255 3192 2962 3054 2982
+ 2042 2945>,
+ <2595 2794 2577 2762 2471 2674 2199
+ 2553 3189 3255 3192 2962 3054 2982
+ 2042 2945>,
+ <2391 2550 2483 2638 2382 2564 2259
+ 2555 2766 3041 2988 2935 2873 2688
+ 2013 2784>,
+ <2066 2153 2300 2434 2220 2386 2288
+ 2465 2028 2511 2487 2734 2554 2117
+ 1892 2377>;
qcom,cpr-open-loop-voltage-fuse-adjustment =
/* Speed bin 0 */
@@ -487,18 +487,18 @@
2112000000 2208000000>;
qcom,cpr-ro-scaling-factor =
- <4001 4019 3747 3758 3564 3480 2336
- 2247 3442 3147 2136 4156 4028 3030
- 3727 3190>,
- <4001 4019 3747 3758 3564 3480 2336
- 2247 3442 3147 2136 4156 4028 3030
- 3727 3198>,
- <3704 3601 3465 3567 3356 3473 2686
- 2773 3049 2932 2235 3816 3800 3097
- 2966 2808>,
- <2974 3092 3288 3329 2905 3096 3119
- 3225 2865 3140 2892 3592 3408 3576
- 1559 1392>;
+ <2857 3057 2828 2952 2699 2798 2446
+ 2631 2629 2578 2244 3344 3289 3137
+ 3164 2655>,
+ <2857 3057 2828 2952 2699 2798 2446
+ 2631 2629 2578 2244 3344 3289 3137
+ 3164 2655>,
+ <2603 2755 2676 2777 2573 2685 2465
+ 2610 2312 2423 2243 3104 3022 3036
+ 2740 2303>,
+ <1901 2016 2096 2228 2034 2161 2077
+ 2188 1565 1870 1925 2235 2205 2413
+ 1762 1478>;
qcom,cpr-open-loop-voltage-fuse-adjustment =
/* Speed bin 0 */
@@ -672,8 +672,8 @@
<1036800 444000000 0x55555555>, /* 720p@240, 1080p@120,1440p@60,
* UHD@30 */ /*NOMINAL*/
< 829440 355200000 0x55555555>, /* UHD/4096x2160@30 SVSL1 */
- < 489600 269330000 0x55555555>, /* 1080p@60 SVS */
- < 432000 200000000 0x55555555>, /* 720p@120, 1080p@30 */
+ < 489600 269330000 0x55555555>, /* 1080p@60, 720p@120 SVS */
+ < 345600 200000000 0x55555555>, /* 2560x1440@24, 1080p@30 */
/* SVS2 */
/* Decoders */
diff --git a/arch/arm/boot/dts/qcom/msmfalcon-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-pinctrl.dtsi
index d28d09c2a527..e8c66871425d 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/msmfalcon-pinctrl.dtsi
@@ -48,5 +48,69 @@
output-low;
};
};
+
+ /* SDC pin type */
+ sdc1_clk_on: sdc1_clk_on {
+ config {
+ pins = "sdc1_clk";
+ bias-disable; /* NO pull */
+ drive-strength = <16>; /* 16 MA */
+ };
+ };
+
+ sdc1_clk_off: sdc1_clk_off {
+ config {
+ pins = "sdc1_clk";
+ bias-disable; /* NO pull */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ sdc1_cmd_on: sdc1_cmd_on {
+ config {
+ pins = "sdc1_cmd";
+ bias-pull-up; /* pull up */
+ drive-strength = <10>; /* 10 MA */
+ };
+ };
+
+ sdc1_cmd_off: sdc1_cmd_off {
+ config {
+ pins = "sdc1_cmd";
+ num-grp-pins = <1>;
+ bias-pull-up; /* pull up */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ sdc1_data_on: sdc1_data_on {
+ config {
+ pins = "sdc1_data";
+ bias-pull-up; /* pull up */
+ drive-strength = <10>; /* 10 MA */
+ };
+ };
+
+ sdc1_data_off: sdc1_data_off {
+ config {
+ pins = "sdc1_data";
+ bias-pull-up; /* pull up */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ sdc1_rclk_on: sdc1_rclk_on {
+ config {
+ pins = "sdc1_rclk";
+ bias-pull-down; /* pull down */
+ };
+ };
+
+ sdc1_rclk_off: sdc1_rclk_off {
+ config {
+ pins = "sdc1_rclk";
+ bias-pull-down; /* pull down */
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom/msmfalcon-rumi.dts b/arch/arm/boot/dts/qcom/msmfalcon-rumi.dts
index 0d694a6cd9fa..f0ba8b115120 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon-rumi.dts
+++ b/arch/arm/boot/dts/qcom/msmfalcon-rumi.dts
@@ -27,3 +27,29 @@
pinctrl-names = "default";
pinctrl-0 = <&uart_console_active>;
};
+
+&sdhc_1 {
+ /* device core power supply */
+ vdd-supply = <&pmfalcon_l4b>;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <200 570000>;
+
+ /* device communication power supply */
+ vdd-io-supply = <&pmfalcon_l8a>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <200 325000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on &sdc1_rclk_on>;
+ pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>;
+
+ qcom,clk-rates = <400000 20000000 25000000 50000000 192000000
+ 384000000>;
+
+ qcom,nonremovable;
+ qcom,bus-speed-mode = "HS400_1p8v", "HS200_1p8v", "DDR_1p8v";
+
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/qcom/msmfalcon-sim.dts b/arch/arm/boot/dts/qcom/msmfalcon-sim.dts
index eaaa1b407425..085419b7e108 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon-sim.dts
+++ b/arch/arm/boot/dts/qcom/msmfalcon-sim.dts
@@ -27,3 +27,29 @@
pinctrl-names = "default";
pinctrl-0 = <&uart_console_active>;
};
+
+&sdhc_1 {
+ /* device core power supply */
+ vdd-supply = <&pmfalcon_l4b>;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <200 570000>;
+
+ /* device communication power supply */
+ vdd-io-supply = <&pmfalcon_l8a>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <200 325000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on &sdc1_rclk_on>;
+ pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>;
+
+ qcom,clk-rates = <400000 20000000 25000000 50000000 192000000
+ 384000000>;
+
+ qcom,nonremovable;
+ qcom,bus-speed-mode = "HS400_1p8v", "HS200_1p8v", "DDR_1p8v";
+
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi
index e5db2766c553..e93067e3697c 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi
+++ b/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi
@@ -195,4 +195,27 @@
interrupt-controller;
#interrupt-cells = <2>;
};
+
+ /* ssr - inbound entry from turing */
+ smp2pgpio_ssr_smp2p_5_in: qcom,smp2pgpio-ssr-smp2p-5-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "slave-kernel";
+ qcom,remote-pid = <5>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ /* ssr - outbound entry to turing */
+ smp2pgpio_ssr_smp2p_5_out: qcom,smp2pgpio-ssr-smp2p-5-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "master-kernel";
+ qcom,remote-pid = <5>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
};
diff --git a/arch/arm/boot/dts/qcom/msmfalcon.dtsi b/arch/arm/boot/dts/qcom/msmfalcon.dtsi
index 0cc8a1420f3f..8e8c407734eb 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi
+++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi
@@ -26,6 +26,7 @@
aliases {
serial0 = &uartblsp1dm1;
+ sdhc1 = &sdhc_1; /* SDC1 eMMC slot */
};
chosen {
@@ -377,9 +378,8 @@
};
};
- clock_rpmcc: qcom,dummycc {
- compatible = "qcom,dummycc";
- clock-output-names = "rpmcc_clocks";
+ clock_rpmcc: qcom,rpmcc {
+ compatible = "qcom,rpmcc-msmfalcon", "qcom,rpmcc";
#clock-cells = <1>;
};
@@ -404,6 +404,26 @@
#reset-cells = <1>;
};
+ sdhc_1: sdhci@c0c4000 {
+ compatible = "qcom,sdhci-msm-v5";
+ reg = <0xc0c4000 0x1000>, <0xc0c5000 0x1000>;
+ reg-names = "hc_mem", "cmdq_mem";
+
+ interrupts = <0 129 0>, <0 227 0>;
+ interrupt-names = "hc_irq", "pwr_irq";
+
+ qcom,bus-width = <8>;
+ qcom,large-address-bus;
+
+ qcom,devfreq,freq-table = <50000000 200000000>;
+
+ clocks = <&clock_gcc GCC_SDCC1_AHB_CLK>,
+ <&clock_gcc GCC_SDCC1_APPS_CLK>;
+ clock-names = "iface_clk", "core_clk";
+
+ status = "disabled";
+ };
+
qcom,ipc-spinlock@1f40000 {
compatible = "qcom,ipc-spinlock-sfpb";
reg = <0x1f40000 0x8000>;
@@ -702,6 +722,38 @@
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>;
status = "ok";
};
+
+ qcom,turing@1a300000 {
+ compatible = "qcom,pil-tz-generic";
+ reg = <0x1a300000 0x00100>;
+ interrupts = <0 518 1>;
+
+ vdd_cx-supply = <&pmfalcon_s3b_level>;
+ qcom,proxy-reg-names = "vdd_cx";
+ qcom,vdd_cx-uV-uA = <RPM_SMD_REGULATOR_LEVEL_TURBO 100000>;
+
+ clocks = <&clock_rpmcc CXO_PIL_CDSP_CLK>;
+ clock-names = "xo";
+ qcom,proxy-clock-names = "xo";
+
+ qcom,pas-id = <18>;
+ qcom,proxy-timeout-ms = <10000>;
+ qcom,smem-id = <423>;
+ qcom,sysmon-id = <7>;
+ qcom,ssctl-instance-id = <0x17>;
+ qcom,firmware-name = "cdsp";
+ memory-region = <&cdsp_fw_mem>;
+
+ /* GPIO inputs from turing */
+ qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_5_in 0 0>;
+ qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_5_in 2 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_5_in 1 0>;
+ qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_5_in 3 0>;
+
+ /* GPIO output to turing*/
+ qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_5_out 0 0>;
+ status = "ok";
+ };
};
#include "msmfalcon-ion.dtsi"
diff --git a/arch/arm/boot/dts/qcom/msmtriton.dtsi b/arch/arm/boot/dts/qcom/msmtriton.dtsi
index 0e58fc9ec2e9..083c14af7839 100644
--- a/arch/arm/boot/dts/qcom/msmtriton.dtsi
+++ b/arch/arm/boot/dts/qcom/msmtriton.dtsi
@@ -177,6 +177,14 @@
reg = <0x0 0x92a00000 0x0 0x1e00000>;
};
+ venus_fw_mem: venus_fw_region {
+ compatible = "shared-dma-pool";
+ alloc-ranges = <0x0 0x80000000 0x0 0x20000000>;
+ reusable;
+ alignment = <0x0 0x400000>;
+ size = <0x0 0x800000>;
+ };
+
adsp_mem: adsp_region {
compatible = "shared-dma-pool";
alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>;
@@ -326,9 +334,8 @@
};
};
- clock_rpmcc: qcom,dummycc {
- compatible = "qcom,dummycc";
- clock-output-names = "rpmcc_clocks";
+ clock_rpmcc: qcom,rpmcc {
+ compatible = "qcom,rpmcc-msmfalcon", "qcom,rpmcc";
#clock-cells = <1>;
};
@@ -573,6 +580,35 @@
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>;
status = "ok";
};
+
+ qcom,venus@cce0000 {
+ compatible = "qcom,pil-tz-generic";
+ reg = <0xcce0000 0x4000>;
+
+ vdd-supply = <&gdsc_venus>;
+ qcom,proxy-reg-names = "vdd";
+
+ clocks = <&clock_mmss MMSS_VIDEO_CORE_CLK>,
+ <&clock_mmss MMSS_VIDEO_AHB_CLK>,
+ <&clock_mmss MMSS_VIDEO_AXI_CLK>;
+ clock-names = "core_clk","iface_clk",
+ "bus_clk";
+ qcom,proxy-clock-names = "core_clk",
+ "iface_clk","bus_clk";
+
+ qcom,msm-bus,name = "pil-venus";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <63 512 0 0>,
+ <63 512 0 304000>;
+
+ qcom,pas-id = <9>;
+ qcom,proxy-timeout-ms = <100>;
+ qcom,firmware-name = "venus";
+ memory-region = <&venus_fw_mem>;
+ status = "ok";
+ };
};
#include "msmtriton-ion.dtsi"
diff --git a/arch/arm/configs/msmfalcon_defconfig b/arch/arm/configs/msmfalcon_defconfig
index 41572d54afcb..069603eefe48 100644
--- a/arch/arm/configs/msmfalcon_defconfig
+++ b/arch/arm/configs/msmfalcon_defconfig
@@ -336,7 +336,6 @@ CONFIG_FB_MSM=y
CONFIG_FB_MSM_MDSS=y
CONFIG_FB_MSM_MDSS_WRITEBACK=y
CONFIG_FB_MSM_MDSS_HDMI_PANEL=y
-CONFIG_FB_MSM_MDSS_DP_PANEL=y
CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index 799e43f09a11..0bda100dfb5a 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -321,6 +321,7 @@ CONFIG_QPNP_SMB2=y
CONFIG_SMB138X_CHARGER=y
CONFIG_QPNP_QNOVO=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_CPU_THERMAL=y
CONFIG_LIMITS_MONITOR=y
CONFIG_LIMITS_LITE_HW=y
CONFIG_THERMAL_MONITOR=y
@@ -535,6 +536,7 @@ CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_QSEE_IPC_IRQ_BRIDGE=y
CONFIG_QCOM_SMCINVOKE=y
+CONFIG_QCOM_EARLY_RANDOM=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
CONFIG_QCOM_BIMC_BWMON=y
CONFIG_ARM_MEMLAT_MON=y
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index dfd658d815fe..3568fe4ed29f 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -324,6 +324,7 @@ CONFIG_QPNP_SMB2=y
CONFIG_SMB138X_CHARGER=y
CONFIG_QPNP_QNOVO=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_CPU_THERMAL=y
CONFIG_LIMITS_MONITOR=y
CONFIG_LIMITS_LITE_HW=y
CONFIG_THERMAL_MONITOR=y
@@ -554,6 +555,7 @@ CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_QSEE_IPC_IRQ_BRIDGE=y
CONFIG_QCOM_SMCINVOKE=y
+CONFIG_QCOM_EARLY_RANDOM=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
CONFIG_QCOM_BIMC_BWMON=y
CONFIG_ARM_MEMLAT_MON=y
diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c
index d29fd60719c9..a119c0b27321 100644
--- a/drivers/clk/msm/clock-osm.c
+++ b/drivers/clk/msm/clock-osm.c
@@ -1776,7 +1776,7 @@ static void clk_osm_setup_fsms(struct clk_osm *c)
val = clk_osm_read_reg(c,
DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG);
- val |= BVAL(31, 16, clk_osm_count_ns(c, 500));
+ val |= BVAL(31, 16, clk_osm_count_ns(c, 250));
clk_osm_write_reg(c, val,
DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG);
}
@@ -1793,7 +1793,7 @@ static void clk_osm_setup_fsms(struct clk_osm *c)
val = clk_osm_read_reg(c,
DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG);
- val |= BVAL(15, 0, clk_osm_count_ns(c, 15000));
+ val |= BVAL(15, 0, clk_osm_count_ns(c, 250));
clk_osm_write_reg(c, val,
DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG);
}
@@ -1807,7 +1807,7 @@ static void clk_osm_setup_fsms(struct clk_osm *c)
if (c->wfx_fsm_en || c->ps_fsm_en || c->droop_fsm_en) {
clk_osm_write_reg(c, 0x1, DROOP_PROG_SYNC_DELAY_REG);
- clk_osm_write_reg(c, clk_osm_count_ns(c, 500),
+ clk_osm_write_reg(c, clk_osm_count_ns(c, 5),
DROOP_RELEASE_TIMER_CTRL);
clk_osm_write_reg(c, clk_osm_count_ns(c, 500),
DCVS_DROOP_TIMER_CTRL);
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 933a208392bd..6d12ddb3e245 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -935,6 +935,8 @@ static int clk_gfx3d_src_set_rate_and_parent(struct clk_hw *hw,
}
const struct clk_ops clk_gfx3d_src_ops = {
+ .enable = clk_rcg2_enable,
+ .disable = clk_rcg2_disable,
.is_enabled = clk_rcg2_is_enabled,
.get_parent = clk_rcg2_get_parent,
.set_parent = clk_rcg2_set_parent,
diff --git a/drivers/clk/qcom/gpucc-msmfalcon.c b/drivers/clk/qcom/gpucc-msmfalcon.c
index a2127e2629c7..f194abb471cd 100644
--- a/drivers/clk/qcom/gpucc-msmfalcon.c
+++ b/drivers/clk/qcom/gpucc-msmfalcon.c
@@ -84,12 +84,12 @@ static struct pll_vco gpu_vco[] = {
{ 250000000, 500000000, 3 },
};
-/* 640MHz configuration */
+/* 800MHz configuration */
static const struct pll_config gpu_pll0_config = {
- .l = 0x21,
+ .l = 0x29,
.config_ctl_val = 0x4001055b,
- .alpha = 0x55555600,
- .alpha_u = 0x55,
+ .alpha = 0xaaaaab00,
+ .alpha_u = 0xaa,
.alpha_en_mask = BIT(24),
.vco_val = 0x2 << 20,
.vco_mask = 0x3 << 20,
diff --git a/drivers/devfreq/governor_bw_vbif.c b/drivers/devfreq/governor_bw_vbif.c
index da1eefb8c94e..33e144b653d0 100644
--- a/drivers/devfreq/governor_bw_vbif.c
+++ b/drivers/devfreq/governor_bw_vbif.c
@@ -78,15 +78,13 @@ static int devfreq_vbif_ev_handler(struct devfreq *devfreq,
case DEVFREQ_GOV_START:
mutex_lock(&df_lock);
df = devfreq;
- if (df->profile->get_dev_status)
- ret = df->profile->get_dev_status(df->dev.parent,
- &stat);
+ if (df->profile->get_dev_status &&
+ !df->profile->get_dev_status(df->dev.parent, &stat) &&
+ stat.private_data)
+ dev_ab = stat.private_data;
else
- ret = 0;
- if (ret || !stat.private_data)
pr_warn("Device doesn't take AB votes!\n");
- else
- dev_ab = stat.private_data;
+
mutex_unlock(&df_lock);
ret = devfreq_vbif_update_bw(0, 0);
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 94d828027f20..9940f7a7c2b7 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1961,7 +1961,7 @@ static int adreno_setproperty(struct kgsl_device_private *dev_priv,
KGSL_STATE_ACTIVE);
device->pwrctrl.ctrl_flags = KGSL_PWR_ON;
adreno_fault_detect_stop(adreno_dev);
- kgsl_pwrscale_disable(device);
+ kgsl_pwrscale_disable(device, true);
}
mutex_unlock(&device->mutex);
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index d71c6a63f2d3..172de7406c26 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -1387,6 +1387,47 @@ done:
return 0;
}
+static ssize_t kgsl_pwrctrl_pwrscale_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ int ret;
+ unsigned int enable = 0;
+
+ if (device == NULL)
+ return 0;
+
+ ret = kgsl_sysfs_store(buf, &enable);
+ if (ret)
+ return ret;
+
+ mutex_lock(&device->mutex);
+
+ if (enable)
+ kgsl_pwrscale_enable(device);
+ else
+ kgsl_pwrscale_disable(device, false);
+
+ mutex_unlock(&device->mutex);
+
+ return count;
+}
+
+static ssize_t kgsl_pwrctrl_pwrscale_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ struct kgsl_pwrscale *psc;
+
+ if (device == NULL)
+ return 0;
+ psc = &device->pwrscale;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", psc->enabled);
+}
+
static DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show,
kgsl_pwrctrl_gpuclk_store);
static DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show,
@@ -1449,6 +1490,9 @@ static DEVICE_ATTR(clock_mhz, 0444, kgsl_pwrctrl_clock_mhz_show, NULL);
static DEVICE_ATTR(freq_table_mhz, 0444,
kgsl_pwrctrl_freq_table_mhz_show, NULL);
static DEVICE_ATTR(temp, 0444, kgsl_pwrctrl_temp_show, NULL);
+static DEVICE_ATTR(pwrscale, 0644,
+ kgsl_pwrctrl_pwrscale_show,
+ kgsl_pwrctrl_pwrscale_store);
static const struct device_attribute *pwrctrl_attr_list[] = {
&dev_attr_gpuclk,
@@ -1477,6 +1521,7 @@ static const struct device_attribute *pwrctrl_attr_list[] = {
&dev_attr_clock_mhz,
&dev_attr_freq_table_mhz,
&dev_attr_temp,
+ &dev_attr_pwrscale,
NULL
};
diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
index 01d3b74c16fd..85cd29b5364e 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.c
+++ b/drivers/gpu/msm/kgsl_pwrscale.c
@@ -189,19 +189,21 @@ EXPORT_SYMBOL(kgsl_pwrscale_update);
/*
* kgsl_pwrscale_disable - temporarily disable the governor
* @device: The device
+ * @turbo: Indicates if pwrlevel should be forced to turbo
*
* Temporarily disable the governor, to prevent interference
* with profiling tools that expect a fixed clock frequency.
* This function must be called with the device mutex locked.
*/
-void kgsl_pwrscale_disable(struct kgsl_device *device)
+void kgsl_pwrscale_disable(struct kgsl_device *device, bool turbo)
{
BUG_ON(!mutex_is_locked(&device->mutex));
if (device->pwrscale.devfreqptr)
queue_work(device->pwrscale.devfreq_wq,
&device->pwrscale.devfreq_suspend_ws);
device->pwrscale.enabled = false;
- kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO);
+ if (turbo)
+ kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO);
}
EXPORT_SYMBOL(kgsl_pwrscale_disable);
diff --git a/drivers/gpu/msm/kgsl_pwrscale.h b/drivers/gpu/msm/kgsl_pwrscale.h
index c85317869f1d..0756a4490f22 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.h
+++ b/drivers/gpu/msm/kgsl_pwrscale.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2016, 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
@@ -123,7 +123,7 @@ void kgsl_pwrscale_sleep(struct kgsl_device *device);
void kgsl_pwrscale_wake(struct kgsl_device *device);
void kgsl_pwrscale_enable(struct kgsl_device *device);
-void kgsl_pwrscale_disable(struct kgsl_device *device);
+void kgsl_pwrscale_disable(struct kgsl_device *device, bool turbo);
int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags);
int kgsl_devfreq_get_dev_status(struct device *, struct devfreq_dev_status *);
diff --git a/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c
index ac4caa48312d..fa5e4cca129d 100644
--- a/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c
+++ b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c
@@ -12,6 +12,7 @@
#include <linux/irq.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
#include "pixart_ots.h"
struct pixart_pat9125_data {
@@ -22,12 +23,18 @@ struct pixart_pat9125_data {
bool press_en;
bool inverse_x;
bool inverse_y;
+ struct regulator *vdd;
+ struct regulator *vld;
struct pinctrl *pinctrl;
struct pinctrl_state *pinctrl_state_active;
struct pinctrl_state *pinctrl_state_suspend;
struct pinctrl_state *pinctrl_state_release;
};
+/* Declaration of suspend and resume functions */
+static int pat9125_suspend(struct device *dev);
+static int pat9125_resume(struct device *dev);
+
static int pat9125_i2c_write(struct i2c_client *client, u8 reg, u8 *data,
int len)
{
@@ -143,6 +150,27 @@ static irqreturn_t pat9125_irq(int irq, void *dev_data)
return IRQ_HANDLED;
}
+static ssize_t pat9125_suspend_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct pixart_pat9125_data *data =
+ (struct pixart_pat9125_data *) dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int mode;
+
+ if (kstrtoint(buf, 10, &mode)) {
+ dev_err(dev, "failed to read input for sysfs\n");
+ return -EINVAL;
+ }
+
+ if (mode == 1)
+ pat9125_suspend(&client->dev);
+ else if (mode == 0)
+ pat9125_resume(&client->dev);
+
+ return count;
+}
+
static ssize_t pat9125_test_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
@@ -150,7 +178,7 @@ static ssize_t pat9125_test_store(struct device *dev,
int reg_data = 0, i;
long rd_addr, wr_addr, wr_data;
struct pixart_pat9125_data *data =
- (struct pixart_pat9125_data *)dev->driver_data;
+ (struct pixart_pat9125_data *) dev_get_drvdata(dev);
struct i2c_client *client = data->client;
for (i = 0; i < sizeof(s); i++)
@@ -188,11 +216,15 @@ static ssize_t pat9125_test_show(struct device *dev,
{
return 0;
}
+
+static DEVICE_ATTR(suspend, S_IRUGO | S_IWUSR | S_IWGRP,
+ NULL, pat9125_suspend_store);
static DEVICE_ATTR(test, S_IRUGO | S_IWUSR | S_IWGRP,
pat9125_test_show, pat9125_test_store);
static struct attribute *pat9125_attr_list[] = {
&dev_attr_test.attr,
+ &dev_attr_suspend.attr,
NULL,
};
@@ -238,6 +270,98 @@ static int pixart_pinctrl_init(struct pixart_pat9125_data *data)
return 0;
}
+static int pat9125_regulator_init(struct pixart_pat9125_data *data)
+{
+ int err = 0;
+ struct device *dev = &data->client->dev;
+
+ data->vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(data->vdd)) {
+ dev_err(dev, "Failed to get regulator vdd %ld\n",
+ PTR_ERR(data->vdd));
+ return PTR_ERR(data->vdd);
+ }
+
+ data->vld = devm_regulator_get(dev, "vld");
+ if (IS_ERR(data->vld)) {
+ dev_err(dev, "Failed to get regulator vld %ld\n",
+ PTR_ERR(data->vld));
+ return PTR_ERR(data->vld);
+ }
+
+ err = regulator_set_voltage(data->vdd, VDD_VTG_MIN_UV, VDD_VTG_MAX_UV);
+ if (err) {
+ dev_err(dev, "Failed to set voltage for vdd reg %d\n", err);
+ return err;
+ }
+
+ err = regulator_set_load(data->vdd, VDD_ACTIVE_LOAD_UA);
+ if (err < 0) {
+ dev_err(dev, "Failed to set opt mode for vdd reg %d\n", err);
+ return err;
+ }
+
+ err = regulator_set_voltage(data->vld, VLD_VTG_MIN_UV, VLD_VTG_MAX_UV);
+ if (err) {
+ dev_err(dev, "Failed to set voltage for vld reg %d\n", err);
+ return err;
+ }
+
+ err = regulator_set_load(data->vld, VLD_ACTIVE_LOAD_UA);
+ if (err < 0) {
+ dev_err(dev, "Failed to set opt mode for vld reg %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int pat9125_power_on(struct pixart_pat9125_data *data, bool on)
+{
+ int err = 0;
+ struct device *dev = &data->client->dev;
+
+ if (on) {
+ err = regulator_enable(data->vdd);
+ if (err) {
+ dev_err(dev, "Failed to enable vdd reg %d\n", err);
+ return err;
+ }
+
+ usleep_range(DELAY_BETWEEN_REG_US, DELAY_BETWEEN_REG_US + 1);
+
+ /*
+ * Initialize pixart sensor after some delay, when vdd
+ * regulator is enabled
+ */
+ if (!ots_sensor_init(data->client)) {
+ err = -ENODEV;
+ dev_err(dev, "Failed to initialize sensor %d\n", err);
+ return err;
+ }
+
+ err = regulator_enable(data->vld);
+ if (err) {
+ dev_err(dev, "Failed to enable vld reg %d\n", err);
+ return err;
+ }
+ } else {
+ err = regulator_disable(data->vld);
+ if (err) {
+ dev_err(dev, "Failed to disable vld reg %d\n", err);
+ return err;
+ }
+
+ err = regulator_disable(data->vdd);
+ if (err) {
+ dev_err(dev, "Failed to disable vdd reg %d\n", err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
static int pat9125_parse_dt(struct device *dev,
struct pixart_pat9125_data *data)
{
@@ -351,9 +475,16 @@ static int pat9125_i2c_probe(struct i2c_client *client,
}
}
- if (!ots_sensor_init(client)) {
- err = -ENODEV;
- goto err_sensor_init;
+ err = pat9125_regulator_init(data);
+ if (err) {
+ dev_err(dev, "Failed to init regulator, %d\n", err);
+ return err;
+ }
+
+ err = pat9125_power_on(data, true);
+ if (err) {
+ dev_err(dev, "Failed to power-on the sensor %d\n", err);
+ goto err_power_on;
}
err = devm_request_threaded_irq(dev, client->irq, NULL, pat9125_irq,
@@ -374,7 +505,11 @@ static int pat9125_i2c_probe(struct i2c_client *client,
err_sysfs_create:
err_request_threaded_irq:
-err_sensor_init:
+err_power_on:
+ regulator_set_load(data->vdd, 0);
+ regulator_set_load(data->vld, 0);
+ if (pat9125_power_on(data, false) < 0)
+ dev_err(dev, "Failed to disable regulators\n");
if (data->pinctrl)
if (pinctrl_select_state(data->pinctrl,
data->pinctrl_state_release) < 0)
@@ -388,10 +523,14 @@ static int pat9125_i2c_remove(struct i2c_client *client)
struct pixart_pat9125_data *data = i2c_get_clientdata(client);
struct device *dev = &data->client->dev;
+ sysfs_remove_group(&(data->input->dev.kobj), &pat9125_attr_grp);
if (data->pinctrl)
if (pinctrl_select_state(data->pinctrl,
data->pinctrl_state_release) < 0)
dev_err(dev, "Couldn't set pin to release state\n");
+ regulator_set_load(data->vdd, 0);
+ regulator_set_load(data->vld, 0);
+ pat9125_power_on(data, false);
return 0;
}
@@ -399,7 +538,7 @@ static int pat9125_suspend(struct device *dev)
{
int rc;
struct pixart_pat9125_data *data =
- (struct pixart_pat9125_data *)dev->driver_data;
+ (struct pixart_pat9125_data *) dev_get_drvdata(dev);
disable_irq(data->client->irq);
if (data->pinctrl) {
@@ -410,6 +549,12 @@ static int pat9125_suspend(struct device *dev)
rc);
}
+ rc = pat9125_power_on(data, false);
+ if (rc) {
+ dev_err(dev, "Failed to disable regulators %d\n", rc);
+ return rc;
+ }
+
return 0;
}
@@ -417,7 +562,7 @@ static int pat9125_resume(struct device *dev)
{
int rc;
struct pixart_pat9125_data *data =
- (struct pixart_pat9125_data *)dev->driver_data;
+ (struct pixart_pat9125_data *) dev_get_drvdata(dev);
if (data->pinctrl) {
rc = pinctrl_select_state(data->pinctrl,
@@ -426,9 +571,26 @@ static int pat9125_resume(struct device *dev)
dev_err(dev, "Could not set pin to active state %d\n",
rc);
}
+
+ rc = pat9125_power_on(data, true);
+ if (rc) {
+ dev_err(dev, "Failed to power-on the sensor %d\n", rc);
+ goto err_sensor_init;
+ }
+
enable_irq(data->client->irq);
return 0;
+
+err_sensor_init:
+ if (data->pinctrl)
+ if (pinctrl_select_state(data->pinctrl,
+ data->pinctrl_state_suspend) < 0)
+ dev_err(dev, "Couldn't set pin to suspend state\n");
+ if (pat9125_power_on(data, false) < 0)
+ dev_err(dev, "Failed to disable regulators\n");
+
+ return rc;
}
static const struct i2c_device_id pat9125_device_id[] = {
diff --git a/drivers/input/misc/ots_pat9125/pixart_ots.c b/drivers/input/misc/ots_pat9125/pixart_ots.c
index fa73ffe40985..3d44d068423a 100644
--- a/drivers/input/misc/ots_pat9125/pixart_ots.c
+++ b/drivers/input/misc/ots_pat9125/pixart_ots.c
@@ -19,7 +19,8 @@ static void ots_write_read(struct i2c_client *client, u8 address, u8 wdata)
bool ots_sensor_init(struct i2c_client *client)
{
- unsigned char sensor_pid = 0, read_id_ok = 0;
+ u8 sensor_pid = 0;
+ bool read_id_ok = false;
/*
* Read sensor_pid in address 0x00 to check if the
@@ -28,7 +29,7 @@ bool ots_sensor_init(struct i2c_client *client)
sensor_pid = read_data(client, PIXART_PAT9125_PRODUCT_ID1_REG);
if (sensor_pid == PIXART_PAT9125_SENSOR_ID) {
- read_id_ok = 1;
+ read_id_ok = true;
/*
* PAT9125 sensor recommended settings:
diff --git a/drivers/input/misc/ots_pat9125/pixart_ots.h b/drivers/input/misc/ots_pat9125/pixart_ots.h
index 824d6bafd9bf..5320d588d341 100644
--- a/drivers/input/misc/ots_pat9125/pixart_ots.h
+++ b/drivers/input/misc/ots_pat9125/pixart_ots.h
@@ -13,6 +13,13 @@
#define PINCTRL_STATE_ACTIVE "pmx_rot_switch_active"
#define PINCTRL_STATE_SUSPEND "pmx_rot_switch_suspend"
#define PINCTRL_STATE_RELEASE "pmx_rot_switch_release"
+#define VDD_VTG_MIN_UV 1800000
+#define VDD_VTG_MAX_UV 1800000
+#define VDD_ACTIVE_LOAD_UA 10000
+#define VLD_VTG_MIN_UV 2800000
+#define VLD_VTG_MAX_UV 3300000
+#define VLD_ACTIVE_LOAD_UA 10000
+#define DELAY_BETWEEN_REG_US 20000
/* Register addresses */
#define PIXART_PAT9125_PRODUCT_ID1_REG 0x00
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.c b/drivers/input/touchscreen/gt9xx/gt9xx.c
index a9d7666a6d6f..3b19a45922c4 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx.c
+++ b/drivers/input/touchscreen/gt9xx/gt9xx.c
@@ -115,6 +115,8 @@ struct i2c_client *i2c_connect_client;
#define GTP_DEBUGFS_DIR "ts_debug"
#define GTP_DEBUGFS_FILE_SUSPEND "suspend"
+#define GTP_DEBUGFS_FILE_DATA "data"
+#define GTP_DEBUGFS_FILE_ADDR "addr"
/*******************************************************
Function:
@@ -1521,12 +1523,108 @@ static ssize_t gtp_fw_name_store(struct device *dev,
return size;
}
+static ssize_t gtp_fw_upgrade_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct goodix_ts_data *ts = dev_get_drvdata(dev);
+
+ return snprintf(buf, 2, "%d\n", ts->fw_loading);
+}
+
+static ssize_t gtp_fw_upgrade_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct goodix_ts_data *ts = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ if (size > 2)
+ return -EINVAL;
+
+ ret = kstrtouint(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (ts->gtp_is_suspend) {
+ dev_err(&ts->client->dev,
+ "Can't start fw upgrade. Device is in suspend state");
+ return -EBUSY;
+ }
+
+ mutex_lock(&ts->input_dev->mutex);
+ if (!ts->fw_loading && val) {
+ disable_irq(ts->client->irq);
+ ts->fw_loading = true;
+ if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) {
+ ret = gup_update_proc(NULL);
+ if (ret == FAIL)
+ dev_err(&ts->client->dev,
+ "Fail to update GTP firmware\n");
+ }
+ ts->fw_loading = false;
+ enable_irq(ts->client->irq);
+ }
+ mutex_unlock(&ts->input_dev->mutex);
+
+ return size;
+}
+
+static ssize_t gtp_force_fw_upgrade_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct goodix_ts_data *ts = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ if (size > 2)
+ return -EINVAL;
+
+ ret = kstrtouint(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (ts->gtp_is_suspend) {
+ dev_err(&ts->client->dev,
+ "Can't start fw upgrade. Device is in suspend state.");
+ return -EBUSY;
+ }
+
+ mutex_lock(&ts->input_dev->mutex);
+ if (!ts->fw_loading && val) {
+ disable_irq(ts->client->irq);
+ ts->fw_loading = true;
+ ts->force_update = true;
+ if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) {
+ ret = gup_update_proc(NULL);
+ if (ret == FAIL)
+ dev_err(&ts->client->dev,
+ "Fail to force update GTP firmware.\n");
+ }
+ ts->force_update = false;
+ ts->fw_loading = false;
+ enable_irq(ts->client->irq);
+ }
+ mutex_unlock(&ts->input_dev->mutex);
+
+ return size;
+}
+
static DEVICE_ATTR(fw_name, (S_IRUGO | S_IWUSR | S_IWGRP),
gtp_fw_name_show,
gtp_fw_name_store);
+static DEVICE_ATTR(fw_upgrade, (S_IRUGO | S_IWUSR | S_IWGRP),
+ gtp_fw_upgrade_show,
+ gtp_fw_upgrade_store);
+static DEVICE_ATTR(force_fw_upgrade, (S_IRUGO | S_IWUSR | S_IWGRP),
+ gtp_fw_upgrade_show,
+ gtp_force_fw_upgrade_store);
static struct attribute *gtp_attrs[] = {
&dev_attr_fw_name.attr,
+ &dev_attr_fw_upgrade.attr,
+ &dev_attr_force_fw_upgrade.attr,
NULL
};
@@ -1534,6 +1632,84 @@ static const struct attribute_group gtp_attr_grp = {
.attrs = gtp_attrs,
};
+static int gtp_debug_addr_is_valid(u16 addr)
+{
+ if (addr < GTP_VALID_ADDR_START || addr > GTP_VALID_ADDR_END) {
+ pr_err("GTP reg address is invalid: 0x%x\n", addr);
+ return false;
+ }
+
+ return true;
+}
+
+static int gtp_debug_data_set(void *_data, u64 val)
+{
+ struct goodix_ts_data *ts = _data;
+
+ mutex_lock(&ts->input_dev->mutex);
+ if (gtp_debug_addr_is_valid(ts->addr))
+ dev_err(&ts->client->dev,
+ "Writing to GTP registers not supported\n");
+ mutex_unlock(&ts->input_dev->mutex);
+
+ return 0;
+}
+
+static int gtp_debug_data_get(void *_data, u64 *val)
+{
+ struct goodix_ts_data *ts = _data;
+ int ret;
+ u8 buf[3] = {0};
+
+ mutex_lock(&ts->input_dev->mutex);
+ buf[0] = ts->addr >> 8;
+ buf[1] = ts->addr & 0x00ff;
+
+ if (gtp_debug_addr_is_valid(ts->addr)) {
+ ret = gtp_i2c_read(ts->client, buf, 3);
+ if (ret < 0)
+ dev_err(&ts->client->dev,
+ "GTP read register 0x%x failed (%d)\n",
+ ts->addr, ret);
+ else
+ *val = buf[2];
+ }
+ mutex_unlock(&ts->input_dev->mutex);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, gtp_debug_data_get,
+ gtp_debug_data_set, "%llx\n");
+
+static int gtp_debug_addr_set(void *_data, u64 val)
+{
+ struct goodix_ts_data *ts = _data;
+
+ if (gtp_debug_addr_is_valid(val)) {
+ mutex_lock(&ts->input_dev->mutex);
+ ts->addr = val;
+ mutex_unlock(&ts->input_dev->mutex);
+ }
+
+ return 0;
+}
+
+static int gtp_debug_addr_get(void *_data, u64 *val)
+{
+ struct goodix_ts_data *ts = _data;
+
+ mutex_lock(&ts->input_dev->mutex);
+ if (gtp_debug_addr_is_valid(ts->addr))
+ *val = ts->addr;
+ mutex_unlock(&ts->input_dev->mutex);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, gtp_debug_addr_get,
+ gtp_debug_addr_set, "%llx\n");
+
static int gtp_debug_suspend_set(void *_data, u64 val)
{
struct goodix_ts_data *ts = _data;
@@ -1567,7 +1743,7 @@ static int gtp_debugfs_init(struct goodix_ts_data *data)
data->debug_base = debugfs_create_dir(GTP_DEBUGFS_DIR, NULL);
if (IS_ERR_OR_NULL(data->debug_base)) {
- pr_err("Failed to create debugfs dir\n");
+ dev_err(&data->client->dev, "Failed to create debugfs dir\n");
return -EINVAL;
}
@@ -1576,7 +1752,27 @@ static int gtp_debugfs_init(struct goodix_ts_data *data)
data->debug_base,
data,
&debug_suspend_fops)))) {
- pr_err("Failed to create suspend file\n");
+ dev_err(&data->client->dev, "Failed to create suspend file\n");
+ debugfs_remove_recursive(data->debug_base);
+ return -EINVAL;
+ }
+
+ if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_DATA,
+ S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP,
+ data->debug_base,
+ data,
+ &debug_data_fops)))) {
+ dev_err(&data->client->dev, "Failed to create data file\n");
+ debugfs_remove_recursive(data->debug_base);
+ return -EINVAL;
+ }
+
+ if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_ADDR,
+ S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP,
+ data->debug_base,
+ data,
+ &debug_addr_fops)))) {
+ dev_err(&data->client->dev, "Failed to create addr file\n");
debugfs_remove_recursive(data->debug_base);
return -EINVAL;
}
@@ -1645,8 +1841,8 @@ static int goodix_parse_dt(struct device *dev,
pdata->i2c_pull_up = of_property_read_bool(np,
"goodix,i2c-pull-up");
- pdata->no_force_update = of_property_read_bool(np,
- "goodix,no-force-update");
+ pdata->force_update = of_property_read_bool(np,
+ "goodix,force-update");
pdata->enable_power_off = of_property_read_bool(np,
"goodix,enable-power-off");
@@ -1761,9 +1957,7 @@ static int goodix_ts_probe(struct i2c_client *client,
return -EINVAL;
}
-#if GTP_ESD_PROTECT
i2c_connect_client = client;
-#endif
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "GTP I2C not supported\n");
@@ -1811,22 +2005,24 @@ static int goodix_ts_probe(struct i2c_client *client,
goto exit_power_off;
}
+ if (pdata->force_update)
+ ts->force_update = true;
+
if (pdata->fw_name)
strlcpy(ts->fw_name, pdata->fw_name,
strlen(pdata->fw_name) + 1);
-#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
- ret = gup_init_update_proc(ts);
- if (ret < 0) {
- dev_err(&client->dev,
- "GTP Create firmware update thread error.\n");
- goto exit_power_off;
+ if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) {
+ ret = gup_init_update_proc(ts);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "GTP Create firmware update thread error\n");
+ goto exit_power_off;
+ }
}
-#endif
-
ret = gtp_init_panel(ts);
if (ret < 0) {
- dev_err(&client->dev, "GTP init panel failed.\n");
+ dev_err(&client->dev, "GTP init panel failed\n");
ts->abs_x_max = GTP_MAX_WIDTH;
ts->abs_y_max = GTP_MAX_HEIGHT;
ts->int_trigger_type = GTP_INT_TRIGGER;
@@ -1834,7 +2030,7 @@ static int goodix_ts_probe(struct i2c_client *client,
ret = gtp_request_input_dev(ts);
if (ret) {
- dev_err(&client->dev, "GTP request input dev failed.\n");
+ dev_err(&client->dev, "GTP request input dev failed\n");
goto exit_free_inputdev;
}
input_set_drvdata(ts->input_dev, ts);
@@ -2017,6 +2213,14 @@ static int goodix_ts_suspend(struct device *dev)
}
mutex_lock(&ts->lock);
+
+ if (ts->fw_loading) {
+ dev_info(&ts->client->dev,
+ "Fw upgrade in progress, can't go to suspend.");
+ mutex_unlock(&ts->lock);
+ return 0;
+ }
+
#if GTP_ESD_PROTECT
gtp_esd_switch(ts->client, SWITCH_OFF);
#endif
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.h b/drivers/input/touchscreen/gt9xx/gt9xx.h
index 38487eea7b10..779a0ddd93f8 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx.h
+++ b/drivers/input/touchscreen/gt9xx/gt9xx.h
@@ -53,7 +53,7 @@ struct goodix_ts_platform_data {
u32 panel_miny;
u32 panel_maxx;
u32 panel_maxy;
- bool no_force_update;
+ bool force_update;
bool i2c_pull_up;
bool enable_power_off;
size_t config_data_len[GOODIX_MAX_CFG_GROUP];
@@ -74,6 +74,7 @@ struct goodix_ts_data {
s32 use_irq;
u16 abs_x_max;
u16 abs_y_max;
+ u16 addr;
u8 max_touch_num;
u8 int_trigger_type;
u8 green_wake_mode;
@@ -88,6 +89,8 @@ struct goodix_ts_data {
u8 fw_error;
bool power_on;
struct mutex lock;
+ bool fw_loading;
+ bool force_update;
struct regulator *avdd;
struct regulator *vdd;
struct regulator *vcc_i2c;
@@ -172,6 +175,8 @@ extern u16 total_len;
/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
#define GTP_I2C_ADDRESS_HIGH 0x14
#define GTP_I2C_ADDRESS_LOW 0x5D
+#define GTP_VALID_ADDR_START 0x8040
+#define GTP_VALID_ADDR_END 0x8177
/* GTP CM_HEAD RW flags */
#define GTP_RW_READ 0
@@ -210,11 +215,9 @@ s32 init_wr_node(struct i2c_client *client);
void uninit_wr_node(void);
#endif
-#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
-extern u8 gup_init_update_proc(struct goodix_ts_data *ts);
+u8 gup_init_update_proc(struct goodix_ts_data *ts);
s32 gup_enter_update_mode(struct i2c_client *client);
void gup_leave_update_mode(struct i2c_client *client);
s32 gup_update_proc(void *dir);
extern struct i2c_client *i2c_connect_client;
-#endif
#endif /* _GOODIX_GT9XX_H_ */
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
index 4660b27d156c..a91256c576e3 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx_update.c
+++ b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
@@ -1408,10 +1408,15 @@ s32 gup_update_proc(void *dir)
goto file_fail;
}
- ret = gup_enter_update_judge(ts->client, &fw_head);
- if (ret == FAIL) {
- pr_err("Check *.bin file fail");
- goto file_fail;
+ if (ts->force_update) {
+ dev_dbg(&ts->client->dev, "Enter force update.");
+ } else {
+ ret = gup_enter_update_judge(ts->client, &fw_head);
+ if (ret == FAIL) {
+ dev_err(&ts->client->dev,
+ "Check *.bin file fail.");
+ goto file_fail;
+ }
}
ts->enter_update = 1;
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index afa519aa8203..702706ae60f7 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -2938,7 +2938,7 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev)
*/
group = generic_device_group(dev);
- if (IS_ERR(group))
+ if (IS_ERR_OR_NULL(group))
return group;
if (dev_is_pci(dev))
diff --git a/drivers/media/dvb-core/demux.h b/drivers/media/dvb-core/demux.h
index cd02396abbe7..227dbfa97d64 100644
--- a/drivers/media/dvb-core/demux.h
+++ b/drivers/media/dvb-core/demux.h
@@ -407,8 +407,7 @@ typedef int (*dmx_ts_cb)(const u8 *buffer1,
size_t buffer1_length,
const u8 *buffer2,
size_t buffer2_length,
- struct dmx_ts_feed *source,
- enum dmx_success success);
+ struct dmx_ts_feed *source);
/**
* typedef dmx_section_cb - DVB demux TS filter callback function prototype
@@ -449,8 +448,7 @@ typedef int (*dmx_section_cb)(const u8 *buffer1,
size_t buffer1_len,
const u8 *buffer2,
size_t buffer2_len,
- struct dmx_section_filter *source,
- enum dmx_success success);
+ struct dmx_section_filter *source);
typedef int (*dmx_ts_fullness) (
struct dmx_ts_feed *source,
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index 63becfd57eaa..a9c4237d631a 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -2603,15 +2603,15 @@ static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
const u8 *buffer2, size_t buffer2_len,
- struct dmx_section_filter *filter,
- enum dmx_success success)
+ struct dmx_section_filter *filter)
{
struct dmxdev_filter *dmxdevfilter = filter->priv;
struct dmx_filter_event event;
ssize_t free;
+
if (!dmxdevfilter) {
- pr_err("%s: null filter. status=%d\n", __func__, success);
+ pr_err("%s: null filter.\n", __func__);
return -EINVAL;
}
@@ -2633,7 +2633,7 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
}
if ((buffer1_len + buffer2_len) == 0) {
- if (success == DMX_CRC_ERROR) {
+ if (buffer1 == NULL && buffer2 == NULL) {
/* Section was dropped due to CRC error */
event.type = DMX_EVENT_SECTION_CRC_ERROR;
dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
@@ -2671,11 +2671,6 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
event.params.section.actual_length =
event.params.section.total_length;
- if (success == DMX_MISSED_ERROR)
- event.params.section.flags = DMX_FILTER_CC_ERROR;
- else
- event.params.section.flags = 0;
-
dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
if (dmxdevfilter->params.sec.flags & DMX_ONESHOT)
@@ -2687,8 +2682,7 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
const u8 *buffer2, size_t buffer2_len,
- struct dmx_ts_feed *feed,
- enum dmx_success success)
+ struct dmx_ts_feed *feed)
{
struct dmxdev_filter *dmxdevfilter = feed->priv;
struct dvb_ringbuffer *buffer;
@@ -2697,11 +2691,10 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
ssize_t free;
if (!dmxdevfilter) {
- pr_err("%s: null filter (feed->is_filtering=%d) status=%d\n",
- __func__, feed->is_filtering, success);
+ pr_err("%s: null filter (feed->is_filtering=%d)\n",
+ __func__, feed->is_filtering);
return -EINVAL;
}
-
spin_lock(&dmxdevfilter->dev->lock);
if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER ||
@@ -2725,36 +2718,8 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
return buffer->error;
}
- if (dmxdevfilter->params.pes.output == DMX_OUT_TAP) {
- if (success == DMX_OK && !events->current_event_data_size) {
- events->current_event_start_offset = buffer->pwrite;
- } else if (success == DMX_OK_PES_END) {
- event.type = DMX_EVENT_NEW_PES;
-
- event.params.pes.actual_length =
- events->current_event_data_size;
- event.params.pes.total_length =
- events->current_event_data_size;
-
- event.params.pes.base_offset =
- events->current_event_start_offset;
- event.params.pes.start_offset =
- events->current_event_start_offset;
-
- event.params.pes.flags = 0;
- event.params.pes.stc = 0;
- event.params.pes.transport_error_indicator_counter = 0;
- event.params.pes.continuity_error_counter = 0;
- event.params.pes.ts_packets_num = 0;
-
- /* Do not report zero length PES */
- if (event.params.pes.total_length)
- dvb_dmxdev_add_event(events, &event);
- events->current_event_data_size = 0;
- }
- } else if (!events->current_event_data_size) {
+ if (!events->current_event_data_size)
events->current_event_start_offset = buffer->pwrite;
- }
/* Verify output buffer has sufficient space, or report overflow */
free = dvb_ringbuffer_free(buffer);
diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c
index d45bcc55b76a..7809770bd1ae 100644
--- a/drivers/media/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb-core/dvb_demux.c
@@ -550,7 +550,7 @@ static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed,
if (feed->pusi_seen == 0)
return 0;
- ret = feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK);
+ ret = feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts);
/* Verify TS packet was copied successfully */
if (!ret) {
@@ -582,7 +582,7 @@ static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed,
return 0;
return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen,
- NULL, 0, &f->filter, DMX_OK);
+ NULL, 0, &f->filter);
}
static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)
@@ -613,7 +613,7 @@ static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)
/* Notify on CRC error */
feed->cb.sec(NULL, 0, NULL, 0,
- &f->filter, DMX_CRC_ERROR);
+ &f->filter);
return -1;
}
@@ -1256,9 +1256,9 @@ static inline void dvb_dmx_swfilter_output_packet(
*/
if (feed->tsp_out_format == DMX_TSP_FORMAT_192_HEAD)
feed->cb.ts(timestamp, TIMESTAMP_LEN, NULL,
- 0, &feed->feed.ts, DMX_OK);
+ 0, &feed->feed.ts);
- feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+ feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts);
/*
* if we output 192 packet with timestamp at tail of packet,
@@ -1266,7 +1266,7 @@ static inline void dvb_dmx_swfilter_output_packet(
*/
if (feed->tsp_out_format == DMX_TSP_FORMAT_192_TAIL)
feed->cb.ts(timestamp, TIMESTAMP_LEN, NULL,
- 0, &feed->feed.ts, DMX_OK);
+ 0, &feed->feed.ts);
if (feed->idx_params.enable)
dvb_dmx_index(feed, buf, timestamp);
@@ -1749,7 +1749,7 @@ void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count)
{
spin_lock(&demux->lock);
- demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts, DMX_OK);
+ demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts);
spin_unlock(&demux->lock);
}
@@ -2520,7 +2520,7 @@ static int dvbdmx_ts_insertion_insert_buffer(struct dmx_ts_feed *ts_feed,
return 0;
}
- feed->cb.ts(data, size, NULL, 0, ts_feed, DMX_OK);
+ feed->cb.ts(data, size, NULL, 0, ts_feed);
spin_unlock(&demux->lock);
diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c
index 454584a8bf17..ce4332e80a91 100644
--- a/drivers/media/dvb-core/dvb_net.c
+++ b/drivers/media/dvb-core/dvb_net.c
@@ -761,8 +761,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
static int dvb_net_ts_callback(const u8 *buffer1, size_t buffer1_len,
const u8 *buffer2, size_t buffer2_len,
- struct dmx_ts_feed *feed,
- enum dmx_success success)
+ struct dmx_ts_feed *feed)
{
struct net_device *dev = feed->priv;
@@ -871,8 +870,7 @@ static void dvb_net_sec(struct net_device *dev,
static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len,
const u8 *buffer2, size_t buffer2_len,
- struct dmx_section_filter *filter,
- enum dmx_success success)
+ struct dmx_section_filter *filter)
{
struct net_device *dev = filter->priv;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
index a2aa2983b056..98b29cc3a9e3 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -1943,7 +1943,7 @@ static void msm_vfe40_stats_cfg_wm_reg(
stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
stats_base = VFE40_STATS_BASE(stats_idx);
/*WR_ADDR_CFG*/
- msm_camera_io_w(stream_info->framedrop_period << 2,
+ msm_camera_io_w((stream_info->framedrop_period - 1) << 2,
vfe_dev->vfe_base + stats_base + 0x8);
/*WR_IRQ_FRAMEDROP_PATTERN*/
msm_camera_io_w(stream_info->framedrop_pattern,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
index c77eff66ccca..0e14e0957a2a 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
@@ -1590,7 +1590,7 @@ static void msm_vfe44_stats_cfg_wm_reg(
if (stats_idx == STATS_IDX_BF_SCALE)
return;
/*WR_ADDR_CFG*/
- msm_camera_io_w(stream_info->framedrop_period << 2,
+ msm_camera_io_w((stream_info->framedrop_period - 1) << 2,
vfe_dev->vfe_base + stats_base + 0x8);
/*WR_IRQ_FRAMEDROP_PATTERN*/
msm_camera_io_w(stream_info->framedrop_pattern,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
index 6336892b1b4e..5237b84e9477 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
@@ -1680,7 +1680,7 @@ static void msm_vfe46_stats_cfg_wm_reg(
return;
/* WR_ADDR_CFG */
- msm_camera_io_w(stream_info->framedrop_period << 2,
+ msm_camera_io_w((stream_info->framedrop_period - 1) << 2,
vfe_dev->vfe_base + stats_base + 0x8);
/* WR_IRQ_FRAMEDROP_PATTERN */
msm_camera_io_w(stream_info->framedrop_pattern,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
index f65963c964b3..df7e2a88e7ca 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
@@ -2062,7 +2062,7 @@ void msm_vfe47_stats_cfg_wm_reg(
stats_base = VFE47_STATS_BASE(stats_idx);
/* WR_ADDR_CFG */
- msm_camera_io_w(stream_info->framedrop_period << 2,
+ msm_camera_io_w((stream_info->framedrop_period - 1) << 2,
vfe_dev->vfe_base + stats_base + 0x10);
/* WR_IRQ_FRAMEDROP_PATTERN */
msm_camera_io_w(stream_info->framedrop_pattern,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
index e226f7e40a07..afa498f80928 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
@@ -1263,7 +1263,7 @@ int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg)
stream_info->framedrop_pattern = 0x0;
else
stream_info->framedrop_pattern = 0x1;
- stream_info->framedrop_period = framedrop_period - 1;
+ stream_info->framedrop_period = framedrop_period;
if (stream_info->init_stats_frame_drop == 0)
for (k = 0; k < stream_info->num_isp; k++)
stream_info->vfe_dev[k]->hw_info->
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
index d1e3c090c972..fb8f0c4bae37 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
@@ -4186,7 +4186,7 @@ static int mpq_sdmx_section_filtering(struct mpq_feed *mpq_feed,
mpq_feed->sdmx_buf.size) {
feed->cb.sec(&mpq_feed->sdmx_buf.data[mpq_feed->sdmx_buf.pread],
header->payload_length,
- NULL, 0, &f->filter, DMX_OK);
+ NULL, 0, &f->filter);
} else {
int split = mpq_feed->sdmx_buf.size - mpq_feed->sdmx_buf.pread;
@@ -4194,7 +4194,7 @@ static int mpq_sdmx_section_filtering(struct mpq_feed *mpq_feed,
split,
&mpq_feed->sdmx_buf.data[0],
header->payload_length - split,
- &f->filter, DMX_OK);
+ &f->filter);
}
return 0;
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index 352defe6204b..df3901093006 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/msm_gsi.h>
#include <linux/platform_device.h>
+#include <linux/delay.h>
#include "gsi.h"
#include "gsi_reg.h"
@@ -26,6 +27,8 @@
#define GSI_MHI_ER_START 10
#define GSI_MHI_ER_END 16
+#define GSI_RESET_WA_MIN_SLEEP 1000
+#define GSI_RESET_WA_MAX_SLEEP 2000
static const struct of_device_id msm_gsi_match[] = {
{ .compatible = "qcom,msm_gsi", },
{ },
@@ -105,6 +108,11 @@ static void gsi_handle_ch_ctrl(int ee)
GSIDBG("ch %x\n", ch);
for (i = 0; i < 32; i++) {
if ((1 << i) & ch) {
+ if (i >= gsi_ctx->max_ch || i >= GSI_CHAN_MAX) {
+ GSIERR("invalid channel %d\n", i);
+ break;
+ }
+
ctx = &gsi_ctx->chan[i];
val = gsi_readl(gsi_ctx->base +
GSI_EE_n_GSI_CH_k_CNTXT_0_OFFS(i, ee));
@@ -113,6 +121,7 @@ static void gsi_handle_ch_ctrl(int ee)
GSI_EE_n_GSI_CH_k_CNTXT_0_CHSTATE_SHFT;
GSIDBG("ch %u state updated to %u\n", i, ctx->state);
complete(&ctx->compl);
+ gsi_ctx->ch_dbg[i].cmd_completed++;
}
}
@@ -132,6 +141,11 @@ static void gsi_handle_ev_ctrl(int ee)
GSIDBG("ev %x\n", ch);
for (i = 0; i < 32; i++) {
if ((1 << i) & ch) {
+ if (i >= gsi_ctx->max_ev || i >= GSI_EVT_RING_MAX) {
+ GSIERR("invalid event %d\n", i);
+ break;
+ }
+
ctx = &gsi_ctx->evtr[i];
val = gsi_readl(gsi_ctx->base +
GSI_EE_n_EV_CH_k_CNTXT_0_OFFS(i, ee));
@@ -1602,6 +1616,7 @@ int gsi_alloc_channel(struct gsi_chan_props *props, unsigned long dev_hdl,
ctx->props = *props;
mutex_lock(&gsi_ctx->mlock);
+ gsi_ctx->ch_dbg[props->ch_id].ch_allocate++;
val = (((props->ch_id << GSI_EE_n_GSI_CH_CMD_CHID_SHFT) &
GSI_EE_n_GSI_CH_CMD_CHID_BMSK) |
((op << GSI_EE_n_GSI_CH_CMD_OPCODE_SHFT) &
@@ -1772,6 +1787,7 @@ int gsi_start_channel(unsigned long chan_hdl)
mutex_lock(&gsi_ctx->mlock);
init_completion(&ctx->compl);
+ gsi_ctx->ch_dbg[chan_hdl].ch_start++;
val = (((chan_hdl << GSI_EE_n_GSI_CH_CMD_CHID_SHFT) &
GSI_EE_n_GSI_CH_CMD_CHID_BMSK) |
((op << GSI_EE_n_GSI_CH_CMD_OPCODE_SHFT) &
@@ -1829,6 +1845,7 @@ int gsi_stop_channel(unsigned long chan_hdl)
mutex_lock(&gsi_ctx->mlock);
init_completion(&ctx->compl);
+ gsi_ctx->ch_dbg[chan_hdl].ch_stop++;
val = (((chan_hdl << GSI_EE_n_GSI_CH_CMD_CHID_SHFT) &
GSI_EE_n_GSI_CH_CMD_CHID_BMSK) |
((op << GSI_EE_n_GSI_CH_CMD_OPCODE_SHFT) &
@@ -1897,6 +1914,7 @@ int gsi_stop_db_channel(unsigned long chan_hdl)
mutex_lock(&gsi_ctx->mlock);
init_completion(&ctx->compl);
+ gsi_ctx->ch_dbg[chan_hdl].ch_db_stop++;
val = (((chan_hdl << GSI_EE_n_GSI_CH_CMD_CHID_SHFT) &
GSI_EE_n_GSI_CH_CMD_CHID_BMSK) |
((op << GSI_EE_n_GSI_CH_CMD_OPCODE_SHFT) &
@@ -1961,6 +1979,7 @@ int gsi_reset_channel(unsigned long chan_hdl)
reset:
init_completion(&ctx->compl);
+ gsi_ctx->ch_dbg[chan_hdl].ch_reset++;
val = (((chan_hdl << GSI_EE_n_GSI_CH_CMD_CHID_SHFT) &
GSI_EE_n_GSI_CH_CMD_CHID_BMSK) |
((op << GSI_EE_n_GSI_CH_CMD_OPCODE_SHFT) &
@@ -1982,6 +2001,7 @@ reset:
/* workaround: reset GSI producers again */
if (ctx->props.dir == GSI_CHAN_DIR_FROM_GSI && !reset_done) {
+ usleep_range(GSI_RESET_WA_MIN_SLEEP, GSI_RESET_WA_MAX_SLEEP);
reset_done = true;
goto reset;
}
@@ -2026,6 +2046,7 @@ int gsi_dealloc_channel(unsigned long chan_hdl)
mutex_lock(&gsi_ctx->mlock);
init_completion(&ctx->compl);
+ gsi_ctx->ch_dbg[chan_hdl].ch_de_alloc++;
val = (((chan_hdl << GSI_EE_n_GSI_CH_CMD_CHID_SHFT) &
GSI_EE_n_GSI_CH_CMD_CHID_BMSK) |
((op << GSI_EE_n_GSI_CH_CMD_OPCODE_SHFT) &
diff --git a/drivers/platform/msm/gsi/gsi.h b/drivers/platform/msm/gsi/gsi.h
index 0b94ed2d3a92..750ae2b329d3 100644
--- a/drivers/platform/msm/gsi/gsi.h
+++ b/drivers/platform/msm/gsi/gsi.h
@@ -125,12 +125,23 @@ struct gsi_ee_scratch {
uint32_t word1;
};
+struct ch_debug_stats {
+ unsigned long ch_allocate;
+ unsigned long ch_start;
+ unsigned long ch_stop;
+ unsigned long ch_reset;
+ unsigned long ch_de_alloc;
+ unsigned long ch_db_stop;
+ unsigned long cmd_completed;
+};
+
struct gsi_ctx {
void __iomem *base;
struct device *dev;
struct gsi_per_props per;
bool per_registered;
struct gsi_chan_ctx chan[GSI_CHAN_MAX];
+ struct ch_debug_stats ch_dbg[GSI_CHAN_MAX];
struct gsi_evt_ctx evtr[GSI_EVT_RING_MAX];
struct mutex mlock;
spinlock_t slock;
diff --git a/drivers/platform/msm/ipa/ipa_rm.c b/drivers/platform/msm/ipa/ipa_rm.c
index 209264d69b26..bcdd99deae1f 100644
--- a/drivers/platform/msm/ipa/ipa_rm.c
+++ b/drivers/platform/msm/ipa/ipa_rm.c
@@ -820,7 +820,8 @@ static void ipa_rm_wq_resume_handler(struct work_struct *work)
}
ipa_rm_resource_consumer_request_work(
(struct ipa_rm_resource_cons *)resource,
- ipa_rm_work->prev_state, ipa_rm_work->needed_bw, true);
+ ipa_rm_work->prev_state, ipa_rm_work->needed_bw, true,
+ ipa_rm_work->inc_usage_count);
spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
bail:
kfree(ipa_rm_work);
@@ -916,7 +917,8 @@ int ipa_rm_wq_send_suspend_cmd(enum ipa_rm_resource_name resource_name,
int ipa_rm_wq_send_resume_cmd(enum ipa_rm_resource_name resource_name,
enum ipa_rm_resource_state prev_state,
- u32 needed_bw)
+ u32 needed_bw,
+ bool inc_usage_count)
{
int result = -ENOMEM;
struct ipa_rm_wq_suspend_resume_work_type *work = kzalloc(sizeof(*work),
@@ -926,6 +928,7 @@ int ipa_rm_wq_send_resume_cmd(enum ipa_rm_resource_name resource_name,
work->resource_name = resource_name;
work->prev_state = prev_state;
work->needed_bw = needed_bw;
+ work->inc_usage_count = inc_usage_count;
result = queue_work(ipa_rm_ctx->ipa_rm_wq,
(struct work_struct *)work);
} else {
diff --git a/drivers/platform/msm/ipa/ipa_rm_i.h b/drivers/platform/msm/ipa/ipa_rm_i.h
index eb86c54d7382..1610bb1e1ead 100644
--- a/drivers/platform/msm/ipa/ipa_rm_i.h
+++ b/drivers/platform/msm/ipa/ipa_rm_i.h
@@ -118,6 +118,7 @@ struct ipa_rm_wq_suspend_resume_work_type {
enum ipa_rm_resource_name resource_name;
enum ipa_rm_resource_state prev_state;
u32 needed_bw;
+ bool inc_usage_count;
};
@@ -128,7 +129,8 @@ int ipa_rm_wq_send_cmd(enum ipa_rm_wq_cmd wq_cmd,
int ipa_rm_wq_send_resume_cmd(enum ipa_rm_resource_name resource_name,
enum ipa_rm_resource_state prev_state,
- u32 needed_bw);
+ u32 needed_bw,
+ bool inc_usage_count);
int ipa_rm_wq_send_suspend_cmd(enum ipa_rm_resource_name resource_name,
enum ipa_rm_resource_state prev_state,
diff --git a/drivers/platform/msm/ipa/ipa_rm_resource.c b/drivers/platform/msm/ipa/ipa_rm_resource.c
index 0a3f66307eee..da4490ce0aa0 100644
--- a/drivers/platform/msm/ipa/ipa_rm_resource.c
+++ b/drivers/platform/msm/ipa/ipa_rm_resource.c
@@ -116,7 +116,8 @@ bail:
int ipa_rm_resource_consumer_request_work(struct ipa_rm_resource_cons *consumer,
enum ipa_rm_resource_state prev_state,
u32 prod_needed_bw,
- bool notify_completion)
+ bool notify_completion,
+ bool dec_client_on_err)
{
int driver_result;
@@ -135,7 +136,8 @@ int ipa_rm_resource_consumer_request_work(struct ipa_rm_resource_cons *consumer,
} else if (driver_result != -EINPROGRESS) {
consumer->resource.state = prev_state;
consumer->resource.needed_bw -= prod_needed_bw;
- consumer->usage_count--;
+ if (dec_client_on_err)
+ consumer->usage_count--;
}
return driver_result;
@@ -170,19 +172,22 @@ int ipa_rm_resource_consumer_request(
ipa_rm_resource_str(consumer->resource.name));
ipa_rm_wq_send_resume_cmd(consumer->resource.name,
prev_state,
- prod_needed_bw);
+ prod_needed_bw,
+ inc_usage_count);
result = -EINPROGRESS;
break;
}
result = ipa_rm_resource_consumer_request_work(consumer,
prev_state,
prod_needed_bw,
- false);
+ false,
+ inc_usage_count);
break;
case IPA_RM_GRANTED:
if (wake_client) {
result = ipa_rm_resource_consumer_request_work(
- consumer, prev_state, prod_needed_bw, false);
+ consumer, prev_state, prod_needed_bw, false,
+ inc_usage_count);
break;
}
ipa_rm_perf_profile_change(consumer->resource.name);
diff --git a/drivers/platform/msm/ipa/ipa_rm_resource.h b/drivers/platform/msm/ipa/ipa_rm_resource.h
index 5c3a0190753f..da149c51c96c 100644
--- a/drivers/platform/msm/ipa/ipa_rm_resource.h
+++ b/drivers/platform/msm/ipa/ipa_rm_resource.h
@@ -155,7 +155,8 @@ int ipa_rm_resource_producer_print_stat(
int ipa_rm_resource_consumer_request_work(struct ipa_rm_resource_cons *consumer,
enum ipa_rm_resource_state prev_state,
u32 needed_bw,
- bool notify_completion);
+ bool notify_completion,
+ bool dec_client_on_err);
int ipa_rm_resource_consumer_release_work(
struct ipa_rm_resource_cons *consumer,
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 94e8bba1fe01..09c7c1b0fd05 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -778,10 +778,28 @@ static void ipa3_transport_irq_cmd_ack(void *user1, int user2)
*/
int ipa3_send_cmd(u16 num_desc, struct ipa3_desc *descr)
{
+ return ipa3_send_cmd_timeout(num_desc, descr, 0);
+}
+
+/**
+ * ipa3_send_cmd_timeout - send immediate commands with limited time
+ * waiting for ACK from IPA HW
+ * @num_desc: number of descriptors within the desc struct
+ * @descr: descriptor structure
+ * @timeout: millisecond to wait till get ACK from IPA HW
+ *
+ * Function will block till command gets ACK from IPA HW or timeout.
+ * Caller needs to free any resources it allocated after function returns
+ * The callback in ipa3_desc should not be set by the caller
+ * for this function.
+ */
+int ipa3_send_cmd_timeout(u16 num_desc, struct ipa3_desc *descr, u32 timeout)
+{
struct ipa3_desc *desc;
int i, result = 0;
struct ipa3_sys_context *sys;
int ep_idx;
+ int completed;
for (i = 0; i < num_desc; i++)
IPADBG("sending imm cmd %d\n", descr[i].opcode);
@@ -808,7 +826,14 @@ int ipa3_send_cmd(u16 num_desc, struct ipa3_desc *descr)
result = -EFAULT;
goto bail;
}
- wait_for_completion(&descr->xfer_done);
+ if (timeout) {
+ completed = wait_for_completion_timeout(
+ &descr->xfer_done, msecs_to_jiffies(timeout));
+ if (!completed)
+ IPADBG("timeout waiting for imm-cmd ACK\n");
+ } else {
+ wait_for_completion(&descr->xfer_done);
+ }
} else {
desc = &descr[num_desc - 1];
init_completion(&desc->xfer_done);
@@ -823,7 +848,15 @@ int ipa3_send_cmd(u16 num_desc, struct ipa3_desc *descr)
result = -EFAULT;
goto bail;
}
- wait_for_completion(&desc->xfer_done);
+ if (timeout) {
+ completed = wait_for_completion_timeout(
+ &desc->xfer_done, msecs_to_jiffies(timeout));
+ if (!completed)
+ IPADBG("timeout waiting for imm-cmd ACK\n");
+ } else {
+ wait_for_completion(&desc->xfer_done);
+ }
+
}
bail:
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 8e85822d9719..33be22f98b9d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -1835,6 +1835,7 @@ int ipa3_init_mem_partition(struct device_node *dev_node);
int ipa3_controller_static_bind(struct ipa3_controller *controller,
enum ipa_hw_type ipa_hw_type);
int ipa3_cfg_route(struct ipahal_reg_route *route);
+int ipa3_send_cmd_timeout(u16 num_desc, struct ipa3_desc *descr, u32 timeout);
int ipa3_send_cmd(u16 num_desc, struct ipa3_desc *descr);
int ipa3_cfg_filter(u32 disable);
int ipa3_pipe_mem_init(u32 start_ofst, u32 size);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index a21eb9c1530b..c0a6e8b00d71 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -47,6 +47,8 @@
#define IPA_EOT_COAL_GRAN_MIN (1)
#define IPA_EOT_COAL_GRAN_MAX (16)
+#define IPA_DMA_TASK_FOR_GSI_TIMEOUT_MSEC (15)
+
#define IPA_AGGR_BYTE_LIMIT (\
IPA_ENDP_INIT_AGGR_N_AGGR_BYTE_LIMIT_BMSK >> \
IPA_ENDP_INIT_AGGR_N_AGGR_BYTE_LIMIT_SHFT)
@@ -101,7 +103,7 @@
#define IPA_GROUP_DPL IPA_GROUP_DL
#define IPA_GROUP_DIAG (2)
#define IPA_GROUP_DMA (3)
-#define IPA_GROUP_IMM_CMD IPA_GROUP_DMA
+#define IPA_GROUP_IMM_CMD IPA_GROUP_UL
#define IPA_GROUP_Q6ZIP (4)
#define IPA_GROUP_Q6ZIP_GENERAL IPA_GROUP_Q6ZIP
#define IPA_GROUP_UC_RX_Q (5)
@@ -3470,7 +3472,8 @@ int ipa3_inject_dma_task_for_gsi(void)
desc.type = IPA_IMM_CMD_DESC;
IPADBG("sending 1B packet to IPA\n");
- if (ipa3_send_cmd(1, &desc)) {
+ if (ipa3_send_cmd_timeout(1, &desc,
+ IPA_DMA_TASK_FOR_GSI_TIMEOUT_MSEC)) {
IPAERR("ipa3_send_cmd failed\n");
return -EFAULT;
}
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index ea2694c8c58d..2718ea93bd45 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -275,6 +275,7 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(charger_temp_max),
POWER_SUPPLY_ATTR(parallel_disable),
POWER_SUPPLY_ATTR(parallel_percent),
+ POWER_SUPPLY_ATTR(pe_start),
/* Local extensions of type int64_t */
POWER_SUPPLY_ATTR(charge_counter_ext),
/* Properties of type `const char *' */
diff --git a/drivers/power/qcom-charger/bcl_peripheral.c b/drivers/power/qcom-charger/bcl_peripheral.c
index fc958b160f86..8a7012ac2bef 100644
--- a/drivers/power/qcom-charger/bcl_peripheral.c
+++ b/drivers/power/qcom-charger/bcl_peripheral.c
@@ -459,7 +459,7 @@ static void bcl_lmh_dcvs_enable(void)
desc_arg.arginfo = SCM_ARGS(5, SCM_RO, SCM_VAL, SCM_VAL,
SCM_VAL, SCM_VAL);
- dmac_flush_range(payload, payload + 5 * (sizeof(uint32_t)));
+ dmac_flush_range(payload, (void *)payload + 5 * (sizeof(uint32_t)));
if (scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, LMH_DCVSH),
&desc_arg))
pr_err("Error enabling LMH BCL monitoringfor cluster0\n");
diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h
index adc640c7afe1..a703b208f6e4 100644
--- a/drivers/power/qcom-charger/fg-core.h
+++ b/drivers/power/qcom-charger/fg-core.h
@@ -118,7 +118,14 @@ enum {
DELTA_SOC_IRQ_WA = BIT(0),
};
-/* SRAM parameters */
+/*
+ * List of FG_SRAM parameters. Please add a parameter only if it is an entry
+ * that will be used either to configure an entity (e.g. termination current)
+ * which might need some encoding (or) it is an entry that will be read from
+ * SRAM and decoded (e.g. CC_SOC_SW) for SW to use at various places. For
+ * generic read/writes to SRAM registers, please use fg_sram_read/write APIs
+ * directly without adding an entry here.
+ */
enum fg_sram_param_id {
FG_SRAM_BATT_SOC = 0,
FG_SRAM_VOLTAGE_PRED,
diff --git a/drivers/power/qcom-charger/fg-util.c b/drivers/power/qcom-charger/fg-util.c
index bbdbe48896d7..0e3c7dbb5731 100644
--- a/drivers/power/qcom-charger/fg-util.c
+++ b/drivers/power/qcom-charger/fg-util.c
@@ -621,6 +621,17 @@ static ssize_t fg_sram_dfs_reg_write(struct file *file, const char __user *buf,
/* Parse the data in the buffer. It should be a string of numbers */
while ((pos < count) &&
sscanf(kbuf + pos, "%i%n", &data, &bytes_read) == 1) {
+ /*
+ * We shouldn't be receiving a string of characters that
+ * exceeds a size of 5 to keep this functionally correct.
+ * Also, we should make sure that pos never gets overflowed
+ * beyond the limit.
+ */
+ if (bytes_read > 5 || bytes_read > INT_MAX - pos) {
+ cnt = 0;
+ ret = -EINVAL;
+ break;
+ }
pos += bytes_read;
values[cnt++] = data & 0xff;
}
diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c
index 30408218b7e7..3d3f95c2376c 100644
--- a/drivers/power/qcom-charger/qpnp-fg-gen3.c
+++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c
@@ -64,6 +64,8 @@
#define PROFILE_LOAD_OFFSET 0
#define NOM_CAP_WORD 58
#define NOM_CAP_OFFSET 0
+#define ACT_BATT_CAP_BKUP_WORD 74
+#define ACT_BATT_CAP_BKUP_OFFSET 0
#define CYCLE_COUNT_WORD 75
#define CYCLE_COUNT_OFFSET 0
#define PROFILE_INTEGRITY_WORD 79
@@ -156,8 +158,8 @@ static struct fg_sram_param pmicobalt_v1_sram_params[] = {
fg_decode_cc_soc),
PARAM(CC_SOC_SW, CC_SOC_SW_WORD, CC_SOC_SW_OFFSET, 4, 1, 1, 0, NULL,
fg_decode_cc_soc),
- PARAM(ACT_BATT_CAP, ACT_BATT_CAP_WORD, ACT_BATT_CAP_OFFSET, 2, 1, 1, 0,
- NULL, fg_decode_default),
+ PARAM(ACT_BATT_CAP, ACT_BATT_CAP_BKUP_WORD, ACT_BATT_CAP_BKUP_OFFSET, 2,
+ 1, 1, 0, NULL, fg_decode_default),
/* Entries below here are configurable during initialization */
PARAM(CUTOFF_VOLT, CUTOFF_VOLT_WORD, CUTOFF_VOLT_OFFSET, 2, 1000000,
244141, 0, fg_encode_voltage, NULL),
@@ -208,8 +210,8 @@ static struct fg_sram_param pmicobalt_v2_sram_params[] = {
fg_decode_cc_soc),
PARAM(CC_SOC_SW, CC_SOC_SW_WORD, CC_SOC_SW_OFFSET, 4, 1, 1, 0, NULL,
fg_decode_cc_soc),
- PARAM(ACT_BATT_CAP, ACT_BATT_CAP_WORD, ACT_BATT_CAP_OFFSET, 2, 1, 1, 0,
- NULL, fg_decode_default),
+ PARAM(ACT_BATT_CAP, ACT_BATT_CAP_BKUP_WORD, ACT_BATT_CAP_BKUP_OFFSET, 2,
+ 1, 1, 0, NULL, fg_decode_default),
/* Entries below here are configurable during initialization */
PARAM(CUTOFF_VOLT, CUTOFF_VOLT_WORD, CUTOFF_VOLT_OFFSET, 2, 1000000,
244141, 0, fg_encode_voltage, NULL),
@@ -887,10 +889,20 @@ static int fg_save_learned_cap_to_sram(struct fg_chip *chip)
return -EPERM;
cc_mah = div64_s64(chip->cl.learned_cc_uah, 1000);
+ /* Write to a backup register to use across reboot */
rc = fg_sram_write(chip, chip->sp[FG_SRAM_ACT_BATT_CAP].addr_word,
chip->sp[FG_SRAM_ACT_BATT_CAP].addr_byte, (u8 *)&cc_mah,
chip->sp[FG_SRAM_ACT_BATT_CAP].len, FG_IMA_DEFAULT);
if (rc < 0) {
+ pr_err("Error in writing act_batt_cap_bkup, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Write to actual capacity register for coulomb counter operation */
+ rc = fg_sram_write(chip, ACT_BATT_CAP_WORD, ACT_BATT_CAP_OFFSET,
+ (u8 *)&cc_mah, chip->sp[FG_SRAM_ACT_BATT_CAP].len,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
pr_err("Error in writing act_batt_cap, rc=%d\n", rc);
return rc;
}
@@ -927,16 +939,14 @@ static int fg_load_learned_cap_from_sram(struct fg_chip *chip)
* the nominal capacity.
*/
if (chip->cl.nom_cap_uah && delta_cc_uah > pct_nom_cap_uah) {
- fg_dbg(chip, FG_CAP_LEARN, "learned_cc_uah: %lld is higher than expected\n",
- chip->cl.learned_cc_uah);
- fg_dbg(chip, FG_CAP_LEARN, "Capping it to nominal:%lld\n",
- chip->cl.nom_cap_uah);
+ fg_dbg(chip, FG_CAP_LEARN, "learned_cc_uah: %lld is higher than expected, capping it to nominal: %lld\n",
+ chip->cl.learned_cc_uah, chip->cl.nom_cap_uah);
chip->cl.learned_cc_uah = chip->cl.nom_cap_uah;
- rc = fg_save_learned_cap_to_sram(chip);
- if (rc < 0)
- pr_err("Error in saving learned_cc_uah, rc=%d\n",
- rc);
}
+
+ rc = fg_save_learned_cap_to_sram(chip);
+ if (rc < 0)
+ pr_err("Error in saving learned_cc_uah, rc=%d\n", rc);
}
fg_dbg(chip, FG_CAP_LEARN, "learned_cc_uah:%lld nom_cap_uah: %lld\n",
@@ -1644,6 +1654,37 @@ out:
return rc;
}
+static void fg_notify_charger(struct fg_chip *chip)
+{
+ union power_supply_propval prop = {0, };
+ int rc;
+
+ if (!is_charger_available(chip)) {
+ pr_warn("Charger not available yet?\n");
+ return;
+ }
+
+ prop.intval = chip->bp.float_volt_uv;
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop);
+ if (rc < 0) {
+ pr_err("Error in setting voltage_max property on batt_psy, rc=%d\n",
+ rc);
+ return;
+ }
+
+ prop.intval = chip->bp.fastchg_curr_ma * 1000;
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &prop);
+ if (rc < 0) {
+ pr_err("Error in setting constant_charge_current_max property on batt_psy, rc=%d\n",
+ rc);
+ return;
+ }
+
+ fg_dbg(chip, FG_STATUS, "Notified charger on float voltage and FCC\n");
+}
+
static void profile_load_work(struct work_struct *work)
{
struct fg_chip *chip = container_of(work,
@@ -1709,6 +1750,7 @@ done:
rc);
}
+ fg_notify_charger(chip);
chip->profile_loaded = true;
fg_dbg(chip, FG_STATUS, "profile loaded successfully");
out:
@@ -1798,6 +1840,7 @@ static int fg_psy_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
pval->intval = chip->bp.float_volt_uv;
+ break;
case POWER_SUPPLY_PROP_CYCLE_COUNT:
pval->intval = fg_get_cycle_count(chip);
break;
diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c
index 954a03f042f7..93965dbe99ae 100644
--- a/drivers/power/qcom-charger/qpnp-smb2.c
+++ b/drivers/power/qcom-charger/qpnp-smb2.c
@@ -233,11 +233,6 @@ module_param_named(
debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR
);
-static int __pl_master_percent = 50;
-module_param_named(
- pl_master_percent, __pl_master_percent, int, S_IRUSR | S_IWUSR
-);
-
#define MICRO_1P5A 1500000
static int smb2_parse_dt(struct smb2 *chip)
{
@@ -356,7 +351,7 @@ static enum power_supply_property smb2_usb_props[] = {
POWER_SUPPLY_PROP_PD_ACTIVE,
POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
POWER_SUPPLY_PROP_INPUT_CURRENT_NOW,
- POWER_SUPPLY_PROP_PARALLEL_DISABLE,
+ POWER_SUPPLY_PROP_PE_START,
};
static int smb2_usb_get_prop(struct power_supply *psy,
@@ -422,16 +417,15 @@ static int smb2_usb_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW:
rc = smblib_get_prop_usb_current_now(chg, val);
break;
- case POWER_SUPPLY_PROP_PARALLEL_DISABLE:
- val->intval = get_client_vote(chg->pl_disable_votable,
- USER_VOTER);
- break;
case POWER_SUPPLY_PROP_PD_IN_HARD_RESET:
rc = smblib_get_prop_pd_in_hard_reset(chg, val);
break;
case POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED:
val->intval = chg->system_suspend_supported;
break;
+ case POWER_SUPPLY_PROP_PE_START:
+ rc = smblib_get_pe_start(chg, val);
+ break;
default:
pr_err("get prop %d is not supported\n", psp);
rc = -EINVAL;
@@ -471,9 +465,6 @@ static int smb2_usb_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_PD_ACTIVE:
rc = smblib_set_prop_pd_active(chg, val);
break;
- case POWER_SUPPLY_PROP_PARALLEL_DISABLE:
- vote(chg->pl_disable_votable, USER_VOTER, (bool)val->intval, 0);
- break;
case POWER_SUPPLY_PROP_PD_IN_HARD_RESET:
rc = smblib_set_prop_pd_in_hard_reset(chg, val);
break;
@@ -494,7 +485,6 @@ static int smb2_usb_prop_is_writeable(struct power_supply *psy,
{
switch (psp) {
case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE:
- case POWER_SUPPLY_PROP_PARALLEL_DISABLE:
return 1;
default:
break;
@@ -647,12 +637,16 @@ static enum power_supply_property smb2_batt_props[] = {
POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED,
POWER_SUPPLY_PROP_STEP_CHARGING_STEP,
POWER_SUPPLY_PROP_CHARGE_DONE,
+ POWER_SUPPLY_PROP_PARALLEL_DISABLE,
+ POWER_SUPPLY_PROP_PARALLEL_PERCENT,
};
static int smb2_batt_get_prop(struct power_supply *psy,
@@ -692,6 +686,7 @@ static int smb2_batt_get_prop(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
rc = smblib_get_prop_input_current_limited(chg, val);
+ break;
case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED:
val->intval = chg->step_chg_enabled;
break;
@@ -701,9 +696,16 @@ static int smb2_batt_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
rc = smblib_get_prop_batt_voltage_now(chg, val);
break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ val->intval = get_client_vote(chg->fv_votable, DEFAULT_VOTER);
+ break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
rc = smblib_get_prop_batt_current_now(chg, val);
break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ val->intval = get_client_vote(chg->fcc_max_votable,
+ DEFAULT_VOTER);
+ break;
case POWER_SUPPLY_PROP_TEMP:
rc = smblib_get_prop_batt_temp(chg, val);
break;
@@ -713,6 +715,13 @@ static int smb2_batt_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_CHARGE_DONE:
rc = smblib_get_prop_batt_charge_done(chg, val);
break;
+ case POWER_SUPPLY_PROP_PARALLEL_DISABLE:
+ val->intval = get_client_vote(chg->pl_disable_votable,
+ USER_VOTER);
+ break;
+ case POWER_SUPPLY_PROP_PARALLEL_PERCENT:
+ val->intval = chg->pl.slave_pct;
+ break;
default:
pr_err("batt power supply prop %d not supported\n", psp);
return -EINVAL;
@@ -743,6 +752,21 @@ static int smb2_batt_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_CAPACITY:
rc = smblib_set_prop_batt_capacity(chg, val);
break;
+ case POWER_SUPPLY_PROP_PARALLEL_DISABLE:
+ vote(chg->pl_disable_votable, USER_VOTER, (bool)val->intval, 0);
+ break;
+ case POWER_SUPPLY_PROP_PARALLEL_PERCENT:
+ if (val->intval < 0 || val->intval > 100)
+ return -EINVAL;
+ chg->pl.slave_pct = val->intval;
+ rerun_election(chg->fcc_votable);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ vote(chg->fv_votable, DEFAULT_VOTER, true, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ vote(chg->fcc_max_votable, DEFAULT_VOTER, true, val->intval);
+ break;
default:
rc = -EINVAL;
}
@@ -757,6 +781,8 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy,
case POWER_SUPPLY_PROP_INPUT_SUSPEND:
case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
case POWER_SUPPLY_PROP_CAPACITY:
+ case POWER_SUPPLY_PROP_PARALLEL_DISABLE:
+ case POWER_SUPPLY_PROP_PARALLEL_PERCENT:
return 1;
default:
break;
@@ -1375,7 +1401,8 @@ static struct smb2_irq_info smb2_irqs[] = {
},
{
.name = "dcin-plugin",
- .handler = smblib_handle_debug,
+ .handler = smblib_handle_dc_plugin,
+ .wake = true,
},
{
.name = "div2-en-dg",
@@ -1585,7 +1612,6 @@ static int smb2_probe(struct platform_device *pdev)
chg->debug_mask = &__debug_mask;
chg->mode = PARALLEL_MASTER;
chg->name = "PMI";
- chg->pl.master_percent = &__pl_master_percent;
chg->regmap = dev_get_regmap(chg->dev->parent, NULL);
if (!chg->regmap) {
@@ -1654,6 +1680,7 @@ static int smb2_probe(struct platform_device *pdev)
return 0;
}
+ chg->pl.slave_pct = 50;
rc = smb2_init_batt_psy(chip);
if (rc < 0) {
pr_err("Couldn't initialize batt psy rc=%d\n", rc);
diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c
index c719773fae30..5785e42e0140 100644
--- a/drivers/power/qcom-charger/smb-lib.c
+++ b/drivers/power/qcom-charger/smb-lib.c
@@ -38,8 +38,8 @@
static bool is_secure(struct smb_charger *chg, int addr)
{
- /* assume everything above 0xC0 is secure */
- return (bool)((addr & 0xFF) >= 0xC0);
+ /* assume everything above 0xA0 is secure */
+ return (bool)((addr & 0xFF) >= 0xA0);
}
int smblib_read(struct smb_charger *chg, u16 addr, u8 *val)
@@ -91,15 +91,13 @@ unlock:
return rc;
}
-static int smblib_get_step_charging_adjustment(struct smb_charger *chg,
- int *cc_offset)
+static int smblib_get_step_cc_delta(struct smb_charger *chg, int *cc_delta_ua)
{
- int step_state;
- int rc;
+ int rc, step_state;
u8 stat;
if (!chg->step_chg_enabled) {
- *cc_offset = 0;
+ *cc_delta_ua = 0;
return 0;
}
@@ -113,57 +111,71 @@ static int smblib_get_step_charging_adjustment(struct smb_charger *chg,
step_state = (stat & STEP_CHARGING_STATUS_MASK) >>
STEP_CHARGING_STATUS_SHIFT;
rc = smblib_get_charge_param(chg, &chg->param.step_cc_delta[step_state],
- cc_offset);
+ cc_delta_ua);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get step cc delta rc=%d\n", rc);
+ return rc;
+ }
- return rc;
+ return 0;
}
-static void smblib_fcc_split_ua(struct smb_charger *chg, int total_fcc,
- int *master_ua, int *slave_ua)
+static int smblib_get_jeita_cc_delta(struct smb_charger *chg, int *cc_delta_ua)
{
- int rc, cc_reduction_ua = 0;
- int step_cc_delta;
- int master_percent = min(max(*chg->pl.master_percent, 0), 100);
- union power_supply_propval pval = {0, };
- int effective_fcc;
+ int rc, cc_minus_ua;
+ u8 stat;
- /*
- * if master_percent is 0, s/w will configure master's fcc to zero and
- * slave's fcc to the max. However since master's fcc is zero it
- * disables its own charging and as a result the slave's charging is
- * disabled via the fault line.
- */
+ rc = smblib_read(chg, BATTERY_CHARGER_STATUS_2_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n",
+ rc);
+ return rc;
+ }
- rc = smblib_get_prop_batt_health(chg, &pval);
- if (rc == 0) {
- if (pval.intval == POWER_SUPPLY_HEALTH_WARM
- || pval.intval == POWER_SUPPLY_HEALTH_COOL) {
- rc = smblib_get_charge_param(chg,
- &chg->param.jeita_cc_comp,
- &cc_reduction_ua);
- if (rc < 0) {
- smblib_err(chg, "Could not get jeita comp, rc=%d\n",
- rc);
- cc_reduction_ua = 0;
- }
- }
+ if (!(stat & BAT_TEMP_STATUS_SOFT_LIMIT_MASK)) {
+ *cc_delta_ua = 0;
+ return 0;
}
- rc = smblib_get_step_charging_adjustment(chg, &step_cc_delta);
- if (rc < 0)
- step_cc_delta = 0;
+ rc = smblib_get_charge_param(chg, &chg->param.jeita_cc_comp,
+ &cc_minus_ua);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get jeita cc minus rc=%d\n", rc);
+ return rc;
+ }
- /*
- * During JEITA condition and with step_charging enabled, PMI will
- * pick the lower of the two value: (FCC - JEITA current compensation)
- * or (FCC + step_charging current delta)
- */
+ *cc_delta_ua = -cc_minus_ua;
+ return 0;
+}
+
+static void smblib_split_fcc(struct smb_charger *chg, int total_ua,
+ int *master_ua, int *slave_ua)
+{
+ int rc, jeita_cc_delta_ua, step_cc_delta_ua, effective_total_ua,
+ slave_limited_ua, hw_cc_delta_ua = 0;
- effective_fcc = min(max(0, total_fcc - cc_reduction_ua),
- max(0, total_fcc + step_cc_delta));
- *master_ua = (effective_fcc * master_percent) / 100;
- *slave_ua = (effective_fcc - *master_ua) * chg->pl.taper_percent / 100;
- *master_ua = max(0, *master_ua + total_fcc - effective_fcc);
+ rc = smblib_get_step_cc_delta(chg, &step_cc_delta_ua);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get step cc delta rc=%d\n", rc);
+ step_cc_delta_ua = 0;
+ } else {
+ hw_cc_delta_ua = step_cc_delta_ua;
+ }
+
+ rc = smblib_get_jeita_cc_delta(chg, &jeita_cc_delta_ua);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get jeita cc delta rc=%d\n", rc);
+ jeita_cc_delta_ua = 0;
+ } else if (jeita_cc_delta_ua < 0) {
+ /* HW will take the min between JEITA and step charge */
+ hw_cc_delta_ua = min(hw_cc_delta_ua, jeita_cc_delta_ua);
+ }
+
+ effective_total_ua = max(0, total_ua + hw_cc_delta_ua);
+ slave_limited_ua = min(effective_total_ua, chg->input_limited_fcc_ua);
+ *slave_ua = (slave_limited_ua * chg->pl.slave_pct) / 100;
+ *slave_ua = (*slave_ua * chg->pl.taper_pct) / 100;
+ *master_ua = max(0, total_ua - *slave_ua);
}
/********************
@@ -459,9 +471,8 @@ static int try_rerun_apsd_for_hvdcp(struct smb_charger *chg)
return 0;
}
-static int smblib_update_usb_type(struct smb_charger *chg)
+static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg)
{
- int rc = 0;
const struct apsd_result *apsd_result;
/* if PD is active, APSD is disabled so won't have a valid result */
@@ -472,7 +483,7 @@ static int smblib_update_usb_type(struct smb_charger *chg)
apsd_result = smblib_get_apsd_result(chg);
chg->usb_psy_desc.type = apsd_result->pst;
- return rc;
+ return apsd_result;
}
static int smblib_notifier_call(struct notifier_block *nb,
@@ -577,27 +588,25 @@ static int smblib_fcc_max_vote_callback(struct votable *votable, void *data,
}
static int smblib_fcc_vote_callback(struct votable *votable, void *data,
- int fcc_ua, const char *client)
+ int total_fcc_ua, const char *client)
{
struct smb_charger *chg = data;
- int rc = 0;
union power_supply_propval pval = {0, };
- int master_ua = fcc_ua, slave_ua;
+ int rc, master_fcc_ua = total_fcc_ua, slave_fcc_ua = 0;
- if (fcc_ua < 0) {
- smblib_dbg(chg, PR_MISC, "No Voter\n");
+ if (total_fcc_ua < 0)
return 0;
- }
if (chg->mode == PARALLEL_MASTER
&& !get_effective_result_locked(chg->pl_disable_votable)) {
- smblib_fcc_split_ua(chg, fcc_ua, &master_ua, &slave_ua);
+ smblib_split_fcc(chg, total_fcc_ua, &master_fcc_ua,
+ &slave_fcc_ua);
/*
* parallel charger is not disabled, implying that
* chg->pl.psy exists
*/
- pval.intval = slave_ua;
+ pval.intval = slave_fcc_ua;
rc = power_supply_set_property(chg->pl.psy,
POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
if (rc < 0) {
@@ -606,15 +615,20 @@ static int smblib_fcc_vote_callback(struct votable *votable, void *data,
return rc;
}
- chg->pl.slave_fcc = slave_ua;
+ chg->pl.slave_fcc_ua = slave_fcc_ua;
}
- rc = smblib_set_charge_param(chg, &chg->param.fcc, master_ua);
+ rc = smblib_set_charge_param(chg, &chg->param.fcc, master_fcc_ua);
if (rc < 0) {
- smblib_err(chg, "Error in setting fcc, rc=%d\n", rc);
+ smblib_err(chg, "Couldn't set master fcc rc=%d\n", rc);
return rc;
}
+ smblib_dbg(chg, PR_PARALLEL, "master_fcc=%d slave_fcc=%d distribution=(%d/%d)\n",
+ master_fcc_ua, slave_fcc_ua,
+ (master_fcc_ua * 100) / total_fcc_ua,
+ (slave_fcc_ua * 100) / total_fcc_ua);
+
return 0;
}
@@ -811,7 +825,7 @@ static int smblib_pl_disable_vote_callback(struct votable *votable, void *data,
if (chg->mode != PARALLEL_MASTER || !chg->pl.psy)
return 0;
- chg->pl.taper_percent = 100;
+ chg->pl.taper_pct = 100;
rerun_election(chg->fv_votable);
rerun_election(chg->fcc_votable);
@@ -824,6 +838,9 @@ static int smblib_pl_disable_vote_callback(struct votable *votable, void *data,
return rc;
}
+ smblib_dbg(chg, PR_PARALLEL, "parallel charging %s\n",
+ pl_disable ? "disabled" : "enabled");
+
return 0;
}
@@ -1749,7 +1766,7 @@ int smblib_get_prop_typec_power_role(struct smb_charger *chg,
int smblib_get_prop_pd_allowed(struct smb_charger *chg,
union power_supply_propval *val)
{
- val->intval = get_effective_result_locked(chg->pd_allowed_votable);
+ val->intval = get_effective_result(chg->pd_allowed_votable);
return 0;
}
@@ -1775,6 +1792,19 @@ int smblib_get_prop_pd_in_hard_reset(struct smb_charger *chg,
return 0;
}
+int smblib_get_pe_start(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ /*
+ * hvdcp timeout voter is the last one to allow pd. Use its vote
+ * to indicate start of pe engine
+ */
+ val->intval
+ = !get_client_vote_locked(chg->pd_disallowed_votable_indirect,
+ HVDCP_TIMEOUT_VOTER);
+ return 0;
+}
+
/*******************
* USB PSY SETTERS *
* *****************/
@@ -1945,6 +1975,13 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
smblib_update_usb_type(chg);
power_supply_changed(chg->usb_psy);
+ rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT,
+ chg->pd_active ? 0 : EN_TRYSINK_MODE_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't set TRYSINK_MODE rc=%d\n", rc);
+ return rc;
+ }
+
return rc;
}
@@ -2177,25 +2214,34 @@ skip_dpdm_float:
return IRQ_HANDLED;
}
-#define USB_WEAK_INPUT_MA 1400000
+#define USB_WEAK_INPUT_UA 1400000
+#define EFFICIENCY_PCT 80
irqreturn_t smblib_handle_icl_change(int irq, void *data)
{
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
- int icl_ma;
- int rc;
+ int rc, icl_ua;
+
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
- rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &icl_ma);
+ rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &icl_ua);
if (rc < 0) {
smblib_err(chg, "Couldn't get ICL status rc=%d\n", rc);
return IRQ_HANDLED;
}
- if (chg->mode == PARALLEL_MASTER)
- vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER,
- icl_ma >= USB_WEAK_INPUT_MA, 0);
+ if (chg->mode != PARALLEL_MASTER)
+ return IRQ_HANDLED;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
+ chg->input_limited_fcc_ua = div64_s64(
+ (s64)icl_ua * MICRO_5V * EFFICIENCY_PCT,
+ (s64)get_effective_result(chg->fv_votable) * 100);
+
+ if (!get_effective_result(chg->pl_disable_votable))
+ rerun_election(chg->fcc_votable);
+
+ vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER,
+ icl_ua >= USB_WEAK_INPUT_UA, 0);
return IRQ_HANDLED;
}
@@ -2271,13 +2317,12 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg,
#define HVDCP_DET_MS 2500
static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
{
- int rc;
const struct apsd_result *apsd_result;
if (!rising)
return;
- apsd_result = smblib_get_apsd_result(chg);
+ apsd_result = smblib_update_usb_type(chg);
switch (apsd_result->bit) {
case SDP_CHARGER_BIT:
case CDP_CHARGER_BIT:
@@ -2296,10 +2341,6 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
break;
}
- rc = smblib_update_usb_type(chg);
- if (rc < 0)
- smblib_err(chg, "Couldn't update usb type rc=%d\n", rc);
-
smblib_dbg(chg, PR_INTERRUPT, "IRQ: apsd-done rising; %s detected\n",
apsd_result->name);
}
@@ -2506,6 +2547,15 @@ irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
return IRQ_HANDLED;
}
+irqreturn_t smblib_handle_dc_plugin(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+
+ power_supply_changed(chg->dc_psy);
+ return IRQ_HANDLED;
+}
+
irqreturn_t smblib_handle_high_duty_cycle(int irq, void *data)
{
struct smb_irq_data *irq_data = data;
@@ -2569,7 +2619,7 @@ static void smblib_pl_detect_work(struct work_struct *work)
#define MINIMUM_PARALLEL_FCC_UA 500000
#define PL_TAPER_WORK_DELAY_MS 100
-#define TAPER_RESIDUAL_PERCENT 75
+#define TAPER_RESIDUAL_PCT 75
static void smblib_pl_taper_work(struct work_struct *work)
{
struct smb_charger *chg = container_of(work, struct smb_charger,
@@ -2577,7 +2627,7 @@ static void smblib_pl_taper_work(struct work_struct *work)
union power_supply_propval pval = {0, };
int rc;
- if (chg->pl.slave_fcc < MINIMUM_PARALLEL_FCC_UA) {
+ if (chg->pl.slave_fcc_ua < MINIMUM_PARALLEL_FCC_UA) {
vote(chg->pl_disable_votable, TAPER_END_VOTER, true, 0);
goto done;
}
@@ -2591,8 +2641,8 @@ static void smblib_pl_taper_work(struct work_struct *work)
if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) {
vote(chg->awake_votable, PL_TAPER_WORK_RUNNING_VOTER, true, 0);
/* Reduce the taper percent by 25 percent */
- chg->pl.taper_percent = chg->pl.taper_percent
- * TAPER_RESIDUAL_PERCENT / 100;
+ chg->pl.taper_pct = chg->pl.taper_pct
+ * TAPER_RESIDUAL_PCT / 100;
rerun_election(chg->fcc_votable);
schedule_delayed_work(&chg->pl_taper_work,
msecs_to_jiffies(PL_TAPER_WORK_DELAY_MS));
diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h
index b6913fa12f1a..4be06ffcfb25 100644
--- a/drivers/power/qcom-charger/smb-lib.h
+++ b/drivers/power/qcom-charger/smb-lib.h
@@ -22,6 +22,7 @@ enum print_reason {
PR_INTERRUPT = BIT(0),
PR_REGISTER = BIT(1),
PR_MISC = BIT(2),
+ PR_PARALLEL = BIT(3),
};
#define DEFAULT_VOTER "DEFAULT_VOTER"
@@ -102,9 +103,9 @@ struct smb_params {
struct parallel_params {
struct power_supply *psy;
- int *master_percent;
- int taper_percent;
- int slave_fcc;
+ int slave_pct;
+ int taper_pct;
+ int slave_fcc_ua;
};
struct smb_iio {
@@ -190,6 +191,7 @@ struct smb_charger {
bool step_chg_enabled;
bool is_hdc;
bool chg_done;
+ int input_limited_fcc_ua;
/* workaround flag */
u32 wa_flags;
@@ -236,6 +238,7 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data);
irqreturn_t smblib_handle_usb_source_change(int irq, void *data);
irqreturn_t smblib_handle_icl_change(int irq, void *data);
irqreturn_t smblib_handle_usb_typec_change(int irq, void *data);
+irqreturn_t smblib_handle_dc_plugin(int irq, void *data);
irqreturn_t smblib_handle_high_duty_cycle(int irq, void *data);
int smblib_get_prop_input_suspend(struct smb_charger *chg,
@@ -307,6 +310,8 @@ int smblib_get_prop_input_current_settled(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_pd_in_hard_reset(struct smb_charger *chg,
union power_supply_propval *val);
+int smblib_get_pe_start(struct smb_charger *chg,
+ union power_supply_propval *val);
int smblib_get_prop_charger_temp(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_charger_temp_max(struct smb_charger *chg,
diff --git a/drivers/power/qcom-charger/smb-reg.h b/drivers/power/qcom-charger/smb-reg.h
index c2941f61f4a2..ba501761c209 100644
--- a/drivers/power/qcom-charger/smb-reg.h
+++ b/drivers/power/qcom-charger/smb-reg.h
@@ -52,6 +52,7 @@ enum {
#define CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(5)
#define CHARGER_ERROR_STATUS_BAT_TERM_MISSING_BIT BIT(4)
#define BAT_TEMP_STATUS_MASK GENMASK(3, 0)
+#define BAT_TEMP_STATUS_SOFT_LIMIT_MASK GENMASK(3, 2)
#define BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT BIT(3)
#define BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT BIT(2)
#define BAT_TEMP_STATUS_TOO_HOT_BIT BIT(1)
diff --git a/drivers/power/qcom-charger/smb1351-charger.c b/drivers/power/qcom-charger/smb1351-charger.c
index 0f18844b9afa..79fbe33acf5d 100644
--- a/drivers/power/qcom-charger/smb1351-charger.c
+++ b/drivers/power/qcom-charger/smb1351-charger.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016 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
@@ -1872,6 +1872,12 @@ static void smb1351_chg_adc_notification(enum qpnp_tm_state state, void *ctx)
}
}
+ if (!cur) {
+ pr_debug("Couldn't choose batt state, adc state=%d and temp=%d\n",
+ state, temp);
+ return;
+ }
+
if (cur->batt_present)
chip->battery_missing = false;
else
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 2bc74941abc8..8c43effadc70 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -821,4 +821,12 @@ config QCOM_SMCINVOKE
Enable SMCInvoke driver which supports capability based secure
communication between QSEE and HLOS.
+config QCOM_EARLY_RANDOM
+ bool "Initialize random pool very early"
+ help
+ The standard random pool may not initialize until late in the boot
+ process which means that any calls to get random numbers before then
+ may not be truly random. Select this option to make an early call
+ to get some random data to put in the pool. If unsure, say N.
+
source "drivers/soc/qcom/memshare/Kconfig"
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 434a114c000f..0105e03b082d 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -99,3 +99,4 @@ obj-$(CONFIG_MSM_RPM_LOG) += rpm_log.o
obj-$(CONFIG_QSEE_IPC_IRQ_BRIDGE) += qsee_ipc_irq_bridge.o
obj-$(CONFIG_WCD_DSP_GLINK) += wcd-dsp-glink.o
obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o
+obj-$(CONFIG_QCOM_EARLY_RANDOM) += early_random.o
diff --git a/drivers/soc/qcom/early_random.c b/drivers/soc/qcom/early_random.c
new file mode 100644
index 000000000000..d1ab39b16c81
--- /dev/null
+++ b/drivers/soc/qcom/early_random.c
@@ -0,0 +1,63 @@
+/* Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/random.h>
+
+#include <soc/qcom/scm.h>
+
+#include <asm/io.h>
+#include <asm/cacheflush.h>
+
+#define TZ_SVC_CRYPTO 10
+#define PRNG_CMD_ID 0x01
+
+struct tz_prng_data {
+ uint8_t *out_buf;
+ uint32_t out_buf_sz;
+} __packed;
+
+DEFINE_SCM_BUFFER(common_scm_buf)
+#define RANDOM_BUFFER_SIZE PAGE_SIZE
+char random_buffer[RANDOM_BUFFER_SIZE] __aligned(PAGE_SIZE);
+
+void __init init_random_pool(void)
+{
+ struct tz_prng_data data;
+ int ret;
+ u32 resp;
+ struct scm_desc desc;
+
+ data.out_buf = (uint8_t *) virt_to_phys(random_buffer);
+ desc.args[0] = (unsigned long) data.out_buf;
+ desc.args[1] = data.out_buf_sz = SZ_512;
+ desc.arginfo = SCM_ARGS(2, SCM_RW, SCM_VAL);
+
+ dmac_flush_range(random_buffer, random_buffer + RANDOM_BUFFER_SIZE);
+
+ if (!is_scm_armv8())
+ ret = scm_call_noalloc(TZ_SVC_CRYPTO, PRNG_CMD_ID, &data,
+ sizeof(data), &resp, sizeof(resp),
+ common_scm_buf,
+ SCM_BUFFER_SIZE(common_scm_buf));
+ else
+ ret = scm_call2(SCM_SIP_FNID(TZ_SVC_CRYPTO, PRNG_CMD_ID),
+ &desc);
+
+ if (!ret) {
+ dmac_inv_range(random_buffer, random_buffer +
+ RANDOM_BUFFER_SIZE);
+ add_device_randomness(random_buffer, SZ_512);
+ }
+}
+
diff --git a/drivers/soc/qcom/glink_spi_xprt.c b/drivers/soc/qcom/glink_spi_xprt.c
index 66caa6ecaad2..47c66c892736 100644
--- a/drivers/soc/qcom/glink_spi_xprt.c
+++ b/drivers/soc/qcom/glink_spi_xprt.c
@@ -875,21 +875,20 @@ static void __rx_worker(struct edge_info *einfo)
int rcu_id;
rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return;
+ }
+
if (unlikely(!einfo->rx_fifo_start)) {
rx_avail = glink_spi_xprt_read_avail(einfo);
if (!rx_avail) {
srcu_read_unlock(&einfo->use_ref, rcu_id);
return;
}
- einfo->in_ssr = false;
einfo->xprt_if.glink_core_if_ptr->link_up(&einfo->xprt_if);
}
- if (einfo->in_ssr) {
- srcu_read_unlock(&einfo->use_ref, rcu_id);
- return;
- }
-
glink_spi_xprt_set_poll_mode(einfo);
while (inactive_cycles < MAX_INACTIVE_CYCLES) {
if (einfo->tx_resume_needed &&
@@ -1818,9 +1817,16 @@ static int glink_wdsp_cmpnt_event_handler(struct device *dev,
spi_dev = to_spi_device(sdev);
einfo->spi_dev = spi_dev;
break;
+ case WDSP_EVENT_POST_BOOTUP:
+ einfo->in_ssr = false;
+ synchronize_srcu(&einfo->use_ref);
+ /* No break here to trigger fake rx_worker */
case WDSP_EVENT_IPC1_INTR:
queue_kthread_work(&einfo->kworker, &einfo->kwork);
break;
+ case WDSP_EVENT_PRE_SHUTDOWN:
+ ssr(&einfo->xprt_if);
+ break;
default:
pr_debug("%s: unhandled event %d", __func__, event);
break;
@@ -2040,7 +2046,6 @@ static int glink_spi_probe(struct platform_device *pdev)
init_xprt_cfg(einfo, subsys_name);
init_xprt_if(einfo);
- einfo->in_ssr = true;
einfo->fifo_size = DEFAULT_FIFO_SIZE;
init_kthread_work(&einfo->kwork, rx_worker);
init_kthread_worker(&einfo->kworker);
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index f47d4a51fccd..5e7f5c8bd2a1 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -107,6 +107,18 @@ module_param(qmi_timeout, ulong, 0600);
#define WCSS_CLK_CTL_WCSS_CSS_GDSCR_HW_CONTROL BIT(1)
#define WCSS_CLK_CTL_WCSS_CSS_GDSCR_PWR_ON BIT(31)
+#define WCSS_CLK_CTL_NOC_CMD_RCGR_OFFSET 0x1D1030
+#define WCSS_CLK_CTL_NOC_CMD_RCGR_UPDATE BIT(0)
+
+#define WCSS_CLK_CTL_NOC_CFG_RCGR_OFFSET 0x1D1034
+#define WCSS_CLK_CTL_NOC_CFG_RCGR_SRC_SEL GENMASK(10, 8)
+
+#define WCSS_CLK_CTL_REF_CMD_RCGR_OFFSET 0x1D602C
+#define WCSS_CLK_CTL_REF_CMD_RCGR_UPDATE BIT(0)
+
+#define WCSS_CLK_CTL_REF_CFG_RCGR_OFFSET 0x1D6030
+#define WCSS_CLK_CTL_REF_CFG_RCGR_SRC_SEL GENMASK(10, 8)
+
/*
* Registers: WCSS_HM_A_WIFI_APB_3_A_WCMN_MAC_WCMN_REG
* Base Address: 0x18AF0000
@@ -1084,7 +1096,7 @@ static void icnss_hw_io_reset(struct icnss_priv *priv, bool on)
}
}
-int icnss_hw_reset_wlan_ss_power_down(struct icnss_priv *priv)
+static int icnss_hw_reset_wlan_ss_power_down(struct icnss_priv *priv)
{
u32 rdata;
@@ -1116,7 +1128,7 @@ int icnss_hw_reset_wlan_ss_power_down(struct icnss_priv *priv)
return 0;
}
-int icnss_hw_reset_common_ss_power_down(struct icnss_priv *priv)
+static int icnss_hw_reset_common_ss_power_down(struct icnss_priv *priv)
{
u32 rdata;
@@ -1161,7 +1173,7 @@ int icnss_hw_reset_common_ss_power_down(struct icnss_priv *priv)
}
-int icnss_hw_reset_wlan_rfactrl_power_down(struct icnss_priv *priv)
+static int icnss_hw_reset_wlan_rfactrl_power_down(struct icnss_priv *priv)
{
u32 rdata;
@@ -1191,7 +1203,7 @@ int icnss_hw_reset_wlan_rfactrl_power_down(struct icnss_priv *priv)
return 0;
}
-void icnss_hw_wsi_cmd_error_recovery(struct icnss_priv *priv)
+static void icnss_hw_wsi_cmd_error_recovery(struct icnss_priv *priv)
{
icnss_pr_dbg("RESET: WSI CMD Error recovery, state: 0x%lx\n",
priv->state);
@@ -1215,7 +1227,7 @@ void icnss_hw_wsi_cmd_error_recovery(struct icnss_priv *priv)
PMM_WSI_CMD_SW_BUS_SYNC, 0);
}
-u32 icnss_hw_rf_register_read_command(struct icnss_priv *priv, u32 addr)
+static u32 icnss_hw_rf_register_read_command(struct icnss_priv *priv, u32 addr)
{
u32 rdata = 0;
int ret;
@@ -1264,7 +1276,7 @@ u32 icnss_hw_rf_register_read_command(struct icnss_priv *priv, u32 addr)
return rdata;
}
-int icnss_hw_reset_rf_reset_cmd(struct icnss_priv *priv)
+static int icnss_hw_reset_rf_reset_cmd(struct icnss_priv *priv)
{
u32 rdata;
int ret;
@@ -1318,7 +1330,30 @@ int icnss_hw_reset_rf_reset_cmd(struct icnss_priv *priv)
return 0;
}
-int icnss_hw_reset_xo_disable_cmd(struct icnss_priv *priv)
+static int icnss_hw_reset_switch_to_cxo(struct icnss_priv *priv)
+{
+ icnss_pr_dbg("RESET: Switch to CXO, state: 0x%lx\n", priv->state);
+
+ icnss_hw_write_reg_field(priv->mem_base_va,
+ WCSS_CLK_CTL_NOC_CFG_RCGR_OFFSET,
+ WCSS_CLK_CTL_NOC_CFG_RCGR_SRC_SEL, 0);
+
+ icnss_hw_write_reg_field(priv->mem_base_va,
+ WCSS_CLK_CTL_NOC_CMD_RCGR_OFFSET,
+ WCSS_CLK_CTL_NOC_CMD_RCGR_UPDATE, 1);
+
+ icnss_hw_write_reg_field(priv->mem_base_va,
+ WCSS_CLK_CTL_REF_CFG_RCGR_OFFSET,
+ WCSS_CLK_CTL_REF_CFG_RCGR_SRC_SEL, 0);
+
+ icnss_hw_write_reg_field(priv->mem_base_va,
+ WCSS_CLK_CTL_REF_CMD_RCGR_OFFSET,
+ WCSS_CLK_CTL_REF_CMD_RCGR_UPDATE, 1);
+
+ return 0;
+}
+
+static int icnss_hw_reset_xo_disable_cmd(struct icnss_priv *priv)
{
int ret;
@@ -1366,7 +1401,7 @@ int icnss_hw_reset_xo_disable_cmd(struct icnss_priv *priv)
return 0;
}
-int icnss_hw_reset(struct icnss_priv *priv)
+static int icnss_hw_reset(struct icnss_priv *priv)
{
u32 rdata;
u32 rdata1;
@@ -1424,6 +1459,8 @@ int icnss_hw_reset(struct icnss_priv *priv)
icnss_hw_reset_rf_reset_cmd(priv);
+ icnss_hw_reset_switch_to_cxo(priv);
+
icnss_hw_reset_xo_disable_cmd(priv);
icnss_hw_write_reg_field(priv->mpm_config_va, MPM_WCSSAON_CONFIG_OFFSET,
@@ -2377,6 +2414,7 @@ static int icnss_call_driver_probe(struct icnss_priv *priv)
out:
icnss_hw_power_off(priv);
+ penv->ops = NULL;
return ret;
}
@@ -2479,6 +2517,7 @@ static int icnss_driver_event_register_driver(void *data)
power_off:
icnss_hw_power_off(penv);
+ penv->ops = NULL;
out:
return ret;
}
@@ -2947,7 +2986,7 @@ int icnss_register_driver(struct icnss_driver_ops *ops)
ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
ICNSS_EVENT_SYNC, ops);
- if (ret == -ERESTARTSYS)
+ if (ret == -EINTR)
ret = 0;
out:
diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c
index ab00eac7f633..8cba88742cb8 100644
--- a/drivers/soc/qcom/service-notifier.c
+++ b/drivers/soc/qcom/service-notifier.c
@@ -462,7 +462,7 @@ static int ssr_event_notify(struct notifier_block *this,
struct qmi_client_info, ssr_notifier);
struct notif_data *notif = data;
switch (code) {
- case SUBSYS_AFTER_SHUTDOWN:
+ case SUBSYS_BEFORE_SHUTDOWN:
pr_debug("Root PD DOWN(SSR notification), crashed?%d\n",
notif->crashed);
if (notif->crashed)
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index 6ceac4f2d4b2..560c5c72daeb 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -103,6 +103,7 @@ struct cpufreq_cooling_device {
int dyn_power_table_entries;
struct device *cpu_dev;
get_static_t plat_get_static_power;
+ struct cpu_cooling_ops *plat_ops;
};
static DEFINE_IDR(cpufreq_idr);
static DEFINE_MUTEX(cooling_cpufreq_lock);
@@ -504,8 +505,13 @@ static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
+ unsigned int cpu = cpumask_any(&cpufreq_device->allowed_cpus);
- *state = cpufreq_device->cpufreq_state;
+ if (cpufreq_device->plat_ops
+ && cpufreq_device->plat_ops->get_cur_state)
+ cpufreq_device->plat_ops->get_cur_state(cpu, state);
+ else
+ *state = cpufreq_device->cpufreq_state;
return 0;
}
@@ -539,7 +545,17 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
cpufreq_device->cpufreq_state = state;
cpufreq_device->clipped_freq = clip_freq;
- cpufreq_update_policy(cpu);
+ /* Check if the device has a platform mitigation function that
+ * can handle the CPU freq mitigation, if not, notify cpufreq
+ * framework.
+ */
+ if (cpufreq_device->plat_ops) {
+ if (cpufreq_device->plat_ops->ceil_limit)
+ cpufreq_device->plat_ops->ceil_limit(cpu,
+ clip_freq);
+ } else {
+ cpufreq_update_policy(cpu);
+ }
return 0;
}
@@ -773,6 +789,9 @@ static unsigned int find_next_max(struct cpufreq_frequency_table *table,
* @capacitance: dynamic power coefficient for these cpus
* @plat_static_func: function to calculate the static power consumed by these
* cpus (optional)
+ * @plat_mitig_func: function that does the mitigation by changing the
+ * frequencies (Optional). By default, cpufreq framweork will
+ * be notified of the new limits.
*
* This interface function registers the cpufreq cooling device with the name
* "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
@@ -785,7 +804,8 @@ static unsigned int find_next_max(struct cpufreq_frequency_table *table,
static struct thermal_cooling_device *
__cpufreq_cooling_register(struct device_node *np,
const struct cpumask *clip_cpus, u32 capacitance,
- get_static_t plat_static_func)
+ get_static_t plat_static_func,
+ struct cpu_cooling_ops *plat_ops)
{
struct thermal_cooling_device *cool_dev;
struct cpufreq_cooling_device *cpufreq_dev;
@@ -851,6 +871,8 @@ __cpufreq_cooling_register(struct device_node *np,
}
}
+ cpufreq_dev->plat_ops = plat_ops;
+
ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
if (ret) {
cool_dev = ERR_PTR(ret);
@@ -924,7 +946,7 @@ free_cdev:
struct thermal_cooling_device *
cpufreq_cooling_register(const struct cpumask *clip_cpus)
{
- return __cpufreq_cooling_register(NULL, clip_cpus, 0, NULL);
+ return __cpufreq_cooling_register(NULL, clip_cpus, 0, NULL, NULL);
}
EXPORT_SYMBOL_GPL(cpufreq_cooling_register);
@@ -948,7 +970,7 @@ of_cpufreq_cooling_register(struct device_node *np,
if (!np)
return ERR_PTR(-EINVAL);
- return __cpufreq_cooling_register(np, clip_cpus, 0, NULL);
+ return __cpufreq_cooling_register(np, clip_cpus, 0, NULL, NULL);
}
EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register);
@@ -978,11 +1000,31 @@ cpufreq_power_cooling_register(const struct cpumask *clip_cpus, u32 capacitance,
get_static_t plat_static_func)
{
return __cpufreq_cooling_register(NULL, clip_cpus, capacitance,
- plat_static_func);
+ plat_static_func, NULL);
}
EXPORT_SYMBOL(cpufreq_power_cooling_register);
/**
+ * cpufreq_platform_cooling_register() - create cpufreq cooling device with
+ * additional platform specific mitigation function.
+ *
+ * @clip_cpus: cpumask of cpus where the frequency constraints will happen
+ * @plat_ops: the platform mitigation functions that will be called insted of
+ * cpufreq, if provided.
+ *
+ * Return: a valid struct thermal_cooling_device pointer on success,
+ * on failure, it returns a corresponding ERR_PTR().
+ */
+struct thermal_cooling_device *
+cpufreq_platform_cooling_register(const struct cpumask *clip_cpus,
+ struct cpu_cooling_ops *plat_ops)
+{
+ return __cpufreq_cooling_register(NULL, clip_cpus, 0, NULL,
+ plat_ops);
+}
+EXPORT_SYMBOL(cpufreq_platform_cooling_register);
+
+/**
* of_cpufreq_power_cooling_register() - create cpufreq cooling device with power extensions
* @np: a valid struct device_node to the cooling device device tree node
* @clip_cpus: cpumask of cpus where the frequency constraints will happen
@@ -1015,7 +1057,7 @@ of_cpufreq_power_cooling_register(struct device_node *np,
return ERR_PTR(-EINVAL);
return __cpufreq_cooling_register(np, clip_cpus, capacitance,
- plat_static_func);
+ plat_static_func, NULL);
}
EXPORT_SYMBOL(of_cpufreq_power_cooling_register);
diff --git a/drivers/thermal/lmh_lite.c b/drivers/thermal/lmh_lite.c
index 32a573d22270..44ceb723d34c 100644
--- a/drivers/thermal/lmh_lite.c
+++ b/drivers/thermal/lmh_lite.c
@@ -70,8 +70,8 @@
int idx = 0; \
desc_arg.args[cmd_idx] = cmd_buf.list_start = next; \
trace_lmh_event_call("GET_TYPE enter"); \
- dmac_flush_range(payload, payload + sizeof(uint32_t) * \
- LMH_SCM_PAYLOAD_SIZE); \
+ dmac_flush_range(payload, (void *)payload + \
+ sizeof(uint32_t) * LMH_SCM_PAYLOAD_SIZE);\
if (!is_scm_armv8()) { \
ret = scm_call(SCM_SVC_LMH, cmd_id, \
(void *) &cmd_buf, SCM_BUFFER_SIZE(cmd_buf), \
@@ -321,7 +321,8 @@ static void lmh_read_and_update(struct lmh_driver_data *lmh_dat)
= SCM_BUFFER_SIZE(struct lmh_sensor_packet);
desc_arg.arginfo = SCM_ARGS(2, SCM_RW, SCM_VAL);
trace_lmh_event_call("GET_INTENSITY enter");
- dmac_flush_range(&payload, &payload + sizeof(struct lmh_sensor_packet));
+ dmac_flush_range(&payload, (void *)&payload +
+ sizeof(struct lmh_sensor_packet));
if (!is_scm_armv8())
ret = scm_call(SCM_SVC_LMH, LMH_GET_INTENSITY,
(void *) &cmd_buf, SCM_BUFFER_SIZE(cmd_buf), NULL, 0);
@@ -664,7 +665,7 @@ static int lmh_get_sensor_list(void)
lmh_sensor_packet);
desc_arg.arginfo = SCM_ARGS(2, SCM_RW, SCM_VAL);
trace_lmh_event_call("GET_SENSORS enter");
- dmac_flush_range(payload, payload + buf_size);
+ dmac_flush_range(payload, (void *)payload + buf_size);
if (!is_scm_armv8())
ret = scm_call(SCM_SVC_LMH, LMH_GET_SENSORS,
(void *) &cmd_buf,
@@ -898,7 +899,7 @@ static int lmh_debug_read(struct lmh_debug_ops *ops, uint32_t **buf)
desc_arg.args[1] = cmd_buf.buf_size = curr_size;
desc_arg.arginfo = SCM_ARGS(2, SCM_RW, SCM_VAL);
trace_lmh_event_call("GET_DEBUG_READ enter");
- dmac_flush_range(payload, payload + curr_size);
+ dmac_flush_range(payload, (void *)payload + curr_size);
if (!is_scm_armv8()) {
ret = scm_call(SCM_SVC_LMH, LMH_DEBUG_READ,
(void *) &cmd_buf, SCM_BUFFER_SIZE(cmd_buf),
@@ -968,7 +969,7 @@ static int lmh_debug_config_write(uint32_t cmd_id, uint32_t *buf, int size)
desc_arg.arginfo = SCM_ARGS(5, SCM_RO, SCM_VAL, SCM_VAL, SCM_VAL,
SCM_VAL);
trace_lmh_event_call("CONFIG_DEBUG_WRITE enter");
- dmac_flush_range(payload, payload + size_bytes);
+ dmac_flush_range(payload, (void *)payload + size_bytes);
if (!is_scm_armv8())
ret = scm_call(SCM_SVC_LMH, cmd_id, (void *) &cmd_buf,
SCM_BUFFER_SIZE(cmd_buf), NULL, 0);
diff --git a/drivers/thermal/msm_lmh_dcvs.c b/drivers/thermal/msm_lmh_dcvs.c
index cd45eeccbfe7..3758e39a1c02 100644
--- a/drivers/thermal/msm_lmh_dcvs.c
+++ b/drivers/thermal/msm_lmh_dcvs.c
@@ -24,6 +24,7 @@
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/pm_opp.h>
+#include <linux/cpu_cooling.h>
#include <asm/smp_plat.h>
#include <asm/cacheflush.h>
@@ -40,6 +41,7 @@
#define MSM_LIMITS_NODE_DCVS 0x44435653
#define MSM_LIMITS_SUB_FN_THERMAL 0x54484D4C
+#define MSM_LIMITS_SUB_FN_GENERAL 0x47454E00
#define MSM_LIMITS_ALGO_MODE_ENABLE 0x454E424C
@@ -49,6 +51,8 @@
#define MSM_LIMITS_CLUSTER_0 0x6370302D
#define MSM_LIMITS_CLUSTER_1 0x6370312D
+#define MSM_LIMITS_DOMAIN_MAX 0x444D4158
+
#define MSM_LIMITS_HIGH_THRESHOLD_VAL 95000
#define MSM_LIMITS_ARM_THRESHOLD_VAL 65000
#define MSM_LIMITS_POLLING_DELAY_MS 10
@@ -77,8 +81,12 @@ struct msm_lmh_dcvs_hw {
cpumask_t core_map;
struct timer_list poll_timer;
uint32_t max_freq;
+ uint32_t hw_freq_limit;
+ struct list_head list;
};
+LIST_HEAD(lmh_dcvs_hw_list);
+
static void msm_lmh_dcvs_get_max_freq(uint32_t cpu, uint32_t *max_freq)
{
unsigned long freq_ceil = UINT_MAX;
@@ -99,12 +107,29 @@ static void msm_lmh_dcvs_get_max_freq(uint32_t cpu, uint32_t *max_freq)
static uint32_t msm_lmh_mitigation_notify(struct msm_lmh_dcvs_hw *hw)
{
uint32_t max_limit = 0, val = 0;
+ struct device *cpu_dev = NULL;
+ unsigned long freq_val;
val = readl_relaxed(hw->osm_hw_reg);
dcvsh_get_frequency(val, max_limit);
+ cpu_dev = get_cpu_device(cpumask_first(&hw->core_map));
+ if (!cpu_dev) {
+ pr_err("Error in get CPU%d device\n",
+ cpumask_first(&hw->core_map));
+ goto notify_exit;
+ }
+
+ freq_val = max_limit;
+ rcu_read_lock();
+ dev_pm_opp_find_freq_floor(cpu_dev, &freq_val);
+ rcu_read_unlock();
+ max_limit = freq_val;
+
sched_update_cpu_freq_min_max(&hw->core_map, 0, max_limit);
trace_lmh_dcvs_freq(cpumask_first(&hw->core_map), max_limit);
+notify_exit:
+ hw->hw_freq_limit = max_limit;
return max_limit;
}
@@ -164,7 +189,7 @@ static int msm_lmh_dcvs_write(uint32_t node_id, uint32_t fn,
desc_arg.arginfo = SCM_ARGS(5, SCM_RO, SCM_VAL, SCM_VAL,
SCM_VAL, SCM_VAL);
- dmac_flush_range(payload, payload + 5 * (sizeof(uint32_t)));
+ dmac_flush_range(payload, (void *)payload + 5 * (sizeof(uint32_t)));
ret = scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, MSM_LIMITS_DCVSH), &desc_arg);
kfree(payload);
@@ -250,6 +275,45 @@ static int trip_notify(enum thermal_trip_type type, int temp, void *data)
return 0;
}
+static struct msm_lmh_dcvs_hw *get_dcvsh_hw_from_cpu(int cpu)
+{
+ struct msm_lmh_dcvs_hw *hw;
+
+ list_for_each_entry(hw, &lmh_dcvs_hw_list, list) {
+ if (cpumask_test_cpu(cpu, &hw->core_map))
+ return hw;
+ }
+
+ return NULL;
+}
+
+static int lmh_set_max_limit(int cpu, u32 freq)
+{
+ struct msm_lmh_dcvs_hw *hw = get_dcvsh_hw_from_cpu(cpu);
+
+ if (!hw)
+ return -EINVAL;
+
+ return msm_lmh_dcvs_write(hw->affinity, MSM_LIMITS_SUB_FN_GENERAL,
+ MSM_LIMITS_DOMAIN_MAX, freq);
+}
+
+static int lmh_get_cur_limit(int cpu, unsigned long *freq)
+{
+ struct msm_lmh_dcvs_hw *hw = get_dcvsh_hw_from_cpu(cpu);
+
+ if (!hw)
+ return -EINVAL;
+ *freq = hw->hw_freq_limit;
+
+ return 0;
+}
+
+static struct cpu_cooling_ops cd_ops = {
+ .get_cur_state = lmh_get_cur_limit,
+ .ceil_limit = lmh_set_max_limit,
+};
+
static int msm_lmh_dcvs_probe(struct platform_device *pdev)
{
int ret;
@@ -257,6 +321,7 @@ static int msm_lmh_dcvs_probe(struct platform_device *pdev)
struct msm_lmh_dcvs_hw *hw;
char sensor_name[] = "limits_sensor-00";
struct thermal_zone_device *tzdev;
+ struct thermal_cooling_device *cdev;
struct device_node *dn = pdev->dev.of_node;
struct device_node *cpu_node, *lmh_node;
uint32_t id, max_freq, request_reg, clear_reg;
@@ -331,6 +396,10 @@ static int msm_lmh_dcvs_probe(struct platform_device *pdev)
if (IS_ERR_OR_NULL(tzdev))
return PTR_ERR(tzdev);
+ /* Setup cooling devices to request mitigation states */
+ cdev = cpufreq_platform_cooling_register(&hw->core_map, &cd_ops);
+ if (IS_ERR_OR_NULL(cdev))
+ return PTR_ERR(cdev);
/*
* Driver defaults to for low and hi thresholds.
* Since we make a check for hi > lo value, set the hi threshold
@@ -356,7 +425,7 @@ static int msm_lmh_dcvs_probe(struct platform_device *pdev)
return ret;
}
- hw->max_freq = max_freq;
+ hw->hw_freq_limit = hw->max_freq = max_freq;
switch (affinity) {
case 0:
@@ -399,6 +468,9 @@ static int msm_lmh_dcvs_probe(struct platform_device *pdev)
return ret;
}
+ INIT_LIST_HEAD(&hw->list);
+ list_add(&hw->list, &lmh_dcvs_hw_list);
+
return ret;
}
diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c
index 17ecd61e9ee6..ced2f23addd4 100644
--- a/drivers/thermal/msm_thermal.c
+++ b/drivers/thermal/msm_thermal.c
@@ -1019,7 +1019,7 @@ static int msm_lmh_dcvs_write(uint32_t node_id, uint32_t fn, uint32_t setting,
desc_arg.arginfo = SCM_ARGS(5, SCM_RO, SCM_VAL, SCM_VAL,
SCM_VAL, SCM_VAL);
- dmac_flush_range(payload, payload + 5 * (sizeof(uint32_t)));
+ dmac_flush_range(payload, (void *)payload + 5 * (sizeof(uint32_t)));
ret = scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, MSM_LIMITS_DCVSH), &desc_arg);
kfree(payload);
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index fa3c9e511663..ec9288791667 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -3555,6 +3555,7 @@ static int msm_hs_probe(struct platform_device *pdev)
}
msm_serial_debugfs_init(msm_uport, pdev->id);
+ msm_hs_unconfig_uart_gpios(uport);
uport->line = pdev->id;
if (pdata->userid && pdata->userid <= UARTDM_NR)
diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c
index 316967415aa9..eb306529981f 100644
--- a/drivers/usb/gadget/function/f_qc_rndis.c
+++ b/drivers/usb/gadget/function/f_qc_rndis.c
@@ -1441,13 +1441,13 @@ bool rndis_qc_get_skip_ep_config(void)
return rndis_ipa_params.skip_ep_cfg;
}
-DECLARE_USB_FUNCTION_INIT(qcrndis, qcrndis_alloc_inst, qcrndis_alloc);
+DECLARE_USB_FUNCTION_INIT(rndis_bam, qcrndis_alloc_inst, qcrndis_alloc);
static int __init usb_qcrndis_init(void)
{
int ret;
- ret = usb_function_register(&qcrndisusb_func);
+ ret = usb_function_register(&rndis_bamusb_func);
if (ret) {
pr_err("%s: failed to register diag %d\n", __func__, ret);
return ret;
@@ -1457,7 +1457,7 @@ static int __init usb_qcrndis_init(void)
static void __exit usb_qcrndis_exit(void)
{
- usb_function_unregister(&qcrndisusb_func);
+ usb_function_unregister(&rndis_bamusb_func);
rndis_qc_cleanup();
}
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 2fedf59dc3ae..2bc70d1cf6fa 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -299,7 +299,6 @@ struct usbpd {
enum power_supply_typec_mode typec_mode;
enum power_supply_type psy_type;
bool vbus_present;
- bool pd_allowed;
enum data_role current_dr;
enum power_role current_pr;
@@ -498,6 +497,7 @@ static void pd_send_hard_reset(struct usbpd *pd)
ret = pd_phy_signal(HARD_RESET_SIG, 5); /* tHardResetComplete */
if (!ret)
pd->hard_reset = true;
+ pd->in_pr_swap = false;
}
static void kick_sm(struct usbpd *pd, int ms)
@@ -828,7 +828,15 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
}
}
- if (!pd->pd_allowed)
+ ret = power_supply_get_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_ALLOWED, &val);
+ if (ret) {
+ usbpd_err(&pd->dev, "Unable to read USB PROP_PD_ALLOWED: %d\n",
+ ret);
+ break;
+ }
+
+ if (!val.intval)
break;
/* Reset protocol layer */
@@ -906,8 +914,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
break;
case PE_SNK_TRANSITION_TO_DEFAULT:
- pd->hard_reset = false;
-
if (pd->current_dr != DR_UFP) {
extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 0);
@@ -1455,6 +1461,7 @@ static void usbpd_sm(struct work_struct *w)
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+ pd->in_pr_swap = false;
reset_vdm_state(pd);
if (pd->current_pr == PR_SINK)
@@ -1822,6 +1829,12 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_SNK_TRANSITION_TO_DEFAULT:
+ pd->hard_reset = false;
+
+ val.intval = 0;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
if (pd->vbus_present) {
usbpd_set_state(pd, PE_SNK_STARTUP);
} else {
@@ -2036,41 +2049,41 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
struct usbpd *pd = container_of(nb, struct usbpd, psy_nb);
union power_supply_propval val;
enum power_supply_typec_mode typec_mode;
- bool do_work = false;
int ret;
if (ptr != pd->usb_psy || evt != PSY_EVENT_PROP_CHANGED)
return 0;
ret = power_supply_get_property(pd->usb_psy,
- POWER_SUPPLY_PROP_PD_ALLOWED, &val);
+ POWER_SUPPLY_PROP_TYPEC_MODE, &val);
if (ret) {
- usbpd_err(&pd->dev, "Unable to read USB PROP_PD_ALLOWED: %d\n",
- ret);
+ usbpd_err(&pd->dev, "Unable to read USB TYPEC_MODE: %d\n", ret);
return ret;
}
- if (pd->pd_allowed != val.intval)
- do_work = true;
- pd->pd_allowed = val.intval;
+ typec_mode = val.intval;
ret = power_supply_get_property(pd->usb_psy,
- POWER_SUPPLY_PROP_PRESENT, &val);
+ POWER_SUPPLY_PROP_PE_START, &val);
if (ret) {
- usbpd_err(&pd->dev, "Unable to read USB PRESENT: %d\n", ret);
+ usbpd_err(&pd->dev, "Unable to read USB PROP_PE_START: %d\n",
+ ret);
return ret;
}
- pd->vbus_present = val.intval;
+ /* Don't proceed if PE_START=0 as other props may still change */
+ if (!val.intval && !pd->pd_connected &&
+ typec_mode != POWER_SUPPLY_TYPEC_NONE)
+ return 0;
ret = power_supply_get_property(pd->usb_psy,
- POWER_SUPPLY_PROP_TYPEC_MODE, &val);
+ POWER_SUPPLY_PROP_PRESENT, &val);
if (ret) {
- usbpd_err(&pd->dev, "Unable to read USB TYPEC_MODE: %d\n", ret);
+ usbpd_err(&pd->dev, "Unable to read USB PRESENT: %d\n", ret);
return ret;
}
- typec_mode = val.intval;
+ pd->vbus_present = val.intval;
ret = power_supply_get_property(pd->usb_psy,
POWER_SUPPLY_PROP_TYPE, &val);
@@ -2079,91 +2092,87 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
return ret;
}
- if (pd->psy_type != val.intval)
- do_work = true;
pd->psy_type = val.intval;
+ if (pd->typec_mode == typec_mode)
+ return 0;
+
+ pd->typec_mode = typec_mode;
+
usbpd_dbg(&pd->dev, "typec mode:%d present:%d type:%d orientation:%d\n",
typec_mode, pd->vbus_present, pd->psy_type,
usbpd_get_plug_orientation(pd));
- if (pd->typec_mode != typec_mode) {
- pd->typec_mode = typec_mode;
- do_work = true;
-
- switch (typec_mode) {
- /* Disconnect */
- case POWER_SUPPLY_TYPEC_NONE:
- if (pd->in_pr_swap) {
- usbpd_dbg(&pd->dev, "Ignoring disconnect due to PR swap\n");
- do_work = false;
- }
+ switch (typec_mode) {
+ /* Disconnect */
+ case POWER_SUPPLY_TYPEC_NONE:
+ if (pd->in_pr_swap) {
+ usbpd_dbg(&pd->dev, "Ignoring disconnect due to PR swap\n");
+ return 0;
+ }
- /*
- * Workaround for PMIC HW bug.
- *
- * During hard reset when VBUS goes to 0 the CC logic
- * will report this as a disconnection. In those cases
- * it can be ignored, however the downside is that
- * pd->hard_reset can be momentarily true even when a
- * non-PD capable source is attached, and can't be
- * distinguished from a physical disconnect. In that
- * case, allow for the common case of disconnecting
- * from an SDP.
- *
- * The less common case is a PD-capable SDP which will
- * result in a hard reset getting treated like a
- * disconnect. We can live with this until the HW bug
- * is fixed: in which disconnection won't be reported
- * on VBUS loss alone unless pullup is also removed
- * from CC.
- */
- if (pd->psy_type != POWER_SUPPLY_TYPE_USB &&
- pd->current_state ==
- PE_SNK_TRANSITION_TO_DEFAULT) {
- usbpd_dbg(&pd->dev, "Ignoring disconnect due to hard reset\n");
- do_work = false;
- }
+ /*
+ * Workaround for PMIC HW bug.
+ *
+ * During hard reset when VBUS goes to 0 the CC logic
+ * will report this as a disconnection. In those cases
+ * it can be ignored, however the downside is that
+ * pd->hard_reset can be momentarily true even when a
+ * non-PD capable source is attached, and can't be
+ * distinguished from a physical disconnect. In that
+ * case, allow for the common case of disconnecting
+ * from an SDP.
+ *
+ * The less common case is a PD-capable SDP which will
+ * result in a hard reset getting treated like a
+ * disconnect. We can live with this until the HW bug
+ * is fixed: in which disconnection won't be reported
+ * on VBUS loss alone unless pullup is also removed
+ * from CC.
+ */
+ if (pd->psy_type != POWER_SUPPLY_TYPE_USB &&
+ pd->current_state ==
+ PE_SNK_TRANSITION_TO_DEFAULT) {
+ usbpd_dbg(&pd->dev, "Ignoring disconnect due to hard reset\n");
+ return 0;
+ }
- break;
+ break;
- /* Sink states */
- case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
- case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
- case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
- usbpd_info(&pd->dev, "Type-C Source (%s) connected\n",
- src_current(typec_mode));
- pd->current_pr = PR_SINK;
- pd->in_pr_swap = false;
- break;
+ /* Sink states */
+ case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
+ case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
+ case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
+ usbpd_info(&pd->dev, "Type-C Source (%s) connected\n",
+ src_current(typec_mode));
+ pd->current_pr = PR_SINK;
+ pd->in_pr_swap = false;
+ break;
- /* Source states */
- case POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE:
- case POWER_SUPPLY_TYPEC_SINK:
- usbpd_info(&pd->dev, "Type-C Sink%s connected\n",
- typec_mode == POWER_SUPPLY_TYPEC_SINK ?
- "" : " (powered)");
- pd->current_pr = PR_SRC;
- pd->in_pr_swap = false;
- break;
+ /* Source states */
+ case POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE:
+ case POWER_SUPPLY_TYPEC_SINK:
+ usbpd_info(&pd->dev, "Type-C Sink%s connected\n",
+ typec_mode == POWER_SUPPLY_TYPEC_SINK ?
+ "" : " (powered)");
+ pd->current_pr = PR_SRC;
+ pd->in_pr_swap = false;
+ break;
- case POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY:
- usbpd_info(&pd->dev, "Type-C Debug Accessory connected\n");
- break;
- case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER:
- usbpd_info(&pd->dev, "Type-C Analog Audio Adapter connected\n");
- break;
- default:
- usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n",
- typec_mode);
- break;
- }
+ case POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY:
+ usbpd_info(&pd->dev, "Type-C Debug Accessory connected\n");
+ break;
+ case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER:
+ usbpd_info(&pd->dev, "Type-C Analog Audio Adapter connected\n");
+ break;
+ default:
+ usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n",
+ typec_mode);
+ break;
}
- /* only queue state machine if CC state or PD_ALLOWED changes */
- if (do_work)
- kick_sm(pd, 0);
-
+ /* queue state machine due to CC state change */
+ kick_sm(pd, 0);
return 0;
}
diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h
index c156f5082758..e221494fb7a0 100644
--- a/include/linux/cpu_cooling.h
+++ b/include/linux/cpu_cooling.h
@@ -31,6 +31,11 @@
typedef int (*get_static_t)(cpumask_t *cpumask, int interval,
unsigned long voltage, u32 *power);
+struct cpu_cooling_ops {
+ int (*ceil_limit)(int, u32);
+ int (*get_cur_state)(int, unsigned long *);
+};
+
#ifdef CONFIG_CPU_THERMAL
/**
* cpufreq_cooling_register - function to create cpufreq cooling device.
@@ -43,6 +48,10 @@ struct thermal_cooling_device *
cpufreq_power_cooling_register(const struct cpumask *clip_cpus,
u32 capacitance, get_static_t plat_static_func);
+struct thermal_cooling_device *
+cpufreq_platform_cooling_register(const struct cpumask *clip_cpus,
+ struct cpu_cooling_ops *ops);
+
/**
* of_cpufreq_cooling_register - create cpufreq cooling device based on DT.
* @np: a valid struct device_node to the cooling device device tree node.
@@ -112,6 +121,13 @@ of_cpufreq_power_cooling_register(struct device_node *np,
return NULL;
}
+static inline struct thermal_cooling_device *
+cpufreq_platform_cooling_register(const struct cpumask *clip_cpus,
+ struct cpu_cooling_ops *ops)
+{
+ return NULL;
+}
+
static inline
void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
{
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index a8ccecf5799a..18e1a979db76 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -224,6 +224,7 @@ enum power_supply_property {
POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
POWER_SUPPLY_PROP_PARALLEL_DISABLE,
POWER_SUPPLY_PROP_PARALLEL_PERCENT,
+ POWER_SUPPLY_PROP_PE_START,
/* Local extensions of type int64_t */
POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
/* Properties of type `const char *' */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index a395d8a9ff73..06acefeffd4c 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -356,7 +356,7 @@ extern int lockdep_tasklist_lock_is_held(void);
extern void sched_init(void);
extern void sched_init_smp(void);
extern asmlinkage void schedule_tail(struct task_struct *prev);
-extern void init_idle(struct task_struct *idle, int cpu);
+extern void init_idle(struct task_struct *idle, int cpu, bool hotplug);
extern void init_idle_bootup_task(struct task_struct *idle);
extern cpumask_var_t cpu_isolated_map;
@@ -1332,11 +1332,15 @@ struct ravg {
* sysctl_sched_ravg_hist_size windows. 'demand' could drive frequency
* demand for tasks.
*
- * 'curr_window' represents task's contribution to cpu busy time
- * statistics (rq->curr_runnable_sum) in current window
+ * 'curr_window_cpu' represents task's contribution to cpu busy time on
+ * various CPUs in the current window
*
- * 'prev_window' represents task's contribution to cpu busy time
- * statistics (rq->prev_runnable_sum) in previous window
+ * 'prev_window_cpu' represents task's contribution to cpu busy time on
+ * various CPUs in the previous window
+ *
+ * 'curr_window' represents the sum of all entries in curr_window_cpu
+ *
+ * 'prev_window' represents the sum of all entries in prev_window_cpu
*
* 'pred_demand' represents task's current predicted cpu busy time
*
@@ -1346,6 +1350,7 @@ struct ravg {
u64 mark_start;
u32 sum, demand;
u32 sum_history[RAVG_HIST_SIZE_MAX];
+ u32 *curr_window_cpu, *prev_window_cpu;
u32 curr_window, prev_window;
u16 active_windows;
u32 pred_demand;
diff --git a/kernel/sched/core_ctl.h b/include/linux/sched/core_ctl.h
index 3b0c12acb9c0..98d7cb3e899b 100644
--- a/kernel/sched/core_ctl.h
+++ b/include/linux/sched/core_ctl.h
@@ -16,9 +16,12 @@
#ifdef CONFIG_SCHED_CORE_CTL
void core_ctl_check(u64 wallclock);
-void core_ctl_set_boost(bool boost);
+int core_ctl_set_boost(bool boost);
#else
static inline void core_ctl_check(u64 wallclock) {}
-static inline void core_ctl_set_boost(bool boost) {}
+static inline int core_ctl_set_boost(bool boost)
+{
+ return 0;
+}
#endif
#endif
diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h
index 9fe71c774543..6848454c5447 100644
--- a/include/linux/sched/sysctl.h
+++ b/include/linux/sched/sysctl.h
@@ -44,6 +44,7 @@ extern unsigned int sysctl_sched_wake_to_idle;
#ifdef CONFIG_SCHED_HMP
extern int sysctl_sched_freq_inc_notify;
extern int sysctl_sched_freq_dec_notify;
+extern unsigned int sysctl_sched_freq_reporting_policy;
extern unsigned int sysctl_sched_window_stats_policy;
extern unsigned int sysctl_sched_ravg_hist_size;
extern unsigned int sysctl_sched_cpu_high_irqload;
diff --git a/include/linux/types.h b/include/linux/types.h
index 70dd3dfde631..9f2d2f46b459 100644
--- a/include/linux/types.h
+++ b/include/linux/types.h
@@ -9,6 +9,9 @@
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
+#define DECLARE_BITMAP_ARRAY(name,nr,bits) \
+ unsigned long name[nr][BITS_TO_LONGS(bits)]
+
typedef __u32 __kernel_dev_t;
typedef __kernel_fd_set fd_set;
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index daf69b7df534..7778ff3947de 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -260,6 +260,30 @@ TRACE_EVENT(sched_set_boost,
TP_printk("ref_count=%d", __entry->ref_count)
);
+#if defined(CREATE_TRACE_POINTS) && defined(CONFIG_SCHED_HMP)
+static inline void __window_data(u32 *dst, u32 *src)
+{
+ if (src)
+ memcpy(dst, src, nr_cpu_ids * sizeof(u32));
+ else
+ memset(dst, 0, nr_cpu_ids * sizeof(u32));
+}
+
+struct trace_seq;
+const char *__window_print(struct trace_seq *p, const u32 *buf, int buf_len)
+{
+ int i;
+ const char *ret = p->buffer + seq_buf_used(&p->seq);
+
+ for (i = 0; i < buf_len; i++)
+ trace_seq_printf(p, "%u ", buf[i]);
+
+ trace_seq_putc(p, 0);
+
+ return ret;
+}
+#endif
+
TRACE_EVENT(sched_update_task_ravg,
TP_PROTO(struct task_struct *p, struct rq *rq, enum task_event evt,
@@ -288,13 +312,17 @@ TRACE_EVENT(sched_update_task_ravg,
__field( u64, rq_ps )
__field( u64, grp_cs )
__field( u64, grp_ps )
- __field( u64, grp_nt_cs )
- __field( u64, grp_nt_ps )
+ __field( u64, grp_nt_cs )
+ __field( u64, grp_nt_ps )
__field( u32, curr_window )
__field( u32, prev_window )
+ __dynamic_array(u32, curr_sum, nr_cpu_ids )
+ __dynamic_array(u32, prev_sum, nr_cpu_ids )
__field( u64, nt_cs )
__field( u64, nt_ps )
__field( u32, active_windows )
+ __field( u8, curr_top )
+ __field( u8, prev_top )
),
TP_fast_assign(
@@ -321,22 +349,30 @@ TRACE_EVENT(sched_update_task_ravg,
__entry->grp_nt_ps = cpu_time ? cpu_time->nt_prev_runnable_sum : 0;
__entry->curr_window = p->ravg.curr_window;
__entry->prev_window = p->ravg.prev_window;
+ __window_data(__get_dynamic_array(curr_sum), p->ravg.curr_window_cpu);
+ __window_data(__get_dynamic_array(prev_sum), p->ravg.prev_window_cpu);
__entry->nt_cs = rq->nt_curr_runnable_sum;
__entry->nt_ps = rq->nt_prev_runnable_sum;
__entry->active_windows = p->ravg.active_windows;
+ __entry->curr_top = rq->curr_top;
+ __entry->prev_top = rq->prev_top;
),
- TP_printk("wc %llu ws %llu delta %llu event %s cpu %d cur_freq %u cur_pid %d task %d (%s) ms %llu delta %llu demand %u sum %u irqtime %llu pred_demand %u rq_cs %llu rq_ps %llu cur_window %u prev_window %u nt_cs %llu nt_ps %llu active_wins %u grp_cs %lld grp_ps %lld, grp_nt_cs %llu, grp_nt_ps: %llu"
- , __entry->wallclock, __entry->win_start, __entry->delta,
+ TP_printk("wc %llu ws %llu delta %llu event %s cpu %d cur_freq %u cur_pid %d task %d (%s) ms %llu delta %llu demand %u sum %u irqtime %llu pred_demand %u rq_cs %llu rq_ps %llu cur_window %u (%s) prev_window %u (%s) nt_cs %llu nt_ps %llu active_wins %u grp_cs %lld grp_ps %lld, grp_nt_cs %llu, grp_nt_ps: %llu curr_top %u prev_top %u",
+ __entry->wallclock, __entry->win_start, __entry->delta,
task_event_names[__entry->evt], __entry->cpu,
__entry->cur_freq, __entry->cur_pid,
__entry->pid, __entry->comm, __entry->mark_start,
__entry->delta_m, __entry->demand,
__entry->sum, __entry->irqtime, __entry->pred_demand,
__entry->rq_cs, __entry->rq_ps, __entry->curr_window,
- __entry->prev_window, __entry->nt_cs, __entry->nt_ps,
+ __window_print(p, __get_dynamic_array(curr_sum), nr_cpu_ids),
+ __entry->prev_window,
+ __window_print(p, __get_dynamic_array(prev_sum), nr_cpu_ids),
+ __entry->nt_cs, __entry->nt_ps,
__entry->active_windows, __entry->grp_cs,
- __entry->grp_ps, __entry->grp_nt_cs, __entry->grp_nt_ps)
+ __entry->grp_ps, __entry->grp_nt_cs, __entry->grp_nt_ps,
+ __entry->curr_top, __entry->prev_top)
);
TRACE_EVENT(sched_get_task_cpu_cycles,
@@ -1287,6 +1323,21 @@ TRACE_EVENT(core_ctl_set_busy,
__entry->is_busy)
);
+TRACE_EVENT(core_ctl_set_boost,
+
+ TP_PROTO(u32 refcount, s32 ret),
+ TP_ARGS(refcount, ret),
+ TP_STRUCT__entry(
+ __field(u32, refcount)
+ __field(s32, ret)
+ ),
+ TP_fast_assign(
+ __entry->refcount = refcount;
+ __entry->ret = ret;
+ ),
+ TP_printk("refcount=%u, ret=%d", __entry->refcount, __entry->ret)
+);
+
/**
* sched_isolate - called when cores are isolated/unisolated
*
diff --git a/init/main.c b/init/main.c
index fbafa271531c..7d4532bff5da 100644
--- a/init/main.c
+++ b/init/main.c
@@ -505,11 +505,6 @@ asmlinkage __visible void __init start_kernel(void)
smp_setup_processor_id();
debug_objects_early_init();
- /*
- * Set up the the initial canary ASAP:
- */
- boot_init_stack_canary();
-
cgroup_init_early();
local_irq_disable();
@@ -523,6 +518,10 @@ asmlinkage __visible void __init start_kernel(void)
page_address_init();
pr_notice("%s", linux_banner);
setup_arch(&command_line);
+ /*
+ * Set up the the initial canary ASAP:
+ */
+ boot_init_stack_canary();
mm_init_cpumask(&init_mm);
setup_command_line(command_line);
setup_nr_cpu_ids();
diff --git a/kernel/fork.c b/kernel/fork.c
index e89d0bae6f20..8a5962276788 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1684,7 +1684,7 @@ struct task_struct *fork_idle(int cpu)
task = copy_process(CLONE_VM, 0, 0, NULL, &init_struct_pid, 0, 0);
if (!IS_ERR(task)) {
init_idle_pids(task->pids);
- init_idle(task, cpu);
+ init_idle(task, cpu, false);
}
return task;
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 024fb1007c78..53f7b50b7541 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -75,6 +75,7 @@
#include <linux/context_tracking.h>
#include <linux/compiler.h>
#include <linux/irq.h>
+#include <linux/sched/core_ctl.h>
#include <asm/switch_to.h>
#include <asm/tlb.h>
@@ -85,7 +86,6 @@
#endif
#include "sched.h"
-#include "core_ctl.h"
#include "../workqueue_internal.h"
#include "../smpboot.h"
@@ -2255,13 +2255,13 @@ void __dl_clear_params(struct task_struct *p)
void sched_exit(struct task_struct *p)
{
unsigned long flags;
- int cpu = get_cpu();
- struct rq *rq = cpu_rq(cpu);
+ struct rq *rq;
u64 wallclock;
sched_set_group_id(p, 0);
- raw_spin_lock_irqsave(&rq->lock, flags);
+ rq = task_rq_lock(p, &flags);
+
/* rq->curr == p */
wallclock = sched_ktime_clock();
update_task_ravg(rq->curr, rq, TASK_UPDATE, wallclock, 0);
@@ -2269,11 +2269,13 @@ void sched_exit(struct task_struct *p)
reset_task_stats(p);
p->ravg.mark_start = wallclock;
p->ravg.sum_history[0] = EXITING_TASK_MARKER;
+
+ kfree(p->ravg.curr_window_cpu);
+ kfree(p->ravg.prev_window_cpu);
+
enqueue_task(rq, p, 0);
clear_ed_task(p, rq);
- raw_spin_unlock_irqrestore(&rq->lock, flags);
-
- put_cpu();
+ task_rq_unlock(rq, p, &flags);
}
#endif /* CONFIG_SCHED_HMP */
@@ -2377,6 +2379,7 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p)
int cpu = get_cpu();
__sched_fork(clone_flags, p);
+ init_new_task_load(p, false);
/*
* We mark the process as running here. This guarantees that
* nobody will actually run it, and a signal or other external
@@ -2562,7 +2565,6 @@ void wake_up_new_task(struct task_struct *p)
struct rq *rq;
raw_spin_lock_irqsave(&p->pi_lock, flags);
- init_new_task_load(p);
add_new_task_to_grp(p);
/* Initialize new task's runnable average */
init_entity_runnable_average(&p->se);
@@ -5210,17 +5212,21 @@ void init_idle_bootup_task(struct task_struct *idle)
* init_idle - set up an idle thread for a given CPU
* @idle: task in question
* @cpu: cpu the idle task belongs to
+ * @cpu_up: differentiate between initial boot vs hotplug
*
* NOTE: this function does not set the idle thread's NEED_RESCHED
* flag, to make booting more robust.
*/
-void init_idle(struct task_struct *idle, int cpu)
+void init_idle(struct task_struct *idle, int cpu, bool cpu_up)
{
struct rq *rq = cpu_rq(cpu);
unsigned long flags;
__sched_fork(0, idle);
+ if (!cpu_up)
+ init_new_task_load(idle, true);
+
raw_spin_lock_irqsave(&idle->pi_lock, flags);
raw_spin_lock(&rq->lock);
@@ -8009,6 +8015,22 @@ void __init sched_init(void)
rq->old_estimated_time = 0;
rq->old_busy_time_group = 0;
rq->hmp_stats.pred_demands_sum = 0;
+ rq->curr_table = 0;
+ rq->prev_top = 0;
+ rq->curr_top = 0;
+
+ for (j = 0; j < NUM_TRACKED_WINDOWS; j++) {
+ memset(&rq->load_subs[j], 0,
+ sizeof(struct load_subtractions));
+
+ rq->top_tasks[j] = kcalloc(NUM_LOAD_INDICES,
+ sizeof(u8), GFP_NOWAIT);
+
+ /* No other choice */
+ BUG_ON(!rq->top_tasks[j]);
+
+ clear_top_tasks_bitmap(rq->top_tasks_bitmap[j]);
+ }
#endif
rq->max_idle_balance_cost = sysctl_sched_migration_cost;
@@ -8051,7 +8073,7 @@ void __init sched_init(void)
* but because we are the idle thread, we just pick up running again
* when this runqueue becomes "idle".
*/
- init_idle(current, smp_processor_id());
+ init_idle(current, smp_processor_id(), false);
calc_load_update = jiffies + LOAD_FREQ;
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
index d81886da7ca2..0db85a4fa9c8 100644
--- a/kernel/sched/core_ctl.c
+++ b/kernel/sched/core_ctl.c
@@ -45,7 +45,7 @@ struct cluster_data {
bool nrrun_changed;
struct task_struct *core_ctl_thread;
unsigned int first_cpu;
- bool boost;
+ unsigned int boost;
struct kobject kobj;
};
@@ -652,17 +652,40 @@ static bool do_check(u64 wallclock)
return do_check;
}
-void core_ctl_set_boost(bool boost)
+int core_ctl_set_boost(bool boost)
{
unsigned int index = 0;
struct cluster_data *cluster;
+ unsigned long flags;
+ int ret = 0;
+ bool boost_state_changed = false;
+ spin_lock_irqsave(&state_lock, flags);
for_each_cluster(cluster, index) {
- if (cluster->is_big_cluster && cluster->boost != boost) {
- cluster->boost = boost;
- apply_need(cluster);
+ if (cluster->is_big_cluster) {
+ if (boost) {
+ boost_state_changed = !cluster->boost;
+ ++cluster->boost;
+ } else {
+ if (!cluster->boost) {
+ pr_err("Error turning off boost. Boost already turned off\n");
+ ret = -EINVAL;
+ } else {
+ --cluster->boost;
+ boost_state_changed = !cluster->boost;
+ }
+ }
+ break;
}
}
+ spin_unlock_irqrestore(&state_lock, flags);
+
+ if (boost_state_changed)
+ apply_need(cluster);
+
+ trace_core_ctl_set_boost(cluster->boost, ret);
+
+ return ret;
}
void core_ctl_check(u64 wallclock)
diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
index b6dc131f36a6..c8c4272c61d8 100644
--- a/kernel/sched/debug.c
+++ b/kernel/sched/debug.c
@@ -418,6 +418,7 @@ static void sched_debug_header(struct seq_file *m)
P(min_capacity);
P(max_capacity);
P(sched_ravg_window);
+ P(sched_load_granule);
#endif
#undef PN
#undef P
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 98ae45174a40..e32d4d7903b0 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -3056,7 +3056,8 @@ bias_to_prev_cpu(struct cpu_select_env *env, struct cluster_cpu_stats *stats)
static inline bool
wake_to_waker_cluster(struct cpu_select_env *env)
{
- return !env->need_idle && !env->reason && env->sync &&
+ return env->boost_type == SCHED_BOOST_NONE &&
+ !env->need_idle && !env->reason && env->sync &&
task_load(current) > sched_big_waker_task_load &&
task_load(env->p) < sched_small_wakee_task_load;
}
diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c
index 3d5de8ba70a2..d220482f4dbc 100644
--- a/kernel/sched/hmp.c
+++ b/kernel/sched/hmp.c
@@ -18,9 +18,9 @@
#include <linux/list_sort.h>
#include <linux/syscore_ops.h>
#include <linux/of.h>
+#include <linux/sched/core_ctl.h>
#include "sched.h"
-#include "core_ctl.h"
#include <trace/events/sched.h>
@@ -201,7 +201,7 @@ int sched_update_freq_max_load(const cpumask_t *cpumask)
entry = &max_load->freqs[i];
freq = costs[i].freq;
hpct = get_freq_max_load(cpu, freq);
- if (hpct <= 0 && hpct > 100)
+ if (hpct <= 0 || hpct > 100)
hpct = 100;
hfreq = div64_u64((u64)freq * hpct, 100);
entry->hdemand =
@@ -590,6 +590,7 @@ static struct sched_cluster *alloc_new_cluster(const struct cpumask *cpus)
cluster->dstate_wakeup_latency = 0;
cluster->freq_init_done = false;
+ raw_spin_lock_init(&cluster->load_lock);
cluster->cpus = *cpus;
cluster->efficiency = arch_get_cpu_efficiency(cpumask_first(cpus));
@@ -647,6 +648,7 @@ void init_clusters(void)
{
bitmap_clear(all_cluster_ids, 0, NR_CPUS);
init_cluster.cpus = *cpu_possible_mask;
+ raw_spin_lock_init(&init_cluster.load_lock);
INIT_LIST_HEAD(&cluster_head);
}
@@ -788,6 +790,12 @@ __read_mostly unsigned int sysctl_sched_new_task_windows = 5;
#define SCHED_FREQ_ACCOUNT_WAIT_TIME 0
/*
+ * This governs what load needs to be used when reporting CPU busy time
+ * to the cpufreq governor.
+ */
+__read_mostly unsigned int sysctl_sched_freq_reporting_policy;
+
+/*
* For increase, send notification if
* freq_required - cur_freq > sysctl_sched_freq_inc_notify
*/
@@ -823,15 +831,15 @@ unsigned int max_possible_capacity = 1024; /* max(rq->max_possible_capacity) */
unsigned int
min_max_possible_capacity = 1024; /* min(rq->max_possible_capacity) */
-/* Window size (in ns) */
-__read_mostly unsigned int sched_ravg_window = 10000000;
-
/* Min window size (in ns) = 10ms */
#define MIN_SCHED_RAVG_WINDOW 10000000
/* Max window size (in ns) = 1s */
#define MAX_SCHED_RAVG_WINDOW 1000000000
+/* Window size (in ns) */
+__read_mostly unsigned int sched_ravg_window = MIN_SCHED_RAVG_WINDOW;
+
/* Temporarily disable window-stats activity on all cpus */
unsigned int __read_mostly sched_disable_window_stats;
@@ -851,6 +859,21 @@ static DEFINE_RWLOCK(related_thread_group_lock);
list_for_each_entry(grp, &related_thread_groups, list)
/*
+ * Task load is categorized into buckets for the purpose of top task tracking.
+ * The entire range of load from 0 to sched_ravg_window needs to be covered
+ * in NUM_LOAD_INDICES number of buckets. Therefore the size of each bucket
+ * is given by sched_ravg_window / NUM_LOAD_INDICES. Since the default value
+ * of sched_ravg_window is MIN_SCHED_RAVG_WINDOW, use that to compute
+ * sched_load_granule.
+ */
+__read_mostly unsigned int sched_load_granule =
+ MIN_SCHED_RAVG_WINDOW / NUM_LOAD_INDICES;
+
+/* Size of bitmaps maintained to track top tasks */
+static const unsigned int top_tasks_bitmap_size =
+ BITS_TO_LONGS(NUM_LOAD_INDICES + 1) * sizeof(unsigned long);
+
+/*
* Demand aggregation for frequency purpose:
*
* 'sched_freq_aggregate' controls aggregation of cpu demand of related threads
@@ -1505,7 +1528,7 @@ static inline int invalid_value(unsigned int *data)
/*
* Handle "atomic" update of sysctl_sched_window_stats_policy,
- * sysctl_sched_ravg_hist_size and sched_freq_legacy_mode variables.
+ * sysctl_sched_ravg_hist_size variables.
*/
int sched_window_update_handler(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp,
@@ -1611,7 +1634,7 @@ unsigned int cpu_temp(int cpu)
return 0;
}
-void init_new_task_load(struct task_struct *p)
+void init_new_task_load(struct task_struct *p, bool idle_task)
{
int i;
u32 init_load_windows = sched_init_task_load_windows;
@@ -1623,6 +1646,15 @@ void init_new_task_load(struct task_struct *p)
memset(&p->ravg, 0, sizeof(struct ravg));
p->cpu_cycles = 0;
+ p->ravg.curr_window_cpu = kcalloc(nr_cpu_ids, sizeof(u32), GFP_ATOMIC);
+ p->ravg.prev_window_cpu = kcalloc(nr_cpu_ids, sizeof(u32), GFP_ATOMIC);
+
+ /* Don't have much choice. CPU frequency would be bogus */
+ BUG_ON(!p->ravg.curr_window_cpu || !p->ravg.prev_window_cpu);
+
+ if (idle_task)
+ return;
+
if (init_load_pct)
init_load_windows = div64_u64((u64)init_load_pct *
(u64)sched_ravg_window, 100);
@@ -2161,6 +2193,174 @@ void update_task_pred_demand(struct rq *rq, struct task_struct *p, int event)
p->ravg.pred_demand = new;
}
+void clear_top_tasks_bitmap(unsigned long *bitmap)
+{
+ memset(bitmap, 0, top_tasks_bitmap_size);
+ __set_bit(NUM_LOAD_INDICES, bitmap);
+}
+
+/*
+ * Special case the last index and provide a fast path for index = 0.
+ * Note that sched_load_granule can change underneath us if we are not
+ * holding any runqueue locks while calling the two functions below.
+ */
+static u32 top_task_load(struct rq *rq)
+{
+ int index = rq->prev_top;
+ u8 prev = 1 - rq->curr_table;
+
+ if (!index) {
+ int msb = NUM_LOAD_INDICES - 1;
+
+ if (!test_bit(msb, rq->top_tasks_bitmap[prev]))
+ return 0;
+ else
+ return sched_load_granule;
+ } else if (index == NUM_LOAD_INDICES - 1) {
+ return sched_ravg_window;
+ } else {
+ return (index + 1) * sched_load_granule;
+ }
+}
+
+static int load_to_index(u32 load)
+{
+ if (load < sched_load_granule)
+ return 0;
+ else if (load >= sched_ravg_window)
+ return NUM_LOAD_INDICES - 1;
+ else
+ return load / sched_load_granule;
+}
+
+static void update_top_tasks(struct task_struct *p, struct rq *rq,
+ u32 old_curr_window, int new_window, bool full_window)
+{
+ u8 curr = rq->curr_table;
+ u8 prev = 1 - curr;
+ u8 *curr_table = rq->top_tasks[curr];
+ u8 *prev_table = rq->top_tasks[prev];
+ int old_index, new_index, update_index;
+ u32 curr_window = p->ravg.curr_window;
+ u32 prev_window = p->ravg.prev_window;
+ bool zero_index_update;
+
+ if (old_curr_window == curr_window && !new_window)
+ return;
+
+ old_index = load_to_index(old_curr_window);
+ new_index = load_to_index(curr_window);
+
+ if (!new_window) {
+ zero_index_update = !old_curr_window && curr_window;
+ if (old_index != new_index || zero_index_update) {
+ if (old_curr_window)
+ curr_table[old_index] -= 1;
+ if (curr_window)
+ curr_table[new_index] += 1;
+ if (new_index > rq->curr_top)
+ rq->curr_top = new_index;
+ }
+
+ if (!curr_table[old_index])
+ __clear_bit(NUM_LOAD_INDICES - old_index - 1,
+ rq->top_tasks_bitmap[curr]);
+
+ if (curr_table[new_index] == 1)
+ __set_bit(NUM_LOAD_INDICES - new_index - 1,
+ rq->top_tasks_bitmap[curr]);
+
+ return;
+ }
+
+ /*
+ * The window has rolled over for this task. By the time we get
+ * here, curr/prev swaps would has already occurred. So we need
+ * to use prev_window for the new index.
+ */
+ update_index = load_to_index(prev_window);
+
+ if (full_window) {
+ /*
+ * Two cases here. Either 'p' ran for the entire window or
+ * it didn't run at all. In either case there is no entry
+ * in the prev table. If 'p' ran the entire window, we just
+ * need to create a new entry in the prev table. In this case
+ * update_index will be correspond to sched_ravg_window
+ * so we can unconditionally update the top index.
+ */
+ if (prev_window) {
+ prev_table[update_index] += 1;
+ rq->prev_top = update_index;
+ }
+
+ if (prev_table[update_index] == 1)
+ __set_bit(NUM_LOAD_INDICES - update_index - 1,
+ rq->top_tasks_bitmap[prev]);
+ } else {
+ zero_index_update = !old_curr_window && prev_window;
+ if (old_index != update_index || zero_index_update) {
+ if (old_curr_window)
+ prev_table[old_index] -= 1;
+
+ prev_table[update_index] += 1;
+
+ if (update_index > rq->prev_top)
+ rq->prev_top = update_index;
+
+ if (!prev_table[old_index])
+ __clear_bit(NUM_LOAD_INDICES - old_index - 1,
+ rq->top_tasks_bitmap[prev]);
+
+ if (prev_table[update_index] == 1)
+ __set_bit(NUM_LOAD_INDICES - update_index - 1,
+ rq->top_tasks_bitmap[prev]);
+ }
+ }
+
+ if (curr_window) {
+ curr_table[new_index] += 1;
+
+ if (new_index > rq->curr_top)
+ rq->curr_top = new_index;
+
+ if (curr_table[new_index] == 1)
+ __set_bit(NUM_LOAD_INDICES - new_index - 1,
+ rq->top_tasks_bitmap[curr]);
+ }
+}
+
+static inline void clear_top_tasks_table(u8 *table)
+{
+ memset(table, 0, NUM_LOAD_INDICES * sizeof(u8));
+}
+
+static u32 empty_windows[NR_CPUS];
+
+static void rollover_task_window(struct task_struct *p, bool full_window)
+{
+ u32 *curr_cpu_windows = empty_windows;
+ u32 curr_window;
+ int i;
+
+ /* Rollover the sum */
+ curr_window = 0;
+
+ if (!full_window) {
+ curr_window = p->ravg.curr_window;
+ curr_cpu_windows = p->ravg.curr_window_cpu;
+ }
+
+ p->ravg.prev_window = curr_window;
+ p->ravg.curr_window = 0;
+
+ /* Roll over individual CPU contributions */
+ for (i = 0; i < nr_cpu_ids; i++) {
+ p->ravg.prev_window_cpu[i] = curr_cpu_windows[i];
+ p->ravg.curr_window_cpu[i] = 0;
+ }
+}
+
/*
* Account cpu activity in its busy time counters (rq->curr/prev_runnable_sum)
*/
@@ -2181,6 +2381,8 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
int prev_sum_reset = 0;
bool new_task;
struct related_thread_group *grp;
+ int cpu = rq->cpu;
+ u32 old_curr_window;
new_window = mark_start < window_start;
if (new_window) {
@@ -2240,57 +2442,43 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
* Handle per-task window rollover. We don't care about the idle
* task or exiting tasks.
*/
- if (new_window && !is_idle_task(p) && !exiting_task(p)) {
- u32 curr_window = 0;
+ if (!is_idle_task(p) && !exiting_task(p)) {
+ old_curr_window = p->ravg.curr_window;
- if (!full_window)
- curr_window = p->ravg.curr_window;
-
- p->ravg.prev_window = curr_window;
- p->ravg.curr_window = 0;
+ if (new_window)
+ rollover_task_window(p, full_window);
}
if (flip_counters) {
u64 curr_sum = *curr_runnable_sum;
u64 nt_curr_sum = *nt_curr_runnable_sum;
+ u8 curr_table = rq->curr_table;
+ u8 prev_table = 1 - curr_table;
+ int curr_top = rq->curr_top;
- if (prev_sum_reset)
+ clear_top_tasks_table(rq->top_tasks[prev_table]);
+ clear_top_tasks_bitmap(rq->top_tasks_bitmap[prev_table]);
+
+ if (prev_sum_reset) {
curr_sum = nt_curr_sum = 0;
+ curr_top = 0;
+ clear_top_tasks_table(rq->top_tasks[curr_table]);
+ clear_top_tasks_bitmap(
+ rq->top_tasks_bitmap[curr_table]);
+ }
*prev_runnable_sum = curr_sum;
*nt_prev_runnable_sum = nt_curr_sum;
*curr_runnable_sum = 0;
*nt_curr_runnable_sum = 0;
+ rq->curr_table = prev_table;
+ rq->prev_top = curr_top;
+ rq->curr_top = 0;
}
- if (!account_busy_for_cpu_time(rq, p, irqtime, event)) {
- /*
- * account_busy_for_cpu_time() = 0, so no update to the
- * task's current window needs to be made. This could be
- * for example
- *
- * - a wakeup event on a task within the current
- * window (!new_window below, no action required),
- * - switching to a new task from idle (PICK_NEXT_TASK)
- * in a new window where irqtime is 0 and we aren't
- * waiting on IO
- */
-
- if (!new_window)
- return;
-
- /*
- * A new window has started. The RQ demand must be rolled
- * over if p is the current task.
- */
- if (p_is_curr_task) {
- /* p is idle task */
- BUG_ON(p != rq->idle);
- }
-
- return;
- }
+ if (!account_busy_for_cpu_time(rq, p, irqtime, event))
+ goto done;
if (!new_window) {
/*
@@ -2310,10 +2498,12 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
if (new_task)
*nt_curr_runnable_sum += delta;
- if (!is_idle_task(p) && !exiting_task(p))
+ if (!is_idle_task(p) && !exiting_task(p)) {
p->ravg.curr_window += delta;
+ p->ravg.curr_window_cpu[cpu] += delta;
+ }
- return;
+ goto done;
}
if (!p_is_curr_task) {
@@ -2336,8 +2526,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
* contribution to previous completed window.
*/
delta = scale_exec_time(window_start - mark_start, rq);
- if (!exiting_task(p))
+ if (!exiting_task(p)) {
p->ravg.prev_window += delta;
+ p->ravg.prev_window_cpu[cpu] += delta;
+ }
} else {
/*
* Since at least one full window has elapsed,
@@ -2345,8 +2537,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
* full window (window_size).
*/
delta = scale_exec_time(window_size, rq);
- if (!exiting_task(p))
+ if (!exiting_task(p)) {
p->ravg.prev_window = delta;
+ p->ravg.prev_window_cpu[cpu] = delta;
+ }
}
*prev_runnable_sum += delta;
@@ -2359,10 +2553,12 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
if (new_task)
*nt_curr_runnable_sum += delta;
- if (!exiting_task(p))
+ if (!exiting_task(p)) {
p->ravg.curr_window = delta;
+ p->ravg.curr_window_cpu[cpu] = delta;
+ }
- return;
+ goto done;
}
if (!irqtime || !is_idle_task(p) || cpu_is_waiting_on_io(rq)) {
@@ -2386,8 +2582,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
* contribution to previous completed window.
*/
delta = scale_exec_time(window_start - mark_start, rq);
- if (!is_idle_task(p) && !exiting_task(p))
+ if (!is_idle_task(p) && !exiting_task(p)) {
p->ravg.prev_window += delta;
+ p->ravg.prev_window_cpu[cpu] += delta;
+ }
} else {
/*
* Since at least one full window has elapsed,
@@ -2395,8 +2593,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
* full window (window_size).
*/
delta = scale_exec_time(window_size, rq);
- if (!is_idle_task(p) && !exiting_task(p))
+ if (!is_idle_task(p) && !exiting_task(p)) {
p->ravg.prev_window = delta;
+ p->ravg.prev_window_cpu[cpu] = delta;
+ }
}
/*
@@ -2413,10 +2613,12 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
if (new_task)
*nt_curr_runnable_sum += delta;
- if (!is_idle_task(p) && !exiting_task(p))
+ if (!is_idle_task(p) && !exiting_task(p)) {
p->ravg.curr_window = delta;
+ p->ravg.curr_window_cpu[cpu] = delta;
+ }
- return;
+ goto done;
}
if (irqtime) {
@@ -2461,7 +2663,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
return;
}
- BUG();
+done:
+ if (!is_idle_task(p) && !exiting_task(p))
+ update_top_tasks(p, rq, old_curr_window,
+ new_window, full_window);
}
static inline u32 predict_and_update_buckets(struct rq *rq,
@@ -2829,11 +3034,23 @@ void sched_account_irqstart(int cpu, struct task_struct *curr, u64 wallclock)
void reset_task_stats(struct task_struct *p)
{
u32 sum = 0;
+ u32 *curr_window_ptr = NULL;
+ u32 *prev_window_ptr = NULL;
- if (exiting_task(p))
+ if (exiting_task(p)) {
sum = EXITING_TASK_MARKER;
+ } else {
+ curr_window_ptr = p->ravg.curr_window_cpu;
+ prev_window_ptr = p->ravg.prev_window_cpu;
+ memset(curr_window_ptr, 0, sizeof(u32) * nr_cpu_ids);
+ memset(prev_window_ptr, 0, sizeof(u32) * nr_cpu_ids);
+ }
memset(&p->ravg, 0, sizeof(struct ravg));
+
+ p->ravg.curr_window_cpu = curr_window_ptr;
+ p->ravg.prev_window_cpu = prev_window_ptr;
+
/* Retain EXITING_TASK marker */
p->ravg.sum_history[0] = sum;
}
@@ -2889,7 +3106,9 @@ static void reset_all_task_stats(void)
read_lock(&tasklist_lock);
do_each_thread(g, p) {
+ raw_spin_lock(&p->pi_lock);
reset_task_stats(p);
+ raw_spin_unlock(&p->pi_lock);
} while_each_thread(g, p);
read_unlock(&tasklist_lock);
}
@@ -2934,7 +3153,7 @@ const char *sched_window_reset_reasons[] = {
/* Called with IRQs enabled */
void reset_all_window_stats(u64 window_start, unsigned int window_size)
{
- int cpu;
+ int cpu, i;
unsigned long flags;
u64 start_ts = sched_ktime_clock();
int reason = WINDOW_CHANGE;
@@ -2968,6 +3187,7 @@ void reset_all_window_stats(u64 window_start, unsigned int window_size)
if (window_size) {
sched_ravg_window = window_size * TICK_NSEC;
set_hmp_defaults();
+ sched_load_granule = sched_ravg_window / NUM_LOAD_INDICES;
}
enable_window_stats();
@@ -2979,6 +3199,16 @@ void reset_all_window_stats(u64 window_start, unsigned int window_size)
rq->window_start = window_start;
rq->curr_runnable_sum = rq->prev_runnable_sum = 0;
rq->nt_curr_runnable_sum = rq->nt_prev_runnable_sum = 0;
+ for (i = 0; i < NUM_TRACKED_WINDOWS; i++) {
+ memset(&rq->load_subs[i], 0,
+ sizeof(struct load_subtractions));
+ clear_top_tasks_table(rq->top_tasks[i]);
+ clear_top_tasks_bitmap(rq->top_tasks_bitmap[i]);
+ }
+
+ rq->curr_table = 0;
+ rq->curr_top = 0;
+ rq->prev_top = 0;
reset_cpu_hmp_stats(cpu, 1);
}
@@ -3011,6 +3241,59 @@ void reset_all_window_stats(u64 window_start, unsigned int window_size)
sched_ktime_clock() - start_ts, reason, old, new);
}
+/*
+ * In this function we match the accumulated subtractions with the current
+ * and previous windows we are operating with. Ignore any entries where
+ * the window start in the load_subtraction struct does not match either
+ * the curent or the previous window. This could happen whenever CPUs
+ * become idle or busy with interrupts disabled for an extended period.
+ */
+static inline void account_load_subtractions(struct rq *rq)
+{
+ u64 ws = rq->window_start;
+ u64 prev_ws = ws - sched_ravg_window;
+ struct load_subtractions *ls = rq->load_subs;
+ int i;
+
+ for (i = 0; i < NUM_TRACKED_WINDOWS; i++) {
+ if (ls[i].window_start == ws) {
+ rq->curr_runnable_sum -= ls[i].subs;
+ rq->nt_curr_runnable_sum -= ls[i].new_subs;
+ } else if (ls[i].window_start == prev_ws) {
+ rq->prev_runnable_sum -= ls[i].subs;
+ rq->nt_prev_runnable_sum -= ls[i].new_subs;
+ }
+
+ ls[i].subs = 0;
+ ls[i].new_subs = 0;
+ }
+
+ BUG_ON((s64)rq->prev_runnable_sum < 0);
+ BUG_ON((s64)rq->curr_runnable_sum < 0);
+ BUG_ON((s64)rq->nt_prev_runnable_sum < 0);
+ BUG_ON((s64)rq->nt_curr_runnable_sum < 0);
+}
+
+static inline u64 freq_policy_load(struct rq *rq, u64 load)
+{
+ unsigned int reporting_policy = sysctl_sched_freq_reporting_policy;
+
+ switch (reporting_policy) {
+ case FREQ_REPORT_MAX_CPU_LOAD_TOP_TASK:
+ load = max_t(u64, load, top_task_load(rq));
+ break;
+ case FREQ_REPORT_TOP_TASK:
+ load = top_task_load(rq);
+ break;
+ case FREQ_REPORT_CPU_LOAD:
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+
+ return load;
+}
+
static inline void
sync_window_start(struct rq *rq, struct group_cpu_time *cpu_time);
@@ -3033,6 +3316,7 @@ void sched_get_cpus_busy(struct sched_load *busy,
struct related_thread_group *grp;
u64 total_group_load = 0, total_ngload = 0;
bool aggregate_load = false;
+ struct sched_cluster *cluster = cpu_cluster(cpumask_first(query_cpus));
if (unlikely(cpus == 0))
return;
@@ -3050,6 +3334,13 @@ void sched_get_cpus_busy(struct sched_load *busy,
window_size = sched_ravg_window;
+ /*
+ * We don't really need the cluster lock for this entire for loop
+ * block. However, there is no advantage in optimizing this as rq
+ * locks are held regardless and would prevent migration anyways
+ */
+ raw_spin_lock(&cluster->load_lock);
+
for_each_cpu(cpu, query_cpus) {
rq = cpu_rq(cpu);
@@ -3057,6 +3348,7 @@ void sched_get_cpus_busy(struct sched_load *busy,
0);
cur_freq[i] = cpu_cycles_to_freq(rq->cc.cycles, rq->cc.time);
+ account_load_subtractions(rq);
load[i] = rq->old_busy_time = rq->prev_runnable_sum;
nload[i] = rq->nt_prev_runnable_sum;
pload[i] = rq->hmp_stats.pred_demands_sum;
@@ -3083,6 +3375,8 @@ void sched_get_cpus_busy(struct sched_load *busy,
i++;
}
+ raw_spin_unlock(&cluster->load_lock);
+
for_each_related_thread_group(grp) {
for_each_cpu(cpu, query_cpus) {
/* Protected by rq_lock */
@@ -3117,6 +3411,8 @@ void sched_get_cpus_busy(struct sched_load *busy,
load[i] += group_load[i];
nload[i] += ngload[i];
+
+ load[i] = freq_policy_load(rq, load[i]);
/*
* Scale load in reference to cluster max_possible_freq.
*
@@ -3237,6 +3533,189 @@ int sched_set_window(u64 window_start, unsigned int window_size)
return 0;
}
+static inline void create_subtraction_entry(struct rq *rq, u64 ws, int index)
+{
+ rq->load_subs[index].window_start = ws;
+ rq->load_subs[index].subs = 0;
+ rq->load_subs[index].new_subs = 0;
+}
+
+static bool get_subtraction_index(struct rq *rq, u64 ws)
+{
+ int i;
+ u64 oldest = ULLONG_MAX;
+ int oldest_index = 0;
+
+ for (i = 0; i < NUM_TRACKED_WINDOWS; i++) {
+ u64 entry_ws = rq->load_subs[i].window_start;
+
+ if (ws == entry_ws)
+ return i;
+
+ if (entry_ws < oldest) {
+ oldest = entry_ws;
+ oldest_index = i;
+ }
+ }
+
+ create_subtraction_entry(rq, ws, oldest_index);
+ return oldest_index;
+}
+
+static void update_rq_load_subtractions(int index, struct rq *rq,
+ u32 sub_load, bool new_task)
+{
+ rq->load_subs[index].subs += sub_load;
+ if (new_task)
+ rq->load_subs[index].new_subs += sub_load;
+}
+
+static void update_cluster_load_subtractions(struct task_struct *p,
+ int cpu, u64 ws, bool new_task)
+{
+ struct sched_cluster *cluster = cpu_cluster(cpu);
+ struct cpumask cluster_cpus = cluster->cpus;
+ u64 prev_ws = ws - sched_ravg_window;
+ int i;
+
+ cpumask_clear_cpu(cpu, &cluster_cpus);
+ raw_spin_lock(&cluster->load_lock);
+
+ for_each_cpu(i, &cluster_cpus) {
+ struct rq *rq = cpu_rq(i);
+ int index;
+
+ if (p->ravg.curr_window_cpu[i]) {
+ index = get_subtraction_index(rq, ws);
+ update_rq_load_subtractions(index, rq,
+ p->ravg.curr_window_cpu[i], new_task);
+ p->ravg.curr_window_cpu[i] = 0;
+ }
+
+ if (p->ravg.prev_window_cpu[i]) {
+ index = get_subtraction_index(rq, prev_ws);
+ update_rq_load_subtractions(index, rq,
+ p->ravg.prev_window_cpu[i], new_task);
+ p->ravg.prev_window_cpu[i] = 0;
+ }
+ }
+
+ raw_spin_unlock(&cluster->load_lock);
+}
+
+static inline void inter_cluster_migration_fixup
+ (struct task_struct *p, int new_cpu, int task_cpu, bool new_task)
+{
+ struct rq *dest_rq = cpu_rq(new_cpu);
+ struct rq *src_rq = cpu_rq(task_cpu);
+
+ if (same_freq_domain(new_cpu, task_cpu))
+ return;
+
+ p->ravg.curr_window_cpu[new_cpu] = p->ravg.curr_window;
+ p->ravg.prev_window_cpu[new_cpu] = p->ravg.prev_window;
+
+ dest_rq->curr_runnable_sum += p->ravg.curr_window;
+ dest_rq->prev_runnable_sum += p->ravg.prev_window;
+
+ src_rq->curr_runnable_sum -= p->ravg.curr_window_cpu[task_cpu];
+ src_rq->prev_runnable_sum -= p->ravg.prev_window_cpu[task_cpu];
+
+ if (new_task) {
+ dest_rq->nt_curr_runnable_sum += p->ravg.curr_window;
+ dest_rq->nt_prev_runnable_sum += p->ravg.prev_window;
+
+ src_rq->nt_curr_runnable_sum -=
+ p->ravg.curr_window_cpu[task_cpu];
+ src_rq->nt_prev_runnable_sum -=
+ p->ravg.prev_window_cpu[task_cpu];
+ }
+
+ p->ravg.curr_window_cpu[task_cpu] = 0;
+ p->ravg.prev_window_cpu[task_cpu] = 0;
+
+ update_cluster_load_subtractions(p, task_cpu,
+ src_rq->window_start, new_task);
+
+ BUG_ON((s64)src_rq->prev_runnable_sum < 0);
+ BUG_ON((s64)src_rq->curr_runnable_sum < 0);
+ BUG_ON((s64)src_rq->nt_prev_runnable_sum < 0);
+ BUG_ON((s64)src_rq->nt_curr_runnable_sum < 0);
+}
+
+static int get_top_index(unsigned long *bitmap, unsigned long old_top)
+{
+ int index = find_next_bit(bitmap, NUM_LOAD_INDICES, old_top);
+
+ if (index == NUM_LOAD_INDICES)
+ return 0;
+
+ return NUM_LOAD_INDICES - 1 - index;
+}
+
+static void
+migrate_top_tasks(struct task_struct *p, struct rq *src_rq, struct rq *dst_rq)
+{
+ int index;
+ int top_index;
+ u32 curr_window = p->ravg.curr_window;
+ u32 prev_window = p->ravg.prev_window;
+ u8 src = src_rq->curr_table;
+ u8 dst = dst_rq->curr_table;
+ u8 *src_table;
+ u8 *dst_table;
+
+ if (curr_window) {
+ src_table = src_rq->top_tasks[src];
+ dst_table = dst_rq->top_tasks[dst];
+ index = load_to_index(curr_window);
+ src_table[index] -= 1;
+ dst_table[index] += 1;
+
+ if (!src_table[index])
+ __clear_bit(NUM_LOAD_INDICES - index - 1,
+ src_rq->top_tasks_bitmap[src]);
+
+ if (dst_table[index] == 1)
+ __set_bit(NUM_LOAD_INDICES - index - 1,
+ dst_rq->top_tasks_bitmap[dst]);
+
+ if (index > dst_rq->curr_top)
+ dst_rq->curr_top = index;
+
+ top_index = src_rq->curr_top;
+ if (index == top_index && !src_table[index])
+ src_rq->curr_top = get_top_index(
+ src_rq->top_tasks_bitmap[src], top_index);
+ }
+
+ if (prev_window) {
+ src = 1 - src;
+ dst = 1 - dst;
+ src_table = src_rq->top_tasks[src];
+ dst_table = dst_rq->top_tasks[dst];
+ index = load_to_index(prev_window);
+ src_table[index] -= 1;
+ dst_table[index] += 1;
+
+ if (!src_table[index])
+ __clear_bit(NUM_LOAD_INDICES - index - 1,
+ src_rq->top_tasks_bitmap[src]);
+
+ if (dst_table[index] == 1)
+ __set_bit(NUM_LOAD_INDICES - index - 1,
+ dst_rq->top_tasks_bitmap[dst]);
+
+ if (index > dst_rq->prev_top)
+ dst_rq->prev_top = index;
+
+ top_index = src_rq->prev_top;
+ if (index == top_index && !src_table[index])
+ src_rq->prev_top = get_top_index(
+ src_rq->top_tasks_bitmap[src], top_index);
+ }
+}
+
void fixup_busy_time(struct task_struct *p, int new_cpu)
{
struct rq *src_rq = task_rq(p);
@@ -3246,8 +3725,6 @@ void fixup_busy_time(struct task_struct *p, int new_cpu)
u64 *src_prev_runnable_sum, *dst_prev_runnable_sum;
u64 *src_nt_curr_runnable_sum, *dst_nt_curr_runnable_sum;
u64 *src_nt_prev_runnable_sum, *dst_nt_prev_runnable_sum;
- int migrate_type;
- struct migration_sum_data d;
bool new_task;
struct related_thread_group *grp;
@@ -3281,75 +3758,62 @@ void fixup_busy_time(struct task_struct *p, int new_cpu)
new_task = is_new_task(p);
/* Protected by rq_lock */
grp = p->grp;
+
+ /*
+ * For frequency aggregation, we continue to do migration fixups
+ * even for intra cluster migrations. This is because, the aggregated
+ * load has to reported on a single CPU regardless.
+ */
if (grp && sched_freq_aggregate) {
struct group_cpu_time *cpu_time;
- migrate_type = GROUP_TO_GROUP;
- /* Protected by rq_lock */
cpu_time = _group_cpu_time(grp, cpu_of(src_rq));
- d.src_rq = NULL;
- d.src_cpu_time = cpu_time;
src_curr_runnable_sum = &cpu_time->curr_runnable_sum;
src_prev_runnable_sum = &cpu_time->prev_runnable_sum;
src_nt_curr_runnable_sum = &cpu_time->nt_curr_runnable_sum;
src_nt_prev_runnable_sum = &cpu_time->nt_prev_runnable_sum;
- /* Protected by rq_lock */
cpu_time = _group_cpu_time(grp, cpu_of(dest_rq));
- d.dst_rq = NULL;
- d.dst_cpu_time = cpu_time;
dst_curr_runnable_sum = &cpu_time->curr_runnable_sum;
dst_prev_runnable_sum = &cpu_time->prev_runnable_sum;
dst_nt_curr_runnable_sum = &cpu_time->nt_curr_runnable_sum;
dst_nt_prev_runnable_sum = &cpu_time->nt_prev_runnable_sum;
sync_window_start(dest_rq, cpu_time);
- } else {
- migrate_type = RQ_TO_RQ;
- d.src_rq = src_rq;
- d.src_cpu_time = NULL;
- d.dst_rq = dest_rq;
- d.dst_cpu_time = NULL;
- src_curr_runnable_sum = &src_rq->curr_runnable_sum;
- src_prev_runnable_sum = &src_rq->prev_runnable_sum;
- src_nt_curr_runnable_sum = &src_rq->nt_curr_runnable_sum;
- src_nt_prev_runnable_sum = &src_rq->nt_prev_runnable_sum;
-
- dst_curr_runnable_sum = &dest_rq->curr_runnable_sum;
- dst_prev_runnable_sum = &dest_rq->prev_runnable_sum;
- dst_nt_curr_runnable_sum = &dest_rq->nt_curr_runnable_sum;
- dst_nt_prev_runnable_sum = &dest_rq->nt_prev_runnable_sum;
- }
- if (p->ravg.curr_window) {
- *src_curr_runnable_sum -= p->ravg.curr_window;
- *dst_curr_runnable_sum += p->ravg.curr_window;
- if (new_task) {
- *src_nt_curr_runnable_sum -= p->ravg.curr_window;
- *dst_nt_curr_runnable_sum += p->ravg.curr_window;
+ if (p->ravg.curr_window) {
+ *src_curr_runnable_sum -= p->ravg.curr_window;
+ *dst_curr_runnable_sum += p->ravg.curr_window;
+ if (new_task) {
+ *src_nt_curr_runnable_sum -=
+ p->ravg.curr_window;
+ *dst_nt_curr_runnable_sum +=
+ p->ravg.curr_window;
+ }
}
- }
- if (p->ravg.prev_window) {
- *src_prev_runnable_sum -= p->ravg.prev_window;
- *dst_prev_runnable_sum += p->ravg.prev_window;
- if (new_task) {
- *src_nt_prev_runnable_sum -= p->ravg.prev_window;
- *dst_nt_prev_runnable_sum += p->ravg.prev_window;
+ if (p->ravg.prev_window) {
+ *src_prev_runnable_sum -= p->ravg.prev_window;
+ *dst_prev_runnable_sum += p->ravg.prev_window;
+ if (new_task) {
+ *src_nt_prev_runnable_sum -=
+ p->ravg.prev_window;
+ *dst_nt_prev_runnable_sum +=
+ p->ravg.prev_window;
+ }
}
+ } else {
+ inter_cluster_migration_fixup(p, new_cpu,
+ task_cpu(p), new_task);
}
+ migrate_top_tasks(p, src_rq, dest_rq);
+
if (p == src_rq->ed_task) {
src_rq->ed_task = NULL;
if (!dest_rq->ed_task)
dest_rq->ed_task = p;
}
- trace_sched_migration_update_sum(p, migrate_type, &d);
- BUG_ON((s64)*src_prev_runnable_sum < 0);
- BUG_ON((s64)*src_curr_runnable_sum < 0);
- BUG_ON((s64)*src_nt_prev_runnable_sum < 0);
- BUG_ON((s64)*src_nt_curr_runnable_sum < 0);
-
done:
if (p->state == TASK_WAKING)
double_rq_unlock(src_rq, dest_rq);
@@ -3501,6 +3965,9 @@ static void transfer_busy_time(struct rq *rq, struct related_thread_group *grp,
u64 *src_nt_prev_runnable_sum, *dst_nt_prev_runnable_sum;
struct migration_sum_data d;
int migrate_type;
+ int cpu = cpu_of(rq);
+ bool new_task = is_new_task(p);
+ int i;
if (!sched_freq_aggregate)
return;
@@ -3511,7 +3978,7 @@ static void transfer_busy_time(struct rq *rq, struct related_thread_group *grp,
update_task_ravg(p, rq, TASK_UPDATE, wallclock, 0);
/* cpu_time protected by related_thread_group_lock, grp->lock rq_lock */
- cpu_time = _group_cpu_time(grp, cpu_of(rq));
+ cpu_time = _group_cpu_time(grp, cpu);
if (event == ADD_TASK) {
sync_window_start(rq, cpu_time);
migrate_type = RQ_TO_GROUP;
@@ -3528,6 +3995,19 @@ static void transfer_busy_time(struct rq *rq, struct related_thread_group *grp,
dst_nt_curr_runnable_sum = &cpu_time->nt_curr_runnable_sum;
src_nt_prev_runnable_sum = &rq->nt_prev_runnable_sum;
dst_nt_prev_runnable_sum = &cpu_time->nt_prev_runnable_sum;
+
+ *src_curr_runnable_sum -= p->ravg.curr_window_cpu[cpu];
+ *src_prev_runnable_sum -= p->ravg.prev_window_cpu[cpu];
+ if (new_task) {
+ *src_nt_curr_runnable_sum -=
+ p->ravg.curr_window_cpu[cpu];
+ *src_nt_prev_runnable_sum -=
+ p->ravg.prev_window_cpu[cpu];
+ }
+
+ update_cluster_load_subtractions(p, cpu,
+ rq->window_start, new_task);
+
} else {
migrate_type = GROUP_TO_RQ;
d.src_rq = NULL;
@@ -3550,21 +4030,42 @@ static void transfer_busy_time(struct rq *rq, struct related_thread_group *grp,
dst_nt_curr_runnable_sum = &rq->nt_curr_runnable_sum;
src_nt_prev_runnable_sum = &cpu_time->nt_prev_runnable_sum;
dst_nt_prev_runnable_sum = &rq->nt_prev_runnable_sum;
+
+ *src_curr_runnable_sum -= p->ravg.curr_window;
+ *src_prev_runnable_sum -= p->ravg.prev_window;
+ if (new_task) {
+ *src_nt_curr_runnable_sum -= p->ravg.curr_window;
+ *src_nt_prev_runnable_sum -= p->ravg.prev_window;
+ }
+
+ /*
+ * Need to reset curr/prev windows for all CPUs, not just the
+ * ones in the same cluster. Since inter cluster migrations
+ * did not result in the appropriate book keeping, the values
+ * per CPU would be inaccurate.
+ */
+ for_each_possible_cpu(i) {
+ p->ravg.curr_window_cpu[i] = 0;
+ p->ravg.prev_window_cpu[i] = 0;
+ }
}
- *src_curr_runnable_sum -= p->ravg.curr_window;
*dst_curr_runnable_sum += p->ravg.curr_window;
-
- *src_prev_runnable_sum -= p->ravg.prev_window;
*dst_prev_runnable_sum += p->ravg.prev_window;
-
- if (is_new_task(p)) {
- *src_nt_curr_runnable_sum -= p->ravg.curr_window;
+ if (new_task) {
*dst_nt_curr_runnable_sum += p->ravg.curr_window;
- *src_nt_prev_runnable_sum -= p->ravg.prev_window;
*dst_nt_prev_runnable_sum += p->ravg.prev_window;
}
+ /*
+ * When a task enter or exits a group, it's curr and prev windows are
+ * moved to a single CPU. This behavior might be sub-optimal in the
+ * exit case, however, it saves us the overhead of handling inter
+ * cluster migration fixups while the task is part of a related group.
+ */
+ p->ravg.curr_window_cpu[cpu] = p->ravg.curr_window;
+ p->ravg.prev_window_cpu[cpu] = p->ravg.prev_window;
+
trace_sched_migration_update_sum(p, migrate_type, &d);
BUG_ON((s64)*src_curr_runnable_sum < 0);
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 27b28369440d..471dc9faab35 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -351,13 +351,23 @@ struct cfs_bandwidth { };
#ifdef CONFIG_SCHED_HMP
+#define NUM_TRACKED_WINDOWS 2
+#define NUM_LOAD_INDICES 1000
+
struct hmp_sched_stats {
int nr_big_tasks;
u64 cumulative_runnable_avg;
u64 pred_demands_sum;
};
+struct load_subtractions {
+ u64 window_start;
+ u64 subs;
+ u64 new_subs;
+};
+
struct sched_cluster {
+ raw_spinlock_t load_lock;
struct list_head list;
struct cpumask cpus;
int id;
@@ -742,6 +752,13 @@ struct rq {
u64 prev_runnable_sum;
u64 nt_curr_runnable_sum;
u64 nt_prev_runnable_sum;
+ struct load_subtractions load_subs[NUM_TRACKED_WINDOWS];
+ DECLARE_BITMAP_ARRAY(top_tasks_bitmap,
+ NUM_TRACKED_WINDOWS, NUM_LOAD_INDICES);
+ u8 *top_tasks[NUM_TRACKED_WINDOWS];
+ u8 curr_table;
+ int prev_top;
+ int curr_top;
#endif
#ifdef CONFIG_IRQ_TIME_ACCOUNTING
@@ -1017,6 +1034,10 @@ static inline void sched_ttwu_pending(void) { }
#define WINDOW_STATS_AVG 3
#define WINDOW_STATS_INVALID_POLICY 4
+#define FREQ_REPORT_MAX_CPU_LOAD_TOP_TASK 0
+#define FREQ_REPORT_CPU_LOAD 1
+#define FREQ_REPORT_TOP_TASK 2
+
#define MAJOR_TASK_PCT 85
#define SCHED_UPMIGRATE_MIN_NICE 15
#define EXITING_TASK_MARKER 0xdeaddead
@@ -1056,8 +1077,9 @@ extern unsigned int __read_mostly sched_spill_load;
extern unsigned int __read_mostly sched_upmigrate;
extern unsigned int __read_mostly sched_downmigrate;
extern unsigned int __read_mostly sysctl_sched_spill_nr_run;
+extern unsigned int __read_mostly sched_load_granule;
-extern void init_new_task_load(struct task_struct *p);
+extern void init_new_task_load(struct task_struct *p, bool idle_task);
extern u64 sched_ktime_clock(void);
extern int got_boost_kick(void);
extern int register_cpu_cycle_counter_cb(struct cpu_cycle_counter_cb *cb);
@@ -1401,6 +1423,7 @@ extern int cpu_upmigrate_discourage_write_u64(struct cgroup_subsys_state *css,
struct cftype *cft, u64 upmigrate_discourage);
extern void sched_hmp_parse_dt(void);
extern void init_sched_hmp_boost_policy(void);
+extern void clear_top_tasks_bitmap(unsigned long *bitmap);
#else /* CONFIG_SCHED_HMP */
@@ -1503,7 +1526,9 @@ static inline struct sched_cluster *rq_cluster(struct rq *rq)
return NULL;
}
-static inline void init_new_task_load(struct task_struct *p) { }
+static inline void init_new_task_load(struct task_struct *p, bool idle_task)
+{
+}
static inline u64 scale_load_to_cpu(u64 load, int cpu)
{
@@ -1570,8 +1595,6 @@ static inline int update_preferred_cluster(struct related_thread_group *grp,
static inline void add_new_task_to_grp(struct task_struct *new) {}
#define sched_enable_hmp 0
-#define sched_freq_legacy_mode 1
-#define sched_migration_fixup 0
#define PRED_DEMAND_DELTA (0)
static inline void
diff --git a/kernel/smpboot.c b/kernel/smpboot.c
index 6949476a118f..3a0415803b09 100644
--- a/kernel/smpboot.c
+++ b/kernel/smpboot.c
@@ -32,7 +32,7 @@ struct task_struct *idle_thread_get(unsigned int cpu)
if (!tsk)
return ERR_PTR(-ENOMEM);
- init_idle(tsk, cpu);
+ init_idle(tsk, cpu, true);
return tsk;
}
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 587dbe09c47d..c72cb2053da7 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -297,6 +297,14 @@ static struct ctl_table kern_table[] = {
},
#ifdef CONFIG_SCHED_HMP
{
+ .procname = "sched_freq_reporting_policy",
+ .data = &sysctl_sched_freq_reporting_policy,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ },
+ {
.procname = "sched_freq_inc_notify",
.data = &sysctl_sched_freq_inc_notify,
.maxlen = sizeof(unsigned int),
diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c
index 0a9c283c250c..70be9c98b481 100644
--- a/sound/soc/codecs/wcd-spi.c
+++ b/sound/soc/codecs/wcd-spi.c
@@ -80,7 +80,9 @@
/* Word sizes and min/max lengths */
#define WCD_SPI_WORD_BYTE_CNT (4)
#define WCD_SPI_RW_MULTI_MIN_LEN (16)
-#define WCD_SPI_RW_MULTI_MAX_LEN (64 * 1024)
+
+/* Max size is closest multiple of 16 less than 64Kbytes */
+#define WCD_SPI_RW_MULTI_MAX_LEN ((64 * 1024) - 16)
/* Alignment requirements */
#define WCD_SPI_RW_MIN_ALIGN WCD_SPI_WORD_BYTE_CNT
@@ -104,6 +106,12 @@
mutex_unlock(&lock); \
}
+struct wcd_spi_debug_data {
+ struct dentry *dir;
+ u32 addr;
+ u32 size;
+};
+
struct wcd_spi_priv {
struct spi_device *spi;
u32 mem_base_addr;
@@ -133,6 +141,9 @@ struct wcd_spi_priv {
struct device *m_dev;
struct wdsp_mgr_ops *m_ops;
+
+ /* Debugfs related information */
+ struct wcd_spi_debug_data debug_data;
};
enum xfer_request {
@@ -642,11 +653,10 @@ static int wcd_spi_init(struct spi_device *spi)
regmap_write(wcd_spi->regmap, WCD_SPI_SLAVE_CONFIG,
0x0F3D0800);
- /* Write the MTU to 64K */
+ /* Write the MTU to max allowed size */
regmap_update_bits(wcd_spi->regmap,
WCD_SPI_SLAVE_TRNS_LEN,
- 0xFFFF0000,
- (WCD_SPI_RW_MULTI_MAX_LEN / 4) << 16);
+ 0xFFFF0000, 0xFFFF0000);
err_wr_en:
wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE,
WCD_SPI_CLK_FLAG_IMMEDIATE);
@@ -1012,20 +1022,81 @@ static const struct file_operations state_fops = {
.release = single_release,
};
+static ssize_t wcd_spi_debugfs_mem_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct spi_device *spi = file->private_data;
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data;
+ struct wcd_spi_msg msg;
+ ssize_t buf_size, read_count = 0;
+ char *buf;
+ int ret;
+
+ if (*ppos < 0 || !count)
+ return -EINVAL;
+
+ if (dbg_data->size == 0 || dbg_data->addr == 0) {
+ dev_err(&spi->dev,
+ "%s: Invalid request, size = %u, addr = 0x%x\n",
+ __func__, dbg_data->size, dbg_data->addr);
+ return 0;
+ }
+
+ buf_size = count < dbg_data->size ? count : dbg_data->size;
+ buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ msg.data = buf;
+ msg.remote_addr = dbg_data->addr;
+ msg.len = buf_size;
+ msg.flags = 0;
+
+ ret = wcd_spi_data_read(spi, &msg);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev,
+ "%s: Failed to read %zu bytes from addr 0x%x\n",
+ __func__, buf_size, msg.remote_addr);
+ goto done;
+ }
+
+ read_count = simple_read_from_buffer(ubuf, count, ppos, buf, buf_size);
+
+done:
+ kfree(buf);
+ if (ret < 0)
+ return ret;
+ else
+ return read_count;
+}
+
+static const struct file_operations mem_read_fops = {
+ .open = simple_open,
+ .read = wcd_spi_debugfs_mem_read,
+};
+
static int wcd_spi_debugfs_init(struct spi_device *spi)
{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data;
int rc = 0;
- struct dentry *dir;
- dir = debugfs_create_dir("wcd_spi", NULL);
- if (IS_ERR_OR_NULL(dir)) {
- dir = NULL;
+ dbg_data->dir = debugfs_create_dir("wcd_spi", NULL);
+ if (IS_ERR_OR_NULL(dbg_data->dir)) {
+ dbg_data->dir = NULL;
rc = -ENODEV;
goto done;
}
- debugfs_create_file("state", 0444, dir, spi, &state_fops);
+ debugfs_create_file("state", 0444, dbg_data->dir, spi, &state_fops);
+ debugfs_create_u32("addr", S_IRUGO | S_IWUSR, dbg_data->dir,
+ &dbg_data->addr);
+ debugfs_create_u32("size", S_IRUGO | S_IWUSR, dbg_data->dir,
+ &dbg_data->size);
+ debugfs_create_file("mem_read", S_IRUGO, dbg_data->dir,
+ spi, &mem_read_fops);
done:
return rc;
}
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index 9394ee52cad0..ed984496aec1 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -842,6 +842,9 @@ struct tasha_priv {
int rx_8_count;
bool clk_mode;
bool clk_internal;
+
+ /* Lock to protect mclk enablement */
+ struct mutex mclk_lock;
};
static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec,
@@ -1152,13 +1155,14 @@ static int tasha_cdc_req_mclk_enable(struct tasha_priv *tasha,
{
int ret = 0;
+ mutex_lock(&tasha->mclk_lock);
if (enable) {
tasha_cdc_sido_ccl_enable(tasha, true);
ret = clk_prepare_enable(tasha->wcd_ext_clk);
if (ret) {
dev_err(tasha->dev, "%s: ext clk enable failed\n",
__func__);
- goto err;
+ goto unlock_mutex;
}
/* get BG */
wcd_resmgr_enable_master_bias(tasha->resmgr);
@@ -1172,7 +1176,8 @@ static int tasha_cdc_req_mclk_enable(struct tasha_priv *tasha,
clk_disable_unprepare(tasha->wcd_ext_clk);
tasha_cdc_sido_ccl_enable(tasha, false);
}
-err:
+unlock_mutex:
+ mutex_unlock(&tasha->mclk_lock);
return ret;
}
@@ -10260,14 +10265,14 @@ static int tasha_codec_ec_buf_mux_enable(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x3B);
- snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x68, 0x28);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x08, 0x08);
snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0,
0x08, 0x08);
break;
case SND_SOC_DAPM_POST_PMD:
snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0,
0x08, 0x00);
- snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x68, 0x40);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x08, 0x00);
snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x00);
break;
}
@@ -14114,6 +14119,7 @@ static int tasha_probe(struct platform_device *pdev)
mutex_init(&tasha->swr_read_lock);
mutex_init(&tasha->swr_write_lock);
mutex_init(&tasha->swr_clk_lock);
+ mutex_init(&tasha->mclk_lock);
cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region),
GFP_KERNEL);
@@ -14199,6 +14205,7 @@ err_clk:
err_resmgr:
devm_kfree(&pdev->dev, cdc_pwr);
err_cdc_pwr:
+ mutex_destroy(&tasha->mclk_lock);
devm_kfree(&pdev->dev, tasha);
return ret;
}
@@ -14213,6 +14220,7 @@ static int tasha_remove(struct platform_device *pdev)
clk_put(tasha->wcd_ext_clk);
if (tasha->wcd_native_clk)
clk_put(tasha->wcd_native_clk);
+ mutex_destroy(&tasha->mclk_lock);
devm_kfree(&pdev->dev, tasha);
snd_soc_unregister_codec(&pdev->dev);
return 0;
diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c
index 30c23df444d6..a526e1afdd28 100644
--- a/sound/soc/codecs/wcd934x/wcd934x.c
+++ b/sound/soc/codecs/wcd934x/wcd934x.c
@@ -3602,7 +3602,7 @@ static void tavil_tx_hpf_corner_freq_callback(struct work_struct *work)
struct hpf_work *hpf_work;
struct tavil_priv *tavil;
struct snd_soc_codec *codec;
- u16 dec_cfg_reg, amic_reg;
+ u16 dec_cfg_reg, amic_reg, go_bit_reg;
u8 hpf_cut_off_freq;
int amic_n;
@@ -3613,6 +3613,7 @@ static void tavil_tx_hpf_corner_freq_callback(struct work_struct *work)
hpf_cut_off_freq = hpf_work->hpf_cut_off_freq;
dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * hpf_work->decimator;
+ go_bit_reg = dec_cfg_reg + 7;
dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n",
__func__, hpf_work->decimator, hpf_cut_off_freq);
@@ -3624,6 +3625,10 @@ static void tavil_tx_hpf_corner_freq_callback(struct work_struct *work)
}
snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK,
hpf_cut_off_freq << 5);
+ snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x02);
+ /* Minimum 1 clk cycle delay is required as per HW spec */
+ usleep_range(1000, 1010);
+ snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x00);
}
static void tavil_tx_mute_update_callback(struct work_struct *work)
@@ -3643,7 +3648,6 @@ static void tavil_tx_mute_update_callback(struct work_struct *work)
16 * tx_mute_dwork->decimator;
hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 +
16 * tx_mute_dwork->decimator;
- snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x01);
snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00);
}
@@ -3730,20 +3734,27 @@ static int tavil_codec_enable_dec(struct snd_soc_dapm_widget *w,
break;
}
}
+ /* Enable TX PGA Mute */
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) &
TX_HPF_CUT_OFF_FREQ_MASK) >> 5;
tavil->tx_hpf_work[decimator].hpf_cut_off_freq =
hpf_cut_off_freq;
- if (hpf_cut_off_freq != CF_MIN_3DB_150HZ)
+ if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
snd_soc_update_bits(codec, dec_cfg_reg,
TX_HPF_CUT_OFF_FREQ_MASK,
CF_MIN_3DB_150HZ << 5);
- /* Enable TX PGA Mute */
- snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10);
- break;
- case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x00);
+ snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x02);
+ /*
+ * Minimum 1 clk cycle delay is required as per
+ * HW spec.
+ */
+ usleep_range(1000, 1010);
+ snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x00);
+ }
/* schedule work queue to Remove Mute */
schedule_delayed_work(&tavil->tx_mute_dwork[decimator].dwork,
msecs_to_jiffies(tx_unmute_delay));
@@ -3760,10 +3771,20 @@ static int tavil_codec_enable_dec(struct snd_soc_dapm_widget *w,
snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10);
if (cancel_delayed_work_sync(
&tavil->tx_hpf_work[decimator].dwork)) {
- if (hpf_cut_off_freq != CF_MIN_3DB_150HZ)
+ if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
snd_soc_update_bits(codec, dec_cfg_reg,
TX_HPF_CUT_OFF_FREQ_MASK,
hpf_cut_off_freq << 5);
+ snd_soc_update_bits(codec, hpf_gate_reg,
+ 0x02, 0x02);
+ /*
+ * Minimum 1 clk cycle delay is required as per
+ * HW spec.
+ */
+ usleep_range(1000, 1010);
+ snd_soc_update_bits(codec, hpf_gate_reg,
+ 0x02, 0x00);
+ }
}
cancel_delayed_work_sync(
&tavil->tx_mute_dwork[decimator].dwork);
diff --git a/sound/soc/msm/msmcobalt.c b/sound/soc/msm/msmcobalt.c
index 524f737360d8..d672eac49442 100644
--- a/sound/soc/msm/msmcobalt.c
+++ b/sound/soc/msm/msmcobalt.c
@@ -4632,6 +4632,20 @@ static struct snd_soc_dai_link msm_tasha_fe_dai_links[] = {
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
+ /* CPE LSM EC PP direct dai-link */
+ {
+ .name = "CPE Listen service ECPP",
+ .stream_name = "CPE Listen Audio Service ECPP",
+ .cpu_dai_name = "CPE_LSM_NOHOST",
+ .platform_name = "msm-cpe-lsm.3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "tasha_cpe",
+ .codec_name = "tasha_codec",
+ },
};
static struct snd_soc_dai_link msm_tavil_fe_dai_links[] = {
diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c
index 8337d11bad12..3a108eba3b01 100644
--- a/sound/usb/usb_audio_qmi_svc.c
+++ b/sound/usb/usb_audio_qmi_svc.c
@@ -50,7 +50,7 @@
#define IOVA_XFER_RING_MAX (IOVA_XFER_BUF_BASE - PAGE_SIZE)
#define IOVA_XFER_BUF_MAX (0xfffff000 - PAGE_SIZE)
-#define MAX_XFER_BUFF_LEN (2 * PAGE_SIZE)
+#define MAX_XFER_BUFF_LEN (24 * PAGE_SIZE)
struct iova_info {
struct list_head list;