diff options
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; |
