diff options
83 files changed, 2732 insertions, 765 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_hang_detect.txt b/Documentation/devicetree/bindings/arm/msm/msm_hang_detect.txt index 7f23d9a3c6e8..1700d588fd46 100644 --- a/Documentation/devicetree/bindings/arm/msm/msm_hang_detect.txt +++ b/Documentation/devicetree/bindings/arm/msm/msm_hang_detect.txt @@ -25,6 +25,7 @@ The device tree parameters for the core hang detection are: Required properties: - compatible : "qcom,core-hang-detect" +- label: unique name used to created sysfs entry - qcom,threshold-arr : Array of APCS_ALIAS*_CORE_HANG_THRESHOLD register address for each core. diff --git a/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt b/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt index 02f21835f870..54ba2be39f0c 100644 --- a/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt +++ b/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt @@ -6,5 +6,49 @@ to the Host processor. The host processor reads the direction and number of steps over I2C and passes the data to the rest of the system. Required properties: + - compatible : should be "pixart,pat9125". + - reg : i2c slave address of the device. + - interrupt-parent : parent of interrupt. + - interrupts : interrupt to indicate motion of the rotating switch. - - compatible : should be "pixart,pat9125". +Optional properties: + - pixart,inverse-x : boolean, use this to invert the x data before sending it to input framework + - pixart,inverse-y : boolean, use this to invert the y data before sending it to input framework + - pixart,press-enabled : boolean, use this to enable detection of pressing the button + - pinctrl-names : This should be defined if a target uses pinctrl framework. + See "pinctrl" in Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt + It should specify the names of the configs that pinctrl can + install in driver. + Following are the pinctrl configs that can be installed: + "pmx_rot_switch_active" : Active configuration of pins, + it should specify active config + defined in pin groups of + interrupt gpio. + "pmx_rot_switch_suspend" : Disabled configuration of + pins, it should specify sleep + config defined in pin groups + of interrupt gpio. + "pmx_rot_switch_release" : Release configuration of + pins, it should specify release + config defined in pin groups of + interrupt gpio. + - pixart,irq-gpio : This should be defined if a target doesn't use pinctrl framework. + irq gpio, which is to provide interrupts to host, same as "interrupts" node. + +Required properties if 'pixart,press-enabled' DT property is defined: + - pixart,press-keycode : keycode to be sent when press is detected by the driver. + +Example: + pixart_pat9125@75 { + compatible = "pixart,pat9125"; + reg = <0x75>; + interrupt-parent = <&msm_gpio>; + interrupts = <98 0x2008>; + pixart,irq-gpio = <&msm_gpio 98 0x2008>; + pinctrl-names = "pmx_rot_switch_active", + "pmx_rot_switch_suspend", + "pmx_rot_switch_release"; + pinctrl-0 = <&pix_int_active>; + pinctrl-1 = <&pix_int_suspend>; + pinctrl-2 = <&pix_release>; + }; diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt index bd05c8bebfc8..8365762e520f 100644 --- a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt +++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt @@ -38,18 +38,28 @@ Optional properties: Default value is 4500000 uA. - qcom,rparasitic-uohm : Integer property for flash current predictive mitigation indicating parasitic component of battery resistance. Default value is 0 uOhm. -- qcom,lmh-ocv-threshold-uv : Required property for flash current preemptive mitigation. +- qcom,lmh-ocv-threshold-uv : Required property for flash current preemptive LMH mitigation. Default value is 3700000 uV. -- qcom,lmh-rbatt-threshold-uohm : Required property for flash current preemptive mitigation. +- qcom,lmh-rbatt-threshold-uohm : Required property for flash current preemptive LMH mitigation. Default value is 400000 uOhm. -- qcom,lmh-mitigation-sel : Optional property to configure flash current preemptive mitigation. +- qcom,lmh-mitigation-sel : Optional property to configure flash current preemptive LMH mitigation. Accepted values are: 0: MITIGATION_DISABLED 1: MITIGATION_BY_ILED_THRESHOLD 2: MITIGATION_BY_SW Default value is 2. -- qcom,lmh-level : Optional property to configure flash current preemptive mitigation. +- qcom,chgr-mitigation-sel : Optional property to configure flash current preemptive charger mitigation. + Accepted values are: + 0: MITIGATION_DISABLED + 1: MITIGATION_BY_ILED_THRESHOLD + 2: MITIGATION_BY_SW + Default value is 2. +- qcom,lmh-level : Optional property to configure flash current preemptive LMH mitigation. Accepted values are 0, 1, and 3. Default value is 0. +- qcom,iled-thrsh-ma : Optional property to configure the led current threshold at which HW + preemptive mitigation is triggered. Unit is mA. Default value is 1000. + Accepted values are in the range 0 - 3100, with steps of 100. + 0 disables autonomous HW mitigation. - qcom,thermal-derate-en : Boolean property to enable flash current thermal mitigation. - qcom,thermal-derate-current : Array of currrent limits for thermal mitigation. Required if qcom,thermal-derate-en is specified. Unit is mA. Format is diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt index 7841251c67fe..caabcd347a72 100644 --- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt +++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt @@ -216,6 +216,42 @@ First Level Node - FG Gen3 device Definition: Battery temperature delta interrupt threshold. Possible values are: 2, 4, 6 and 10. Unit is in Kelvin. +- qcom,hold-soc-while-full: + Usage: optional + Value type: <bool> + Definition: A boolean property that when defined holds SOC at 100% when + the battery is full. + +- qcom,ki-coeff-soc-dischg: + Usage: optional + Value type: <prop-encoded-array> + Definition: Array of monotonic SOC threshold values to change the ki + coefficient for medium discharge current during discharge. + This should be defined in the ascending order and in the + range of 0-100. Array limit is set to 3. + +- qcom,ki-coeff-med-dischg: + Usage: optional + Value type: <prop-encoded-array> + Definition: Array of ki coefficient values for medium discharge current + during discharge. These values will be applied when the + monotonic SOC goes below the SOC threshold specified under + qcom,ki-coeff-soc-dischg. Array limit is set to 3. This + property should be specified if qcom,ki-coeff-soc-dischg + is specified to make it fully functional. Value has no + unit. Allowed range is 0 to 62200 in micro units. + +- qcom,ki-coeff-hi-dischg: + Usage: optional + Value type: <prop-encoded-array> + Definition: Array of ki coefficient values for high discharge current + during discharge. These values will be applied when the + monotonic SOC goes below the SOC threshold specified under + qcom,ki-coeff-soc-dischg. Array limit is set to 3. This + property should be specified if qcom,ki-coeff-soc-dischg + is specified to make it fully functional. Value has no + unit. Allowed range is 0 to 62200 in micro units. + ========================================================== Second Level Nodes - Peripherals managed by FG Gen3 driver ========================================================== @@ -246,6 +282,9 @@ pmicobalt_fg: qpnp,fg { qcom,pmic-revid = <&pmicobalt_revid>; io-channels = <&pmicobalt_rradc 3>; io-channel-names = "rradc_batt_id"; + qcom,ki-coeff-soc-dischg = <30 60 90>; + qcom,ki-coeff-med-dischg = <800 1000 1400>; + qcom,ki-coeff-hi-dischg = <1200 1500 2100>; status = "okay"; qcom,fg-batt-soc@4000 { diff --git a/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt b/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt index f5c6651affea..cd1386512bd3 100644 --- a/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt +++ b/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt @@ -40,6 +40,9 @@ Optional properties: - vconn-supply: Regulator that enables VCONN source output. This will be supplied on the USB CC line that is not used for communication when Ra resistance is detected. +- qcom,vconn-uses-external-source: Indicates whether VCONN supply is sourced + from an external regulator. If omitted, then it is + assumed it is connected to VBUS. Example: qcom,qpnp-pdphy@1700 { diff --git a/arch/arm/boot/dts/qcom/msm-gdsc-falcon.dtsi b/arch/arm/boot/dts/qcom/msm-gdsc-falcon.dtsi new file mode 100644 index 000000000000..6550ddcad86c --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm-gdsc-falcon.dtsi @@ -0,0 +1,159 @@ +/* + * 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 + * 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. + */ + +&soc { + /* GCC GDSCs */ + gdsc_usb30: qcom,gdsc@10f004 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_usb30"; + reg = <0x10f004 0x4>; + status = "disabled"; + }; + + gdsc_ufs: qcom,gdsc@175004 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_ufs"; + reg = <0x175004 0x4>; + status = "disabled"; + }; + + gdsc_hlos1_vote_lpass_adsp: qcom,gdsc@17d034 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_hlos1_vote_lpass_adsp"; + reg = <0x17d034 0x4>; + qcom,no-status-check-on-disable; + qcom,gds-timeout = <500>; + status = "disabled"; + }; + + gdsc_hlos1_vote_turing_adsp: qcom,gdsc@17d04c { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_hlos1_vote_turing_adsp"; + reg = <0x17d04c 0x4>; + qcom,no-status-check-on-disable; + qcom,gds-timeout = <500>; + status = "disabled"; + }; + + gdsc_hlos2_vote_turing_adsp: qcom,gdsc@17e04c { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_hlos2_vote_turing_adsp"; + reg = <0x17e04c 0x4>; + qcom,no-status-check-on-disable; + qcom,gds-timeout = <500>; + status = "disabled"; + }; + + /* MMSS GDSCs */ + bimc_smmu_hw_ctrl: syscon@c8ce024 { + compatible = "syscon"; + reg = <0xc8ce024 0x4>; + }; + + gdsc_bimc_smmu: qcom,gdsc@c8ce020 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_bimc_smmu"; + reg = <0xc8ce020 0x4>; + hw-ctrl-addr = <&bimc_smmu_hw_ctrl>; + qcom,no-status-check-on-disable; + qcom,gds-timeout = <500>; + status = "disabled"; + }; + + gdsc_venus: qcom,gdsc@c8c1024 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_venus"; + reg = <0xc8c1024 0x4>; + status = "disabled"; + }; + + gdsc_venus_core0: qcom,gdsc@c8c1040 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_venus_core0"; + reg = <0xc8c1040 0x4>; + status = "disabled"; + }; + + gdsc_camss_top: qcom,gdsc@c8c34a0 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_camss_top"; + reg = <0xc8c34a0 0x4>; + status = "disabled"; + }; + + gdsc_vfe0: qcom,gdsc@c8c3664 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_vfe0"; + reg = <0xc8c3664 0x4>; + status = "disabled"; + }; + + gdsc_vfe1: qcom,gdsc@c8c3674 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_vfe1"; + reg = <0xc8c3674 0x4>; + status = "disabled"; + }; + + gdsc_cpp: qcom,gdsc@c8c36d4 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_cpp"; + reg = <0xc8c36d4 0x4>; + status = "disabled"; + }; + + gdsc_mdss: qcom,gdsc@c8c2304 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_mdss"; + reg = <0xc8c2304 0x4>; + status = "disabled"; + }; + + /* GPU GDSCs */ + gpu_cx_hw_ctrl: syscon@5066008 { + compatible = "syscon"; + reg = <0x5066008 0x4>; + }; + + gdsc_gpu_cx: qcom,gdsc@5066004 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_gpu_cx"; + reg = <0x5066004 0x4>; + hw-ctrl-addr = <&gpu_cx_hw_ctrl>; + qcom,no-status-check-on-disable; + qcom,gds-timeout = <2000>; + status = "disabled"; + }; + + /* GPU GX GDSCs */ + gpu_gx_domain_addr: syscon@5065130 { + compatible = "syscon"; + reg = <0x5065130 0x4>; + }; + + gpu_gx_sw_reset: syscon@5066090 { + compatible = "syscon"; + reg = <0x5066090 0x4>; + }; + + gdsc_gpu_gx: qcom,gdsc@5066094 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_gpu_gx"; + reg = <0x5066094 0x4>; + domain-addr = <&gpu_gx_domain_addr>; + sw-reset = <&gpu_gx_sw_reset>; + qcom,retain-periph; + qcom,reset-aon-logic; + status = "disabled"; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi index c028ea0eeab3..f8069856f3d8 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi @@ -61,7 +61,7 @@ 50000000 100000000 200000000>; qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104"; - cd-gpios = <&tlmm 95 0x1>; + cd-gpios = <&tlmm 95 0x0>; status = "ok"; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi index 9b791d6b7fb0..8016a3822a7f 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi @@ -669,11 +669,10 @@ qcom,load-freq-tbl = /* Encoders */ <1105920 533000000 0x55555555>, /* 4kx2304@30 */ /*TURBO*/ - < 979200 444000000 0x55555555>, /* 1080p@120,1440p@60, + <1036800 444000000 0x55555555>, /* 720p@240, 1080p@120,1440p@60, * UHD@30 */ /*NOMINAL*/ - < 939700 355200000 0x55555555>, /* 4kx2304@24 */ /*SVSL1*/ - < 489600 269330000 0x55555555>, /* 1080p@60, 2560x1440@30 */ - /* SVS */ + < 829440 355200000 0x55555555>, /* UHD/4096x2160@30 SVSL1 */ + < 489600 269330000 0x55555555>, /* 1080p@60 SVS */ < 432000 200000000 0x55555555>, /* 720p@120, 1080p@30 */ /* SVS2 */ @@ -685,7 +684,7 @@ <1675472 355200000 0xffffffff>, /* 4kx2304@44 */ /*SVSL1*/ <1105920 269330000 0xffffffff>, /* UHD/4k2304@30, 1080p@120 */ /* SVS */ - < 864000 200000000 0xffffffff>; /* 720p@240, 1080p@60 */ + < 829440 200000000 0xffffffff>; /* 720p@120, 1080p@60 */ /* SVS2 */ qcom,imem-ab-tbl = @@ -701,11 +700,11 @@ <1728000 1728000 2211840 0x3f00000c>, /* Encoder */ /* Load > Nominal, Nominal <-> Turbo Eg. 4kx2304@30 */ - <972000 972000 1105920 0x04000004>, + <1036800 1036800 1105920 0x04000004>, /* Load > SVSL1, SVSL1<-> Nominal Eg. 3840x2160@30 */ - <939700 939700 972000 0x04000004>, + < 829440 829440 1036800 0x04000004>, /* Load > SVS , SVS <-> SVSL1 Eg. 4kx2304@24 */ - <489600 489600 939700 0x04000004>; + < 489600 489600 829440 0x04000004>; qcom,dcvs-limit = /* Min Frame size, Min MBs/sec */ <32400 30>, /* Encoder 3840x2160@30 */ diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi index 818b213f355d..b21e2dcf8c1a 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi @@ -1135,13 +1135,21 @@ qcom,firmware-name = "ipa_fws"; }; - qcom,chd { + qcom,chd_silver { compatible = "qcom,core-hang-detect"; + label = "silver"; qcom,threshold-arr = <0x179880b0 0x179980b0 - 0x179a80b0 0x179b80b0 0x178880b0 0x178980b0 - 0x178a80b0 0x178b80b0>; + 0x179a80b0 0x179b80b0>; qcom,config-arr = <0x179880b8 0x179980b8 - 0x179a80b8 0x179b80b8 0x178880b8 0x178980b8 + 0x179a80b8 0x179b80b8>; + }; + + qcom,chd_gold { + compatible = "qcom,core-hang-detect"; + label = "gold"; + qcom,threshold-arr = <0x178880b0 0x178980b0 + 0x178a80b0 0x178b80b0>; + qcom,config-arr = <0x178880b8 0x178980b8 0x178a80b8 0x178b80b8>; }; @@ -2889,11 +2897,6 @@ vdd-3.3-ch0-supply = <&pmcobalt_l25_pin_ctrl>; qcom,vdd-0.8-cx-mx-config = <800000 800000>; qcom,vdd-3.3-ch0-config = <3104000 3312000>; - qcom,msm-bus,name = "msm-icnss"; - qcom,msm-bus,num-cases = <2>; - qcom,msm-bus,num-paths = <1>; - qcom,msm-bus,vectors-KBps = <81 10065 0 0>, - <81 10065 0 16000>; qcom,icnss-vadc = <&pmcobalt_vadc>; qcom,icnss-adc_tm = <&pmcobalt_adc_tm>; }; diff --git a/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi index bdbcd9d7b6f9..e5db2766c553 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi @@ -172,4 +172,27 @@ compatible = "qcom,smp2pgpio_test_smp2p_5_out"; gpios = <&smp2pgpio_smp2p_5_out 0 0>; }; + + /* ssr - inbound entry from lpass */ + smp2pgpio_ssr_smp2p_2_in: qcom,smp2pgpio-ssr-smp2p-2-in { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "slave-kernel"; + qcom,remote-pid = <2>; + qcom,is-inbound; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + /* ssr - outbound entry to lpass */ + smp2pgpio_ssr_smp2p_2_out: qcom,smp2pgpio-ssr-smp2p-2-out { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "master-kernel"; + qcom,remote-pid = <2>; + 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 df04d1a57683..2b2a201db8bc 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi @@ -670,12 +670,44 @@ <0 425 0>; /* CE11 */ qcom,wlan-msa-memory = <0x100000>; }; + + qcom,lpass@15700000 { + compatible = "qcom,pil-tz-generic"; + reg = <0x15700000 0x00100>; + interrupts = <0 162 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_LPASS_CLK>; + clock-names = "xo"; + qcom,proxy-clock-names = "xo"; + + qcom,pas-id = <1>; + qcom,proxy-timeout-ms = <10000>; + qcom,smem-id = <423>; + qcom,sysmon-id = <1>; + qcom,ssctl-instance-id = <0x14>; + qcom,firmware-name = "adsp"; + memory-region = <&adsp_fw_mem>; + + /* GPIO inputs from lpass */ + qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_2_in 0 0>; + qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_2_in 2 0>; + qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_2_in 1 0>; + qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_2_in 3 0>; + + /* GPIO output to lpass */ + qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>; + status = "ok"; + }; }; #include "msmfalcon-ion.dtsi" #include "msmfalcon-bus.dtsi" #include "msmfalcon-regulator.dtsi" -#include "msm-gdsc-cobalt.dtsi" +#include "msm-gdsc-falcon.dtsi" &gdsc_usb30 { clock-names = "core_clk"; @@ -699,6 +731,14 @@ status = "ok"; }; +&gdsc_hlos1_vote_turing_adsp { + status = "ok"; +}; + +&gdsc_hlos2_vote_turing_adsp { + status = "ok"; +}; + &gdsc_venus { status = "ok"; }; @@ -749,5 +789,6 @@ &gdsc_gpu_cx { status = "ok"; }; + #include "msm-pmfalcon.dtsi" #include "msm-pm2falcon.dtsi" diff --git a/arch/arm/boot/dts/qcom/msmtriton.dtsi b/arch/arm/boot/dts/qcom/msmtriton.dtsi index 71b4da9bb2d0..09bb5f081602 100644 --- a/arch/arm/boot/dts/qcom/msmtriton.dtsi +++ b/arch/arm/boot/dts/qcom/msmtriton.dtsi @@ -16,6 +16,7 @@ #include <dt-bindings/clock/qcom,mmcc-msmfalcon.h> #include <dt-bindings/clock/qcom,rpmcc.h> #include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h> / { model = "Qualcomm Technologies, Inc. MSMTRITON"; @@ -543,3 +544,78 @@ }; #include "msmtriton-ion.dtsi" +#include "msmfalcon-regulator.dtsi" +#include "msm-gdsc-falcon.dtsi" + +&gdsc_usb30 { + clock-names = "core_clk"; + clocks = <&clock_gcc GCC_USB30_MASTER_CLK>; + status = "ok"; +}; + +&gdsc_ufs { + status = "ok"; +}; + +&gdsc_bimc_smmu { + clock-names = "bus_clk"; + clocks = <&clock_mmss MMSS_BIMC_SMMU_AXI_CLK>; + proxy-supply = <&gdsc_bimc_smmu>; + qcom,proxy-consumer-enable; + status = "ok"; +}; + +&gdsc_hlos1_vote_lpass_adsp { + status = "ok"; +}; + +&gdsc_venus { + status = "ok"; +}; + +&gdsc_venus_core0 { + qcom,support-hw-trigger; + status = "ok"; +}; + +&gdsc_camss_top { + status = "ok"; +}; + +&gdsc_vfe0 { + parent-supply = <&gdsc_camss_top>; + status = "ok"; +}; + +&gdsc_vfe1 { + parent-supply = <&gdsc_camss_top>; + status = "ok"; +}; + +&gdsc_cpp { + parent-supply = <&gdsc_camss_top>; + status = "ok"; +}; + +&gdsc_mdss { + clock-names = "bus_clk", "rot_clk"; + clocks = <&clock_mmss MMSS_MDSS_AXI_CLK>, + <&clock_mmss MMSS_MDSS_ROT_CLK>; + proxy-supply = <&gdsc_mdss>; + qcom,proxy-consumer-enable; + status = "ok"; +}; + +&gdsc_gpu_gx { + clock-names = "bimc_core_clk", "core_clk", "core_root_clk"; + clocks = <&clock_gcc GCC_GPU_BIMC_GFX_CLK>, + <&clock_gfx GPUCC_GFX3D_CLK>, + <&clock_gfx GFX3D_CLK_SRC>; + qcom,force-enable-root-clk; + parent-supply = <&gfx_vreg_corner>; + status = "ok"; +}; + +&gdsc_gpu_cx { + status = "ok"; +}; diff --git a/arch/arm/configs/msmfalcon_defconfig b/arch/arm/configs/msmfalcon_defconfig index 64da50bb55b2..41572d54afcb 100644 --- a/arch/arm/configs/msmfalcon_defconfig +++ b/arch/arm/configs/msmfalcon_defconfig @@ -420,7 +420,7 @@ CONFIG_IPA3=y CONFIG_RMNET_IPA3=y CONFIG_GPIO_USB_DETECT=y CONFIG_USB_BAM=y -CONFIG_MSM_MDSS_PLL=y +CONFIG_QCOM_CLK_SMD_RPM=y CONFIG_REMOTE_SPINLOCK_MSM=y CONFIG_ARM_SMMU=y CONFIG_IOMMU_DEBUG=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 036d6aa5c062..799e43f09a11 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -13,6 +13,7 @@ CONFIG_LOG_CPU_MAX_BUF_SHIFT=17 CONFIG_CGROUP_FREEZER=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHEDTUNE=y CONFIG_RT_GROUP_SCHED=y CONFIG_SCHED_HMP=y CONFIG_SCHED_HMP_CSTATE_AWARE=y @@ -21,6 +22,7 @@ CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set # CONFIG_PID_NS is not set CONFIG_SCHED_AUTOGROUP=y +CONFIG_SCHED_TUNE=y CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set @@ -435,6 +437,7 @@ CONFIG_USB_CONFIGFS_F_MTP=y CONFIG_USB_CONFIGFS_F_PTP=y CONFIG_USB_CONFIGFS_F_ACC=y CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_MIDI=y CONFIG_USB_CONFIGFS_F_HID=y CONFIG_USB_CONFIGFS_F_DIAG=y CONFIG_USB_CONFIGFS_F_GSI=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 77f0129776a3..dfd658d815fe 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -13,6 +13,7 @@ CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHEDTUNE=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_SCHED_HMP=y @@ -21,6 +22,7 @@ CONFIG_SCHED_CORE_CTL=y CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set # CONFIG_PID_NS is not set +CONFIG_SCHED_TUNE=y CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set @@ -437,6 +439,7 @@ CONFIG_USB_CONFIGFS_F_MTP=y CONFIG_USB_CONFIGFS_F_PTP=y CONFIG_USB_CONFIGFS_F_ACC=y CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_MIDI=y CONFIG_USB_CONFIGFS_F_HID=y CONFIG_USB_CONFIGFS_F_DIAG=y CONFIG_USB_CONFIGFS_F_GSI=y diff --git a/arch/arm64/configs/msmfalcon-perf_defconfig b/arch/arm64/configs/msmfalcon-perf_defconfig index 1bc352704893..5d271cad0aad 100644 --- a/arch/arm64/configs/msmfalcon-perf_defconfig +++ b/arch/arm64/configs/msmfalcon-perf_defconfig @@ -473,6 +473,7 @@ CONFIG_RMNET_IPA3=y CONFIG_GPIO_USB_DETECT=y CONFIG_SEEMP_CORE=y CONFIG_USB_BAM=y +CONFIG_QCOM_CLK_SMD_RPM=y CONFIG_REMOTE_SPINLOCK_MSM=y CONFIG_IOMMU_IO_PGTABLE_FAST=y CONFIG_ARM_SMMU=y diff --git a/arch/arm64/configs/msmfalcon_defconfig b/arch/arm64/configs/msmfalcon_defconfig index 348c34a94119..707bc68c825f 100644 --- a/arch/arm64/configs/msmfalcon_defconfig +++ b/arch/arm64/configs/msmfalcon_defconfig @@ -483,6 +483,7 @@ CONFIG_RMNET_IPA3=y CONFIG_GPIO_USB_DETECT=y CONFIG_SEEMP_CORE=y CONFIG_USB_BAM=y +CONFIG_QCOM_CLK_SMD_RPM=y CONFIG_REMOTE_SPINLOCK_MSM=y CONFIG_IOMMU_IO_PGTABLE_FAST=y CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST=y diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index ed763c7e98fc..1c625764133d 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -547,6 +547,26 @@ void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate, EXPORT_SYMBOL_GPL(clk_hw_set_rate_range); /* + * Aggregate the rate of all child nodes which are enabled and exclude the + * child node which requests for clk_aggregate_rate. + */ +unsigned long clk_aggregate_rate(struct clk_hw *hw, + const struct clk_core *parent) +{ + struct clk_core *child; + unsigned long aggre_rate = 0; + + hlist_for_each_entry(child, &parent->children, child_node) { + if (child->enable_count && + strcmp(child->name, hw->init->name)) + aggre_rate = max(child->rate, aggre_rate); + } + + return aggre_rate; +} +EXPORT_SYMBOL_GPL(clk_aggregate_rate); + +/* * Helper for finding best parent to provide a given frequency. This can be used * directly as a determine_rate callback (e.g. for a mux), or from a more * complex clock that may combine a mux with other operations. diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 7ee0294e9dc7..adebefd63e71 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -11,7 +11,7 @@ clk-qcom-y += clk-regmap-divider.o clk-qcom-y += clk-regmap-mux.o clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o clk-qcom-y += clk-hfpll.o -clk-qcom-y += reset.o +clk-qcom-y += reset.o clk-voter.o clk-qcom-y += clk-dummy.o clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o gdsc-regulator.o diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index ac007ec667bb..612e7b37a8d0 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -29,6 +29,8 @@ #include <dt-bindings/clock/qcom,rpmcc.h> #include <dt-bindings/mfd/qcom-rpm.h> +#include "clk-voter.h" + #define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773 #define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370 #define QCOM_RPM_SMD_KEY_RATE 0x007a484b @@ -603,7 +605,7 @@ DEFINE_CLK_SMD_RPM(msmfalcon, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); DEFINE_CLK_SMD_RPM(msmfalcon, cnoc_periph_clk, cnoc_periph_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); DEFINE_CLK_SMD_RPM(msmfalcon, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); -DEFINE_CLK_SMD_RPM(msmfalcon, mmssnoc_axi_rpm_clk, mmssnoc_axi_rpm_a_clk, +DEFINE_CLK_SMD_RPM(msmfalcon, mmssnoc_axi_clk, mmssnoc_axi_a_clk, QCOM_SMD_RPM_MMAXI_CLK, 0); DEFINE_CLK_SMD_RPM(msmfalcon, ipa_clk, ipa_a_clk, QCOM_SMD_RPM_IPA_CLK, 0); DEFINE_CLK_SMD_RPM(msmfalcon, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0); @@ -624,6 +626,27 @@ DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msmfalcon, ln_bb_clk2_pin, ln_bb_clk2_pin_ao, 0x2); DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msmfalcon, ln_bb_clk3_pin, ln_bb_clk3_pin_ao, 0x3); +/* Voter clocks */ +static DEFINE_CLK_VOTER(bimc_msmbus_clk, bimc_clk, LONG_MAX); +static DEFINE_CLK_VOTER(bimc_msmbus_a_clk, bimc_a_clk, LONG_MAX); +static DEFINE_CLK_VOTER(cnoc_msmbus_clk, cnoc_clk, LONG_MAX); +static DEFINE_CLK_VOTER(cnoc_msmbus_a_clk, cnoc_a_clk, LONG_MAX); +static DEFINE_CLK_VOTER(snoc_msmbus_clk, snoc_clk, LONG_MAX); +static DEFINE_CLK_VOTER(snoc_msmbus_a_clk, snoc_a_clk, LONG_MAX); +static DEFINE_CLK_VOTER(cnoc_periph_keepalive_a_clk, cnoc_periph_a_clk, + LONG_MAX); +static DEFINE_CLK_VOTER(mcd_ce1_clk, ce1_clk, 85710000); +static DEFINE_CLK_VOTER(qcedev_ce1_clk, ce1_clk, 85710000); +static DEFINE_CLK_VOTER(qcrypto_ce1_clk, ce1_clk, 85710000); +static DEFINE_CLK_VOTER(qseecom_ce1_clk, ce1_clk, 85710000); +static DEFINE_CLK_VOTER(scm_ce1_clk, ce1_clk, 85710000); + +static DEFINE_CLK_BRANCH_VOTER(cxo_dwc3_clk, cxo); +static DEFINE_CLK_BRANCH_VOTER(cxo_lpm_clk, cxo); +static DEFINE_CLK_BRANCH_VOTER(cxo_otg_clk, cxo); +static DEFINE_CLK_BRANCH_VOTER(cxo_pil_lpass_clk, cxo); +static DEFINE_CLK_BRANCH_VOTER(cxo_pil_cdsp_clk, cxo); + static struct clk_hw *msmfalcon_clks[] = { [RPM_XO_CLK_SRC] = &msmfalcon_cxo.hw, [RPM_XO_A_CLK_SRC] = &msmfalcon_cxo_a.hw, @@ -639,8 +662,8 @@ static struct clk_hw *msmfalcon_clks[] = { [RPM_AGGR2_NOC_A_CLK] = &msmfalcon_aggre2_noc_a_clk.hw, [RPM_CNOC_CLK] = &msmfalcon_cnoc_clk.hw, [RPM_CNOC_A_CLK] = &msmfalcon_cnoc_a_clk.hw, - [RPM_MMAXI_CLK] = &msmfalcon_mmssnoc_axi_rpm_clk.hw, - [RPM_MMAXI_A_CLK] = &msmfalcon_mmssnoc_axi_rpm_a_clk.hw, + [RPM_MMAXI_CLK] = &msmfalcon_mmssnoc_axi_clk.hw, + [RPM_MMAXI_A_CLK] = &msmfalcon_mmssnoc_axi_a_clk.hw, [RPM_IPA_CLK] = &msmfalcon_ipa_clk.hw, [RPM_IPA_A_CLK] = &msmfalcon_ipa_a_clk.hw, [RPM_CE1_CLK] = &msmfalcon_ce1_clk.hw, @@ -661,6 +684,25 @@ static struct clk_hw *msmfalcon_clks[] = { [RPM_LN_BB_CLK3_PIN_AO] = &msmfalcon_ln_bb_clk3_pin_ao.hw, [RPM_CNOC_PERIPH_CLK] = &msmfalcon_cnoc_periph_clk.hw, [RPM_CNOC_PERIPH_A_CLK] = &msmfalcon_cnoc_periph_a_clk.hw, + + /* Voter Clocks */ + [BIMC_MSMBUS_CLK] = &bimc_msmbus_clk.hw, + [BIMC_MSMBUS_A_CLK] = &bimc_msmbus_a_clk.hw, + [CNOC_MSMBUS_CLK] = &cnoc_msmbus_clk.hw, + [CNOC_MSMBUS_A_CLK] = &cnoc_msmbus_a_clk.hw, + [MCD_CE1_CLK] = &mcd_ce1_clk.hw, + [QCEDEV_CE1_CLK] = &qcedev_ce1_clk.hw, + [QCRYPTO_CE1_CLK] = &qcrypto_ce1_clk.hw, + [QSEECOM_CE1_CLK] = &qseecom_ce1_clk.hw, + [SCM_CE1_CLK] = &scm_ce1_clk.hw, + [SNOC_MSMBUS_CLK] = &snoc_msmbus_clk.hw, + [SNOC_MSMBUS_A_CLK] = &snoc_msmbus_a_clk.hw, + [CXO_DWC3_CLK] = &cxo_dwc3_clk.hw, + [CXO_LPM_CLK] = &cxo_lpm_clk.hw, + [CXO_OTG_CLK] = &cxo_otg_clk.hw, + [CXO_PIL_LPASS_CLK] = &cxo_pil_lpass_clk.hw, + [CXO_PIL_CDSP_CLK] = &cxo_pil_cdsp_clk.hw, + [CNOC_PERIPH_KEEPALIVE_A_CLK] = &cnoc_periph_keepalive_a_clk.hw, }; static const struct rpm_smd_clk_desc rpm_clk_msmfalcon = { @@ -757,9 +799,14 @@ static int rpm_smd_clk_probe(struct platform_device *pdev) /* Keep an active vote on CXO in case no other driver votes for it */ if (is_8996) clk_prepare_enable(msm8996_cxo_a.hw.clk); - else if (is_falcon) + else if (is_falcon) { clk_prepare_enable(msmfalcon_cxo_a.hw.clk); + /* Hold an active set vote for the cnoc_periph resource */ + clk_set_rate(cnoc_periph_keepalive_a_clk.hw.clk, 19200000); + clk_prepare_enable(cnoc_periph_keepalive_a_clk.hw.clk); + } + dev_info(&pdev->dev, "Registered RPM clocks\n"); return 0; diff --git a/drivers/clk/qcom/clk-voter.c b/drivers/clk/qcom/clk-voter.c new file mode 100644 index 000000000000..d3409b9e6b64 --- /dev/null +++ b/drivers/clk/qcom/clk-voter.c @@ -0,0 +1,133 @@ +/* + * 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 + * 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/clk.h> + +#include "clk-voter.h" + +static int voter_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int ret = 0; + struct clk_voter *v = to_clk_voter(hw); + unsigned long cur_rate, new_rate, other_rate = 0; + + if (v->is_branch) + return ret; + + if (v->enabled) { + struct clk_hw *parent = clk_hw_get_parent(hw); + + /* + * Get the aggregate rate without this clock's vote and update + * if the new rate is different than the current rate. + */ + other_rate = clk_aggregate_rate(hw, parent->core); + + cur_rate = max(other_rate, clk_get_rate(hw->clk)); + new_rate = max(other_rate, rate); + + if (new_rate != cur_rate) { + ret = clk_set_rate(parent->clk, new_rate); + if (ret) + return ret; + } + } + v->rate = rate; + + return ret; +} + +static int voter_clk_prepare(struct clk_hw *hw) +{ + int ret = 0; + unsigned long cur_rate; + struct clk_hw *parent; + struct clk_voter *v = to_clk_voter(hw); + + parent = clk_hw_get_parent(hw); + + if (v->is_branch) { + v->enabled = true; + return ret; + } + + /* + * Increase the rate if this clock is voting for a higher rate + * than the current rate. + */ + cur_rate = clk_aggregate_rate(hw, parent->core); + + if (v->rate > cur_rate) { + ret = clk_set_rate(parent->clk, v->rate); + if (ret) + return ret; + } + v->enabled = true; + + return ret; +} + +static void voter_clk_unprepare(struct clk_hw *hw) +{ + unsigned long cur_rate, new_rate; + struct clk_hw *parent; + struct clk_voter *v = to_clk_voter(hw); + + + parent = clk_hw_get_parent(hw); + + /* + * Decrease the rate if this clock was the only one voting for + * the highest rate. + */ + v->enabled = false; + if (v->is_branch) + return; + + new_rate = clk_aggregate_rate(hw, parent->core); + cur_rate = max(new_rate, v->rate); + + if (new_rate < cur_rate) + clk_set_rate(parent->clk, new_rate); +} + +static int voter_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_voter *v = to_clk_voter(hw); + + return v->enabled; +} + +static long voter_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return clk_hw_round_rate(clk_hw_get_parent(hw), rate); +} + +static unsigned long voter_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_voter *v = to_clk_voter(hw); + + return v->rate; +} + +struct clk_ops clk_ops_voter = { + .prepare = voter_clk_prepare, + .unprepare = voter_clk_unprepare, + .set_rate = voter_clk_set_rate, + .is_enabled = voter_clk_is_enabled, + .round_rate = voter_clk_round_rate, + .recalc_rate = voter_clk_recalc_rate, +}; diff --git a/drivers/clk/qcom/clk-voter.h b/drivers/clk/qcom/clk-voter.h new file mode 100644 index 000000000000..27092ae7d131 --- /dev/null +++ b/drivers/clk/qcom/clk-voter.h @@ -0,0 +1,50 @@ +/* + * 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 + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __QCOM_CLK_VOTER_H__ +#define __QCOM_CLK_VOTER_H__ + +#include <linux/clk-provider.h> +#include <linux/platform_device.h> + +struct clk_voter { + int is_branch; + bool enabled; + struct clk_hw hw; + unsigned long rate; +}; + +extern struct clk_ops clk_ops_voter; + +#define to_clk_voter(_hw) container_of(_hw, struct clk_voter, hw) + +#define __DEFINE_CLK_VOTER(clk_name, _parent_name, _default_rate, _is_branch) \ + struct clk_voter clk_name = { \ + .is_branch = (_is_branch), \ + .rate = _default_rate, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_ops_voter, \ + .name = #clk_name, \ + .parent_names = (const char *[]){ #_parent_name }, \ + .num_parents = 1, \ + }, \ + } + +#define DEFINE_CLK_VOTER(clk_name, _parent_name, _default_rate) \ + __DEFINE_CLK_VOTER(clk_name, _parent_name, _default_rate, 0) + +#define DEFINE_CLK_BRANCH_VOTER(clk_name, _parent_name) \ + __DEFINE_CLK_VOTER(clk_name, _parent_name, 1000, 1) + +#endif diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index f7c226ab4307..423e975dffee 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -177,7 +177,7 @@ EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk); int qcom_cc_really_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc, struct regmap *regmap) { - int i, ret; + int i = 0, ret, j = 0; struct device *dev = &pdev->dev; struct clk *clk; struct clk_onecell_data *data; @@ -187,8 +187,10 @@ int qcom_cc_really_probe(struct platform_device *pdev, struct gdsc_desc *scd; size_t num_clks = desc->num_clks; struct clk_regmap **rclks = desc->clks; + struct clk_hw **hw_clks = desc->hwclks; - cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, + cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * + (num_clks + desc->num_hwclks), GFP_KERNEL); if (!cc) return -ENOMEM; @@ -196,17 +198,32 @@ int qcom_cc_really_probe(struct platform_device *pdev, clks = cc->clks; data = &cc->data; data->clks = clks; - data->clk_num = num_clks; + data->clk_num = num_clks + desc->num_hwclks; - for (i = 0; i < num_clks; i++) { - if (!rclks[i]) { + for (i = 0; i < desc->num_hwclks; i++) { + if (!hw_clks[i]) { clks[i] = ERR_PTR(-ENOENT); continue; } - clk = devm_clk_register_regmap(dev, rclks[i]); + clk = devm_clk_register(dev, hw_clks[i]); if (IS_ERR(clk)) return PTR_ERR(clk); clks[i] = clk; + pr_debug("Index for hw_clocks %d added %s\n", i, + __clk_get_name(clk)); + } + + for (j = i; j < num_clks; j++) { + if (!rclks[j]) { + clks[j] = ERR_PTR(-ENOENT); + continue; + } + clk = devm_clk_register_regmap(dev, rclks[j]); + if (IS_ERR(clk)) + return PTR_ERR(clk); + clks[j] = clk; + pr_debug("Index for Regmap clocks %d added %s\n", j, + __clk_get_name(clk)); } ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 10cabca921be..e3f450533470 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -25,7 +25,9 @@ struct parent_map; struct qcom_cc_desc { const struct regmap_config *config; struct clk_regmap **clks; + struct clk_hw **hwclks; size_t num_clks; + size_t num_hwclks; const struct qcom_reset_map *resets; size_t num_resets; struct gdsc **gdscs; diff --git a/drivers/clk/qcom/gcc-msmfalcon.c b/drivers/clk/qcom/gcc-msmfalcon.c index d353cc9ade73..2cbc9dff047b 100644 --- a/drivers/clk/qcom/gcc-msmfalcon.c +++ b/drivers/clk/qcom/gcc-msmfalcon.c @@ -2527,6 +2527,32 @@ static struct clk_branch hlos1_vote_lpass_adsp_smmu_clk = { }, }; +static struct clk_branch hlos1_vote_turing_adsp_smmu_clk = { + .halt_reg = 0x7d048, + .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE, + .clkr = { + .enable_reg = 0x7d048, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "hlos1_vote_turing_adsp_smmu_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch hlos2_vote_turing_adsp_smmu_clk = { + .halt_reg = 0x7e048, + .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE, + .clkr = { + .enable_reg = 0x7e048, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "hlos2_vote_turing_adsp_smmu_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_fixed_factor gcc_ce1_ahb_m_clk = { .hw.init = &(struct clk_init_data){ .name = "gcc_ce1_ahb_m_clk", @@ -2683,6 +2709,10 @@ static struct clk_regmap *gcc_falcon_clocks[] = { [GCC_UFS_ICE_CORE_HW_CTL_CLK] = &gcc_ufs_ice_core_hw_ctl_clk.clkr, [GCC_UFS_PHY_AUX_HW_CTL_CLK] = &gcc_ufs_phy_aux_hw_ctl_clk.clkr, [GCC_UFS_UNIPRO_CORE_HW_CTL_CLK] = &gcc_ufs_unipro_core_hw_ctl_clk.clkr, + [HLOS1_VOTE_TURING_ADSP_SMMU_CLK] = + &hlos1_vote_turing_adsp_smmu_clk.clkr, + [HLOS2_VOTE_TURING_ADSP_SMMU_CLK] = + &hlos2_vote_turing_adsp_smmu_clk.clkr, }; static const struct qcom_reset_map gcc_falcon_resets[] = { @@ -2709,6 +2739,8 @@ static const struct qcom_cc_desc gcc_falcon_desc = { .config = &gcc_falcon_regmap_config, .clks = gcc_falcon_clocks, .num_clks = ARRAY_SIZE(gcc_falcon_clocks), + .hwclks = gcc_msmfalcon_hws, + .num_hwclks = ARRAY_SIZE(gcc_msmfalcon_hws), .resets = gcc_falcon_resets, .num_resets = ARRAY_SIZE(gcc_falcon_resets), }; @@ -2735,13 +2767,6 @@ static int gcc_falcon_probe(struct platform_device *pdev) */ regmap_update_bits(regmap, 0x52008, BIT(21), BIT(21)); - /* register hardware clocks */ - for (i = 0; i < ARRAY_SIZE(gcc_msmfalcon_hws); i++) { - clk = devm_clk_register(&pdev->dev, gcc_msmfalcon_hws[i]); - if (IS_ERR(clk)) - return PTR_ERR(clk); - } - vdd_dig.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_dig"); if (IS_ERR(vdd_dig.regulator[0])) { if (!(PTR_ERR(vdd_dig.regulator[0]) == -EPROBE_DEFER)) diff --git a/drivers/devfreq/governor_memlat.c b/drivers/devfreq/governor_memlat.c index 010f9defe33e..a3c826e152e1 100644 --- a/drivers/devfreq/governor_memlat.c +++ b/drivers/devfreq/governor_memlat.c @@ -81,6 +81,29 @@ show_attr(__attr) \ store_attr(__attr, min, max) \ static DEVICE_ATTR(__attr, 0644, show_##__attr, store_##__attr) +static ssize_t show_map(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct devfreq *df = to_devfreq(dev); + struct memlat_node *n = df->data; + struct core_dev_map *map = n->hw->freq_map; + unsigned int cnt = 0; + + cnt += snprintf(buf, PAGE_SIZE, "Core freq (MHz)\tDevice BW\n"); + + while (map->core_mhz && cnt < PAGE_SIZE) { + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "%15u\t%9u\n", + map->core_mhz, map->target_freq); + map++; + } + if (cnt < PAGE_SIZE) + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\n"); + + return cnt; +} + +static DEVICE_ATTR(freq_map, 0444, show_map, NULL); + static unsigned long core_to_dev_freq(struct memlat_node *node, unsigned long coref) { @@ -247,6 +270,7 @@ gov_attr(ratio_ceil, 1U, 10000U); static struct attribute *dev_attr[] = { &dev_attr_ratio_ceil.attr, + &dev_attr_freq_map.attr, NULL, }; diff --git a/drivers/gpu/msm/adreno_a5xx_snapshot.c b/drivers/gpu/msm/adreno_a5xx_snapshot.c index aeffeab2f6dc..c09d2f8c1947 100644 --- a/drivers/gpu/msm/adreno_a5xx_snapshot.c +++ b/drivers/gpu/msm/adreno_a5xx_snapshot.c @@ -410,8 +410,6 @@ static const unsigned int a5xx_registers[] = { 0xEA80, 0xEA80, 0xEA82, 0xEAA3, 0xEAA5, 0xEAC2, /* GPMU */ 0xA800, 0xA8FF, 0xAC60, 0xAC60, - /* DPM */ - 0xB000, 0xB97F, 0xB9A0, 0xB9BF, }; struct a5xx_hlsq_sp_tp_regs { diff --git a/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c index e5edaf5f908d..ac4caa48312d 100644 --- a/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c +++ b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c @@ -11,13 +11,21 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/of_gpio.h> +#include <linux/delay.h> #include "pixart_ots.h" struct pixart_pat9125_data { struct i2c_client *client; struct input_dev *input; int irq_gpio; - u32 irq_flags; + u32 press_keycode; + bool press_en; + bool inverse_x; + bool inverse_y; + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; }; static int pat9125_i2c_write(struct i2c_client *client, u8 reg, u8 *data, @@ -70,7 +78,7 @@ static int pat9125_i2c_read(struct i2c_client *client, u8 reg, u8 *data) return ret; } -unsigned char read_data(struct i2c_client *client, u8 addr) +u8 read_data(struct i2c_client *client, u8 addr) { u8 data = 0xff; @@ -83,8 +91,55 @@ void write_data(struct i2c_client *client, u8 addr, u8 data) pat9125_i2c_write(client, addr, &data, 1); } -static irqreturn_t pixart_pat9125_irq(int irq, void *data) +static irqreturn_t pat9125_irq(int irq, void *dev_data) { + u8 delta_x = 0, delta_y = 0, motion; + struct pixart_pat9125_data *data = dev_data; + struct input_dev *ipdev = data->input; + struct device *dev = &data->client->dev; + + motion = read_data(data->client, PIXART_PAT9125_MOTION_STATUS_REG); + do { + /* check if MOTION bit is set or not */ + if (motion & PIXART_PAT9125_VALID_MOTION_DATA) { + delta_x = read_data(data->client, + PIXART_PAT9125_DELTA_X_LO_REG); + delta_y = read_data(data->client, + PIXART_PAT9125_DELTA_Y_LO_REG); + + /* Inverse x depending upon the device orientation */ + delta_x = (data->inverse_x) ? -delta_x : delta_x; + /* Inverse y depending upon the device orientation */ + delta_y = (data->inverse_y) ? -delta_y : delta_y; + } + + dev_dbg(dev, "motion = %x, delta_x = %x, delta_y = %x\n", + motion, delta_x, delta_y); + + if (delta_x != 0) { + /* Send delta_x as REL_WHEEL for rotation */ + input_report_rel(ipdev, REL_WHEEL, (s8) delta_x); + input_sync(ipdev); + } + + if (data->press_en && delta_y != 0) { + if ((s8) delta_y > 0) { + /* Send DOWN event for press keycode */ + input_report_key(ipdev, data->press_keycode, 1); + input_sync(ipdev); + } else { + /* Send UP event for press keycode */ + input_report_key(ipdev, data->press_keycode, 0); + input_sync(ipdev); + } + } + usleep_range(PIXART_SAMPLING_PERIOD_US_MIN, + PIXART_SAMPLING_PERIOD_US_MAX); + + motion = read_data(data->client, + PIXART_PAT9125_MOTION_STATUS_REG); + } while (motion & PIXART_PAT9125_VALID_MOTION_DATA); + return IRQ_HANDLED; } @@ -145,13 +200,77 @@ static struct attribute_group pat9125_attr_grp = { .attrs = pat9125_attr_list, }; +static int pixart_pinctrl_init(struct pixart_pat9125_data *data) +{ + int err; + struct device *dev = &data->client->dev; + + data->pinctrl = devm_pinctrl_get(&(data->client->dev)); + if (IS_ERR_OR_NULL(data->pinctrl)) { + err = PTR_ERR(data->pinctrl); + dev_err(dev, "Target does not use pinctrl %d\n", err); + return err; + } + + data->pinctrl_state_active = pinctrl_lookup_state(data->pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(data->pinctrl_state_active)) { + err = PTR_ERR(data->pinctrl_state_active); + dev_err(dev, "Can not lookup active pinctrl state %d\n", err); + return err; + } + + data->pinctrl_state_suspend = pinctrl_lookup_state(data->pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(data->pinctrl_state_suspend)) { + err = PTR_ERR(data->pinctrl_state_suspend); + dev_err(dev, "Can not lookup suspend pinctrl state %d\n", err); + return err; + } + + data->pinctrl_state_release = pinctrl_lookup_state(data->pinctrl, + PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { + err = PTR_ERR(data->pinctrl_state_release); + dev_err(dev, "Can not lookup release pinctrl state %d\n", err); + return err; + } + return 0; +} + +static int pat9125_parse_dt(struct device *dev, + struct pixart_pat9125_data *data) +{ + struct device_node *np = dev->of_node; + u32 temp_val; + int ret; + + data->inverse_x = of_property_read_bool(np, "pixart,inverse-x"); + data->inverse_y = of_property_read_bool(np, "pixart,inverse-y"); + data->press_en = of_property_read_bool(np, "pixart,press-enabled"); + if (data->press_en) { + ret = of_property_read_u32(np, "pixart,press-keycode", + &temp_val); + if (!ret) { + data->press_keycode = temp_val; + } else { + dev_err(dev, "Unable to parse press-keycode\n"); + return ret; + } + } + + data->irq_gpio = of_get_named_gpio_flags(np, "pixart,irq-gpio", + 0, NULL); + + return 0; +} + static int pat9125_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int err = 0; struct pixart_pat9125_data *data; struct input_dev *input; - struct device_node *np; struct device *dev = &client->dev; err = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE); @@ -165,6 +284,11 @@ static int pat9125_i2c_probe(struct i2c_client *client, GFP_KERNEL); if (!data) return -ENOMEM; + err = pat9125_parse_dt(dev, data); + if (err) { + dev_err(dev, "DT parsing failed, errno:%d\n", err); + return err; + } } else { data = client->dev.platform_data; if (!data) { @@ -180,6 +304,10 @@ static int pat9125_i2c_probe(struct i2c_client *client, return -ENOMEM; } + input_set_capability(input, EV_REL, REL_WHEEL); + if (data->press_en) + input_set_capability(input, EV_KEY, data->press_keycode); + i2c_set_clientdata(client, data); input_set_drvdata(input, data); input->name = PAT9125_DEV_NAME; @@ -188,24 +316,39 @@ static int pat9125_i2c_probe(struct i2c_client *client, err = input_register_device(data->input); if (err < 0) { dev_err(dev, "Failed to register input device\n"); - goto err_register_input_device; - } - - if (!gpio_is_valid(data->irq_gpio)) { - dev_err(dev, "invalid irq_gpio: %d\n", data->irq_gpio); - return -EINVAL; - } - - err = gpio_request(data->irq_gpio, "pixart_pat9125_irq_gpio"); - if (err) { - dev_err(dev, "unable to request gpio %d\n", data->irq_gpio); return err; } - err = gpio_direction_input(data->irq_gpio); - if (err) { - dev_err(dev, "unable to set dir for gpio %d\n", data->irq_gpio); - goto free_gpio; + err = pixart_pinctrl_init(data); + if (!err && data->pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is found + * let pins to be configured in active state. If not + * found continue further without error. + */ + err = pinctrl_select_state(data->pinctrl, + data->pinctrl_state_active); + if (err < 0) + dev_err(dev, "Could not set pin to active state %d\n", + err); + } else { + if (gpio_is_valid(data->irq_gpio)) { + err = devm_gpio_request(dev, data->irq_gpio, + "pixart_pat9125_irq_gpio"); + if (err) { + dev_err(dev, "Couldn't request gpio %d\n", err); + return err; + } + err = gpio_direction_input(data->irq_gpio); + if (err) { + dev_err(dev, "Couldn't set dir for gpio %d\n", + err); + return err; + } + } else { + dev_err(dev, "Invalid gpio %d\n", data->irq_gpio); + return -EINVAL; + } } if (!ots_sensor_init(client)) { @@ -213,8 +356,8 @@ static int pat9125_i2c_probe(struct i2c_client *client, goto err_sensor_init; } - err = devm_request_threaded_irq(dev, client->irq, NULL, - pixart_pat9125_irq, (unsigned long)data->irq_flags, + err = devm_request_threaded_irq(dev, client->irq, NULL, pat9125_irq, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW, "pixart_pat9125_irq", data); if (err) { dev_err(dev, "Req irq %d failed, errno:%d\n", client->irq, err); @@ -232,33 +375,59 @@ static int pat9125_i2c_probe(struct i2c_client *client, err_sysfs_create: err_request_threaded_irq: err_sensor_init: -free_gpio: - gpio_free(data->irq_gpio); -err_register_input_device: - input_free_device(data->input); + 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"); + return err; } static int pat9125_i2c_remove(struct i2c_client *client) { struct pixart_pat9125_data *data = i2c_get_clientdata(client); + struct device *dev = &data->client->dev; - devm_free_irq(&client->dev, client->irq, data); - if (gpio_is_valid(data->irq_gpio)) - gpio_free(data->irq_gpio); - input_unregister_device(data->input); - devm_kfree(&client->dev, data); - data = NULL; + 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"); return 0; } static int pat9125_suspend(struct device *dev) { + int rc; + struct pixart_pat9125_data *data = + (struct pixart_pat9125_data *)dev->driver_data; + + disable_irq(data->client->irq); + if (data->pinctrl) { + rc = pinctrl_select_state(data->pinctrl, + data->pinctrl_state_suspend); + if (rc < 0) + dev_err(dev, "Could not set pin to suspend state %d\n", + rc); + } + return 0; } static int pat9125_resume(struct device *dev) { + int rc; + struct pixart_pat9125_data *data = + (struct pixart_pat9125_data *)dev->driver_data; + + if (data->pinctrl) { + rc = pinctrl_select_state(data->pinctrl, + data->pinctrl_state_active); + if (rc < 0) + dev_err(dev, "Could not set pin to active state %d\n", + rc); + } + enable_irq(data->client->irq); + return 0; } diff --git a/drivers/input/misc/ots_pat9125/pixart_ots.h b/drivers/input/misc/ots_pat9125/pixart_ots.h index a66ded5c9d08..824d6bafd9bf 100644 --- a/drivers/input/misc/ots_pat9125/pixart_ots.h +++ b/drivers/input/misc/ots_pat9125/pixart_ots.h @@ -10,6 +10,9 @@ #define PAT9125_DEV_NAME "pixart_pat9125" #define MAX_BUF_SIZE 20 #define RESET_DELAY_US 1000 +#define PINCTRL_STATE_ACTIVE "pmx_rot_switch_active" +#define PINCTRL_STATE_SUSPEND "pmx_rot_switch_suspend" +#define PINCTRL_STATE_RELEASE "pmx_rot_switch_release" /* Register addresses */ #define PIXART_PAT9125_PRODUCT_ID1_REG 0x00 @@ -39,6 +42,9 @@ #define PIXART_PAT9125_LOW_VOLTAGE_SEGMENT 0x04 #define PIXART_PAT9125_VALID_MOTION_DATA 0x80 +#define PIXART_SAMPLING_PERIOD_US_MIN 4000 +#define PIXART_SAMPLING_PERIOD_US_MAX 8000 + /* Export functions */ bool ots_sensor_init(struct i2c_client *); diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 5e47e2481300..3333f15f7f16 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -363,7 +363,7 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, else pte |= ARM_LPAE_PTE_TYPE_BLOCK; - pte |= ARM_LPAE_PTE_AF | ARM_LPAE_PTE_SH_IS; + pte |= ARM_LPAE_PTE_AF | ARM_LPAE_PTE_SH_OS; pte |= pfn_to_iopte(paddr >> data->pg_shift, data); *ptep = pte; @@ -481,11 +481,9 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data, pte |= (prot & IOMMU_PRIV) ? ARM_LPAE_PTE_AP_PRIV_RO : ARM_LPAE_PTE_AP_RO; - if (prot & IOMMU_CACHE) { + if (prot & IOMMU_CACHE) pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE << ARM_LPAE_PTE_ATTRINDX_SHIFT); - pte |= ARM_LPAE_PTE_SH_OS; - } if (prot & IOMMU_DEVICE) pte |= (ARM_LPAE_MAIR_ATTR_IDX_DEV << @@ -942,7 +940,7 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_IRGN0_SHIFT) | (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT); else - reg = (ARM_LPAE_TCR_SH_IS << ARM_LPAE_TCR_SH0_SHIFT) | + reg = (ARM_LPAE_TCR_SH_OS << ARM_LPAE_TCR_SH0_SHIFT) | (ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_IRGN0_SHIFT) | (ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_ORGN0_SHIFT); diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c index 0cae5d2e5263..bc94dff08d21 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -48,6 +48,7 @@ #define FLASH_LED_REG_THERMAL_THRSH3(base) (base + 0x58) #define FLASH_LED_REG_VPH_DROOP_THRESHOLD(base) (base + 0x61) #define FLASH_LED_REG_VPH_DROOP_DEBOUNCE(base) (base + 0x62) +#define FLASH_LED_REG_ILED_GRT_THRSH(base) (base + 0x67) #define FLASH_LED_REG_MITIGATION_SEL(base) (base + 0x6E) #define FLASH_LED_REG_MITIGATION_SW(base) (base + 0x6F) #define FLASH_LED_REG_LMH_LEVEL(base) (base + 0x70) @@ -62,22 +63,25 @@ #define FLASH_LED_ISC_WARMUP_DELAY_MASK GENMASK(1, 0) #define FLASH_LED_CURRENT_DERATE_EN_MASK GENMASK(2, 0) #define FLASH_LED_VPH_DROOP_DEBOUNCE_MASK GENMASK(1, 0) +#define FLASH_LED_CHGR_MITIGATION_SEL_MASK GENMASK(5, 4) #define FLASH_LED_LMH_MITIGATION_SEL_MASK GENMASK(1, 0) +#define FLASH_LED_ILED_GRT_THRSH_MASK GENMASK(5, 0) #define FLASH_LED_LMH_LEVEL_MASK GENMASK(1, 0) #define FLASH_LED_VPH_DROOP_HYSTERESIS_MASK GENMASK(5, 4) #define FLASH_LED_VPH_DROOP_THRESHOLD_MASK GENMASK(2, 0) #define FLASH_LED_THERMAL_THRSH_MASK GENMASK(2, 0) #define FLASH_LED_THERMAL_OTST_MASK GENMASK(2, 0) -#define FLASH_LED_PREPARE_OPTIONS_MASK GENMASK(2, 0) #define FLASH_LED_MOD_CTRL_MASK BIT(7) #define FLASH_LED_HW_SW_STROBE_SEL_MASK BIT(2) #define FLASH_LED_VPH_DROOP_FAULT_MASK BIT(4) #define FLASH_LED_LMH_MITIGATION_EN_MASK BIT(0) +#define FLASH_LED_CHGR_MITIGATION_EN_MASK BIT(4) #define VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us) (val_us / 8) #define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25) #define VPH_DROOP_THRESH_MV_TO_VAL(val_mv) ((val_mv / 100) - 25) #define VPH_DROOP_THRESH_VAL_TO_UV(val) ((val + 25) * 100000) +#define MITIGATION_THRSH_MA_TO_VAL(val_ma) (val_ma / 100) #define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6 #define FLASH_LED_WARMUP_DELAY_DEFAULT 2 @@ -99,8 +103,13 @@ #define FLASH_LED_LMH_LEVEL_DEFAULT 0 #define FLASH_LED_LMH_MITIGATION_ENABLE 1 #define FLASH_LED_LMH_MITIGATION_DISABLE 0 -#define FLASH_LED_LMH_MITIGATION_SEL_DEFAULT 2 -#define FLASH_LED_LMH_MITIGATION_SEL_MAX 2 +#define FLASH_LED_CHGR_MITIGATION_ENABLE BIT(4) +#define FLASH_LED_CHGR_MITIGATION_DISABLE 0 +#define FLASH_LED_MITIGATION_SEL_DEFAULT 2 +#define FLASH_LED_MITIGATION_SEL_MAX 2 +#define FLASH_LED_CHGR_MITIGATION_SEL_SHIFT 4 +#define FLASH_LED_MITIGATION_THRSH_DEFAULT 0xA +#define FLASH_LED_MITIGATION_THRSH_MAX 0x1F #define FLASH_LED_LMH_OCV_THRESH_DEFAULT_UV 3700000 #define FLASH_LED_LMH_RBATT_THRESH_DEFAULT_UOHM 400000 #define FLASH_LED_IRES_BASE 3 @@ -199,7 +208,9 @@ struct flash_led_platform_data { u8 vph_droop_hysteresis; u8 vph_droop_debounce; u8 lmh_mitigation_sel; + u8 chgr_mitigation_sel; u8 lmh_level; + u8 iled_thrsh_val; u8 hw_strobe_option; bool hdrm_auto_mode_en; bool thermal_derate_en; @@ -222,6 +233,7 @@ struct qpnp_flash_led { int enable; u16 base; bool trigger_lmh; + bool trigger_chgr; }; static int @@ -352,12 +364,26 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) return rc; rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_MITIGATION_SEL(led->base), + FLASH_LED_CHGR_MITIGATION_SEL_MASK, + led->pdata->chgr_mitigation_sel); + if (rc < 0) + return rc; + + rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_LMH_LEVEL(led->base), FLASH_LED_LMH_LEVEL_MASK, led->pdata->lmh_level); if (rc < 0) return rc; + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_ILED_GRT_THRSH(led->base), + FLASH_LED_ILED_GRT_THRSH_MASK, + led->pdata->iled_thrsh_val); + if (rc < 0) + return rc; + return 0; } @@ -739,6 +765,18 @@ static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode) } } + if (!led->trigger_chgr) { + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_MITIGATION_SW(led->base), + FLASH_LED_CHGR_MITIGATION_EN_MASK, + FLASH_LED_CHGR_MITIGATION_DISABLE); + if (rc < 0) { + dev_err(&led->pdev->dev, "disable chgr mitigation failed, rc=%d\n", + rc); + return rc; + } + } + led->enable--; if (led->enable == 0) { rc = qpnp_flash_led_masked_write(led, @@ -892,6 +930,18 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) } } + if (led->trigger_chgr) { + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_MITIGATION_SW(led->base), + FLASH_LED_CHGR_MITIGATION_EN_MASK, + FLASH_LED_CHGR_MITIGATION_ENABLE); + if (rc < 0) { + dev_err(&led->pdev->dev, "trigger chgr mitigation failed, rc=%d\n", + rc); + return rc; + } + } + rc = qpnp_flash_led_masked_write(led, FLASH_LED_EN_LED_CTRL(led->base), snode->led_mask, val); @@ -951,6 +1001,10 @@ int qpnp_flash_led_prepare(struct led_trigger *trig, int options, *max_current = rc; } + led->trigger_chgr = false; + if (options & PRE_FLASH) + led->trigger_chgr = true; + return 0; } @@ -959,17 +1013,24 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev, { struct flash_node_data *fnode = NULL; struct flash_switch_data *snode = NULL; - struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev); + struct qpnp_flash_led *led = NULL; int rc; if (!strncmp(led_cdev->name, "led:switch", strlen("led:switch"))) { snode = container_of(led_cdev, struct flash_switch_data, cdev); led = dev_get_drvdata(&snode->pdev->dev); - } else { + } else if (!strncmp(led_cdev->name, "led:flash", strlen("led:flash")) || + !strncmp(led_cdev->name, "led:torch", + strlen("led:torch"))) { fnode = container_of(led_cdev, struct flash_node_data, cdev); led = dev_get_drvdata(&fnode->pdev->dev); } + if (!led) { + pr_err("Failed to get flash driver data\n"); + return; + } + spin_lock(&led->lock); if (snode) { rc = qpnp_flash_led_switch_set(snode, value > 0); @@ -1649,7 +1710,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return rc; } - led->pdata->lmh_mitigation_sel = FLASH_LED_LMH_MITIGATION_SEL_DEFAULT; + led->pdata->lmh_mitigation_sel = FLASH_LED_MITIGATION_SEL_DEFAULT; rc = of_property_read_u32(node, "qcom,lmh-mitigation-sel", &val); if (!rc) { led->pdata->lmh_mitigation_sel = val; @@ -1659,11 +1720,43 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return rc; } - if (led->pdata->lmh_mitigation_sel > FLASH_LED_LMH_MITIGATION_SEL_MAX) { + if (led->pdata->lmh_mitigation_sel > FLASH_LED_MITIGATION_SEL_MAX) { dev_err(&led->pdev->dev, "Invalid lmh_mitigation_sel specified\n"); return -EINVAL; } + led->pdata->chgr_mitigation_sel = FLASH_LED_MITIGATION_SEL_DEFAULT; + rc = of_property_read_u32(node, "qcom,chgr-mitigation-sel", &val); + if (!rc) { + led->pdata->chgr_mitigation_sel = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to parse chgr_mitigation_sel, rc=%d\n", + rc); + return rc; + } + + if (led->pdata->chgr_mitigation_sel > FLASH_LED_MITIGATION_SEL_MAX) { + dev_err(&led->pdev->dev, "Invalid chgr_mitigation_sel specified\n"); + return -EINVAL; + } + + led->pdata->chgr_mitigation_sel <<= FLASH_LED_CHGR_MITIGATION_SEL_SHIFT; + + led->pdata->iled_thrsh_val = FLASH_LED_MITIGATION_THRSH_DEFAULT; + rc = of_property_read_u32(node, "qcom,iled-thrsh-ma", &val); + if (!rc) { + led->pdata->iled_thrsh_val = MITIGATION_THRSH_MA_TO_VAL(val); + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to parse iled_thrsh_val, rc=%d\n", + rc); + return rc; + } + + if (led->pdata->iled_thrsh_val > FLASH_LED_MITIGATION_THRSH_MAX) { + dev_err(&led->pdev->dev, "Invalid iled_thrsh_val specified\n"); + return -EINVAL; + } + led->pdata->all_ramp_up_done_irq = of_irq_get_byname(node, "all-ramp-up-done-irq"); if (led->pdata->all_ramp_up_done_irq < 0) diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index 9c855e89e3ff..f071aae3ccab 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -1400,6 +1400,49 @@ static struct msm_vidc_format venc_formats[] = { }, }; +static void msm_venc_update_plane_count(struct msm_vidc_inst *inst, int type) +{ + struct v4l2_ctrl *ctrl = NULL; + u32 extradata = 0; + + if (!inst) + return; + + inst->fmts[type].num_planes = 1; + + ctrl = v4l2_ctrl_find(&inst->ctrl_handler, + V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA); + + if (ctrl) + extradata = v4l2_ctrl_g_ctrl(ctrl); + + if (type == CAPTURE_PORT) { + switch (extradata) { + case V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO: + case V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB: + case V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER: + case V4L2_MPEG_VIDC_EXTRADATA_LTR: + case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI: + inst->fmts[CAPTURE_PORT].num_planes = 2; + default: + break; + } + } else if (type == OUTPUT_PORT) { + switch (extradata) { + case V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP: + case V4L2_MPEG_VIDC_EXTRADATA_DIGITAL_ZOOM: + case V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO: + case V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS: + case V4L2_MPEG_VIDC_EXTRADATA_ROI_QP: + case V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO: + inst->fmts[OUTPUT_PORT].num_planes = 2; + break; + default: + break; + } + } +} + static int msm_venc_set_csc(struct msm_vidc_inst *inst); static int msm_venc_queue_setup(struct vb2_queue *q, @@ -1414,8 +1457,7 @@ static int msm_venc_queue_setup(struct vb2_queue *q, enum hal_property property_id; struct hfi_device *hdev; struct hal_buffer_requirements *buff_req; - struct v4l2_ctrl *ctrl = NULL; - u32 extradata = 0, extra_idx = 0; + u32 extra_idx = 0; struct hal_buffer_requirements *buff_req_buffer = NULL; if (!q || !q->drv_priv) { @@ -1471,21 +1513,8 @@ static int msm_venc_queue_setup(struct vb2_queue *q, temp, *num_buffers); } - ctrl = v4l2_ctrl_find(&inst->ctrl_handler, - V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA); - if (ctrl) - extradata = v4l2_ctrl_g_ctrl(ctrl); - switch (extradata) { - case V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO: - case V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB: - case V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER: - case V4L2_MPEG_VIDC_EXTRADATA_LTR: - case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI: - *num_planes = *num_planes + 1; - default: - break; - } - inst->fmts[CAPTURE_PORT].num_planes = *num_planes; + msm_venc_update_plane_count(inst, CAPTURE_PORT); + *num_planes = inst->fmts[CAPTURE_PORT].num_planes; for (i = 0; i < *num_planes; i++) { int extra_idx = EXTRADATA_IDX(*num_planes); @@ -1543,24 +1572,9 @@ static int msm_venc_queue_setup(struct vb2_queue *q, dprintk(VIDC_DBG, "actual input buffer count set to fw = %d\n", *num_buffers); - ctrl = v4l2_ctrl_find(&inst->ctrl_handler, - V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA); - if (ctrl) - extradata = v4l2_ctrl_g_ctrl(ctrl); - switch (extradata) { - case V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP: - case V4L2_MPEG_VIDC_EXTRADATA_DIGITAL_ZOOM: - case V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO: - case V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS: - case V4L2_MPEG_VIDC_EXTRADATA_ROI_QP: - case V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO: - *num_planes = *num_planes + 1; - break; - default: - break; - } + msm_venc_update_plane_count(inst, OUTPUT_PORT); + *num_planes = inst->fmts[OUTPUT_PORT].num_planes; - inst->fmts[OUTPUT_PORT].num_planes = *num_planes; rc = call_hfi_op(hdev, session_set_property, inst->session, property_id, &new_buf_count); if (rc) @@ -3629,6 +3643,9 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) memcpy(&inst->fmts[fmt->type], fmt, sizeof(struct msm_vidc_format)); + msm_venc_update_plane_count(inst, CAPTURE_PORT); + fmt->num_planes = inst->fmts[CAPTURE_PORT].num_planes; + rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE); if (rc) { dprintk(VIDC_ERR, "Failed to open instance\n"); @@ -3682,6 +3699,9 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) memcpy(&inst->fmts[fmt->type], fmt, sizeof(struct msm_vidc_format)); + msm_venc_update_plane_count(inst, OUTPUT_PORT); + fmt->num_planes = inst->fmts[OUTPUT_PORT].num_planes; + msm_comm_set_color_format(inst, HAL_BUFFER_INPUT, fmt->fourcc); } else { dprintk(VIDC_ERR, "%s - Unsupported buf type: %d\n", @@ -3690,7 +3710,7 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) goto exit; } - f->fmt.pix_mp.num_planes = inst->fmts[fmt->type].num_planes; + f->fmt.pix_mp.num_planes = fmt->num_planes; if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { struct hal_frame_size frame_sz = {0}; @@ -3743,7 +3763,7 @@ int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) const struct msm_vidc_format *fmt = NULL; int rc = 0; int i; - u32 height, width; + u32 height, width, num_planes; unsigned int extra_idx = 0; struct hal_buffer_requirements *bufreq = NULL; @@ -3764,10 +3784,14 @@ int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) fmt = &inst->fmts[CAPTURE_PORT]; height = inst->prop.height[CAPTURE_PORT]; width = inst->prop.width[CAPTURE_PORT]; + msm_venc_update_plane_count(inst, CAPTURE_PORT); + num_planes = inst->fmts[CAPTURE_PORT].num_planes; } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { fmt = &inst->fmts[OUTPUT_PORT]; height = inst->prop.height[OUTPUT_PORT]; width = inst->prop.width[OUTPUT_PORT]; + msm_venc_update_plane_count(inst, OUTPUT_PORT); + num_planes = inst->fmts[OUTPUT_PORT].num_planes; } else { dprintk(VIDC_ERR, "Invalid type: %x\n", f->type); return -ENOTSUPP; @@ -3776,10 +3800,10 @@ int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) f->fmt.pix_mp.pixelformat = fmt->fourcc; f->fmt.pix_mp.height = height; f->fmt.pix_mp.width = width; - f->fmt.pix_mp.num_planes = fmt->num_planes; + f->fmt.pix_mp.num_planes = num_planes; if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - for (i = 0; i < fmt->num_planes; ++i) { + for (i = 0; i < num_planes; ++i) { f->fmt.pix_mp.plane_fmt[i].sizeimage = fmt->get_frame_size(i, height, width); } @@ -3790,7 +3814,7 @@ int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) f->fmt.pix_mp.plane_fmt[0].sizeimage = bufreq ? bufreq->buffer_size : 0; } - extra_idx = EXTRADATA_IDX(fmt->num_planes); + extra_idx = EXTRADATA_IDX(num_planes); if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) { if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) bufreq = get_buff_req_buffer(inst, @@ -3803,7 +3827,7 @@ int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) bufreq ? bufreq->buffer_size : 0; } - for (i = 0; i < fmt->num_planes; ++i) { + for (i = 0; i < num_planes; ++i) { if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { inst->bufq[OUTPUT_PORT].vb2_bufq.plane_sizes[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage; @@ -4057,4 +4081,3 @@ int msm_venc_ctrl_init(struct msm_vidc_inst *inst) return msm_comm_ctrl_init(inst, msm_venc_ctrls, ARRAY_SIZE(msm_venc_ctrls), &msm_venc_ctrl_ops); } - diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c index 02ddf3225af8..e07350a1e2ce 100644 --- a/drivers/mfd/wcd934x-regmap.c +++ b/drivers/mfd/wcd934x-regmap.c @@ -17,6 +17,18 @@ #include <linux/device.h> #include "wcd9xxx-regmap.h" + +static const struct reg_sequence wcd934x_1_1_defaults[] = { + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x01 }, + { WCD934X_BIAS_VBG_FINE_ADJ, 0x75 }, + { WCD934X_HPH_REFBUFF_LP_CTL, 0x0E }, + { WCD934X_EAR_DAC_CTL_ATEST, 0x08 }, + { WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x17 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x81 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x81 }, +}; + static const struct reg_default wcd934x_defaults[] = { { WCD934X_PAGE0_PAGE_REGISTER, 0x00 }, { WCD934X_CODEC_RPM_CLK_BYPASS, 0x00 }, @@ -1803,6 +1815,37 @@ static const struct reg_default wcd934x_defaults[] = { { WCD934X_TEST_DEBUG_CODEC_DIAGS, 0x00 }, }; +/* + * wcd934x_regmap_register_patch: Update register defaults based on version + * @regmap: handle to wcd9xxx regmap + * @version: wcd934x version + * + * Returns error code in case of failure or 0 for success + */ +int wcd934x_regmap_register_patch(struct regmap *regmap, int revision) +{ + int rc = 0; + + if (!regmap) { + pr_err("%s: regmap struct is NULL\n", __func__); + return -EINVAL; + } + + switch (revision) { + case TAVIL_VERSION_1_1: + case TAVIL_VERSION_WCD9340_1_1: + case TAVIL_VERSION_WCD9341_1_1: + regcache_cache_only(regmap, true); + rc = regmap_multi_reg_write(regmap, wcd934x_1_1_defaults, + ARRAY_SIZE(wcd934x_1_1_defaults)); + regcache_cache_only(regmap, false); + break; + } + + return rc; +} +EXPORT_SYMBOL(wcd934x_regmap_register_patch); + static bool wcd934x_is_readable_register(struct device *dev, unsigned int reg) { u8 pg_num, reg_offset; diff --git a/drivers/mfd/wcd9xxx-regmap.h b/drivers/mfd/wcd9xxx-regmap.h index 62e4a620c71c..6db8fc55acae 100644 --- a/drivers/mfd/wcd9xxx-regmap.h +++ b/drivers/mfd/wcd9xxx-regmap.h @@ -21,6 +21,8 @@ typedef int (*regmap_patch_fptr)(struct regmap *, int); #ifdef CONFIG_WCD934X_CODEC extern struct regmap_config wcd934x_regmap_config; +extern int wcd934x_regmap_register_patch(struct regmap *regmap, + int version); #endif #ifdef CONFIG_WCD9335_CODEC @@ -71,6 +73,11 @@ static inline regmap_patch_fptr wcd9xxx_get_regmap_reg_patch(int type) apply_patch = wcd9335_regmap_register_patch; break; #endif +#ifdef CONFIG_WCD934X_CODEC + case WCD934X: + apply_patch = wcd934x_regmap_register_patch; + break; +#endif default: apply_patch = NULL; break; diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.c b/drivers/misc/qcom/qdsp6v2/audio_utils.c index 065b426ca6d0..840597314a5f 100644 --- a/drivers/misc/qcom/qdsp6v2/audio_utils.c +++ b/drivers/misc/qcom/qdsp6v2/audio_utils.c @@ -601,6 +601,7 @@ long audio_in_compat_ioctl(struct file *file, } case AUDIO_GET_CONFIG_32: { struct msm_audio_config32 cfg_32; + memset(&cfg_32, 0, sizeof(cfg_32)); cfg_32.buffer_size = audio->pcm_cfg.buffer_size; cfg_32.buffer_count = audio->pcm_cfg.buffer_count; cfg_32.channel_count = audio->pcm_cfg.channel_count; diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c index 0c44f79549d4..567c948b0efe 100644 --- a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c +++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c @@ -570,6 +570,8 @@ int audio_aio_release(struct inode *inode, struct file *file) struct q6audio_aio *audio = file->private_data; pr_debug("%s[%p]\n", __func__, audio); mutex_lock(&audio->lock); + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); audio->wflush = 1; if (audio->wakelock_voted && (audio->audio_ws_mgr != NULL) && @@ -595,6 +597,8 @@ int audio_aio_release(struct inode *inode, struct file *file) wake_up(&audio->event_wait); audio_aio_reset_event_queue(audio); q6asm_audio_client_free(audio->ac); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); mutex_unlock(&audio->lock); mutex_destroy(&audio->lock); mutex_destroy(&audio->read_lock); @@ -1745,7 +1749,11 @@ static long audio_aio_ioctl(struct file *file, unsigned int cmd, __func__); rc = -EFAULT; } else { + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); rc = audio_aio_ion_add(audio, &info); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); } mutex_unlock(&audio->lock); break; @@ -1760,7 +1768,11 @@ static long audio_aio_ioctl(struct file *file, unsigned int cmd, __func__); rc = -EFAULT; } else { + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); rc = audio_aio_ion_remove(audio, &info); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); } mutex_unlock(&audio->lock); break; @@ -2064,7 +2076,11 @@ static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd, } else { info.fd = info_32.fd; info.vaddr = compat_ptr(info_32.vaddr); + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); rc = audio_aio_ion_add(audio, &info); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); } mutex_unlock(&audio->lock); break; @@ -2081,7 +2097,11 @@ static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd, } else { info.fd = info_32.fd; info.vaddr = compat_ptr(info_32.vaddr); + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); rc = audio_aio_ion_remove(audio, &info); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); } mutex_unlock(&audio->lock); break; diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c index 05ce3969a5c7..75b193def36e 100644 --- a/drivers/platform/msm/ipa/ipa_api.c +++ b/drivers/platform/msm/ipa/ipa_api.c @@ -26,7 +26,8 @@ #define IPA_API_DISPATCH_RETURN(api, p...) \ do { \ if (!ipa_api_ctrl) { \ - pr_err("IPA HW is not supported on this target\n"); \ + pr_err("%s:%d IPA HW is not supported\n", \ + __func__, __LINE__); \ ret = -EPERM; \ } \ else { \ @@ -44,7 +45,8 @@ #define IPA_API_DISPATCH(api, p...) \ do { \ if (!ipa_api_ctrl) \ - pr_err("IPA HW is not supported on this target\n"); \ + pr_err("%s:%d IPA HW is not supported\n", \ + __func__, __LINE__); \ else { \ if (ipa_api_ctrl->api) { \ ipa_api_ctrl->api(p); \ @@ -59,7 +61,8 @@ #define IPA_API_DISPATCH_RETURN_PTR(api, p...) \ do { \ if (!ipa_api_ctrl) { \ - pr_err("IPA HW is not supported on this target\n"); \ + pr_err("%s:%d IPA HW is not supported\n", \ + __func__, __LINE__); \ ret = NULL; \ } \ else { \ @@ -77,7 +80,8 @@ #define IPA_API_DISPATCH_RETURN_BOOL(api, p...) \ do { \ if (!ipa_api_ctrl) { \ - pr_err("IPA HW is not supported on this target\n"); \ + pr_err("%s:%d IPA HW is not supported\n", \ + __func__, __LINE__); \ ret = false; \ } \ else { \ diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c index 838b78c1934d..d18308344431 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c @@ -2034,7 +2034,7 @@ static void ipa_usb_debugfs_init(void) ipa3_usb_ctx->dent = debugfs_create_dir("ipa_usb", 0); if (IS_ERR(ipa3_usb_ctx->dent)) { - IPA_USB_ERR("fail to create folder in debug_fs.\n"); + pr_err("fail to create folder in debug_fs.\n"); return; } @@ -2043,7 +2043,7 @@ static void ipa_usb_debugfs_init(void) &ipa3_ipa_usb_ops); if (!ipa3_usb_ctx->dfile_state_info || IS_ERR(ipa3_usb_ctx->dfile_state_info)) { - IPA_USB_ERR("failed to create file for state_info\n"); + pr_err("failed to create file for state_info\n"); goto fail; } @@ -2644,11 +2644,11 @@ static int __init ipa3_usb_init(void) unsigned long flags; int res; - IPA_USB_DBG("entry\n"); + pr_debug("entry\n"); ipa3_usb_ctx = kzalloc(sizeof(struct ipa3_usb_context), GFP_KERNEL); if (ipa3_usb_ctx == NULL) { - IPA_USB_ERR("failed to allocate memory\n"); - IPA_USB_ERR(":ipa_usb init failed\n"); + pr_err("failed to allocate memory\n"); + pr_err(":ipa_usb init failed\n"); return -EFAULT; } memset(ipa3_usb_ctx, 0, sizeof(struct ipa3_usb_context)); @@ -2680,19 +2680,19 @@ static int __init ipa3_usb_init(void) ipa3_usb_ctx->wq = create_singlethread_workqueue("ipa_usb_wq"); if (!ipa3_usb_ctx->wq) { - IPA_USB_ERR("failed to create workqueue\n"); + pr_err("failed to create workqueue\n"); res = -EFAULT; goto ipa_usb_workqueue_fail; } ipa_usb_debugfs_init(); - IPA_USB_INFO("exit: IPA_USB init success!\n"); + pr_info("exit: IPA_USB init success!\n"); return 0; ipa_usb_workqueue_fail: - IPA_USB_ERR(":init failed (%d)\n", -res); + pr_err(":init failed (%d)\n", -res); kfree(ipa3_usb_ctx); return res; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c index 16f50030b960..3c2a6d4620ba 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c @@ -3152,23 +3152,23 @@ static int ipa_assign_policy_v2(struct ipa_sys_connect_params *in, } else if (in->client == IPA_CLIENT_APPS_WAN_CONS) { sys->pyld_hdlr = ipa_wan_rx_pyld_hdlr; - if (in->recycle_enabled) { + sys->rx_pool_sz = ipa_ctx->wan_rx_ring_size; + if (nr_cpu_ids > 1) { sys->repl_hdlr = - ipa_replenish_rx_cache_recycle; - sys->rx_pool_sz = - IPA_WAN_NAPI_CONS_RX_POOL_SZ; + ipa_fast_replenish_rx_cache; + sys->repl_trig_thresh = + sys->rx_pool_sz / 8; } else { - if (nr_cpu_ids > 1) { - sys->repl_hdlr = - ipa_fast_replenish_rx_cache; - sys->repl_trig_thresh = - sys->rx_pool_sz / 8; - } else { + sys->repl_hdlr = + ipa_replenish_rx_cache; + } + if (in->napi_enabled) { + sys->rx_pool_sz = + IPA_WAN_NAPI_CONS_RX_POOL_SZ; + if (in->recycle_enabled) { sys->repl_hdlr = - ipa_replenish_rx_cache; + ipa_replenish_rx_cache_recycle; } - sys->rx_pool_sz = - ipa_ctx->wan_rx_ring_size; } sys->ep->wakelock_client = IPA_WAKELOCK_REF_CLIENT_WAN_RX; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c index 249de808ec5c..f5afb4b0141c 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c @@ -523,10 +523,9 @@ ssize_t ipa_read(struct file *filp, char __user *buf, size_t count, start = buf; while (1) { - prepare_to_wait(&ipa_ctx->msg_waitq, &wait, TASK_INTERRUPTIBLE); - mutex_lock(&ipa_ctx->msg_lock); locked = 1; + prepare_to_wait(&ipa_ctx->msg_waitq, &wait, TASK_INTERRUPTIBLE); if (!list_empty(&ipa_ctx->msg_list)) { msg = list_first_entry(&ipa_ctx->msg_list, struct ipa_push_msg, link); diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index ebb93e246048..96003d7a16a0 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -1259,11 +1259,13 @@ static int handle_ingress_format(struct net_device *dev, ipa_to_apps_ep_cfg.ipa_ep_cfg.aggr.aggr_pkt_limit = in->u.ingress_format.agg_count; - ipa_to_apps_ep_cfg.recycle_enabled = true; - ep_cfg = (struct rmnet_phys_ep_conf_s *) - rcu_dereference(dev->rx_handler_data); - ep_cfg->recycle = ipa_recycle_wan_skb; - pr_info("Wan Recycle Enabled\n"); + if (ipa_rmnet_res.ipa_napi_enable) { + ipa_to_apps_ep_cfg.recycle_enabled = true; + ep_cfg = (struct rmnet_phys_ep_conf_s *) + rcu_dereference(dev->rx_handler_data); + ep_cfg->recycle = ipa_recycle_wan_skb; + pr_info("Wan Recycle Enabled\n"); + } } } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 643e40402499..94e8bba1fe01 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -3180,22 +3180,20 @@ static int ipa3_assign_policy(struct ipa_sys_connect_params *in, IPA_CLIENT_APPS_WAN_CONS) { sys->pyld_hdlr = ipa3_wan_rx_pyld_hdlr; sys->free_rx_wrapper = ipa3_free_rx_wrapper; - if (in->recycle_enabled) { + sys->rx_pool_sz = ipa3_ctx->wan_rx_ring_size; + if (nr_cpu_ids > 1) { sys->repl_hdlr = - ipa3_replenish_rx_cache_recycle; - sys->rx_pool_sz = - IPA_WAN_NAPI_CONS_RX_POOL_SZ; + ipa3_fast_replenish_rx_cache; } else { - if (nr_cpu_ids > 1) { - sys->repl_hdlr = - ipa3_fast_replenish_rx_cache; - } else { - sys->repl_hdlr = - ipa3_replenish_rx_cache; - } - sys->rx_pool_sz = - ipa3_ctx->wan_rx_ring_size; + sys->repl_hdlr = + ipa3_replenish_rx_cache; } + if (in->napi_enabled) + sys->rx_pool_sz = + IPA_WAN_NAPI_CONS_RX_POOL_SZ; + if (in->napi_enabled && in->recycle_enabled) + sys->repl_hdlr = + ipa3_replenish_rx_cache_recycle; in->ipa_ep_cfg.aggr.aggr_sw_eof_active = true; if (ipa3_ctx-> diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c index 22756c1fb168..b9f57552533e 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c @@ -528,12 +528,12 @@ ssize_t ipa3_read(struct file *filp, char __user *buf, size_t count, start = buf; while (1) { + mutex_lock(&ipa3_ctx->msg_lock); + locked = 1; prepare_to_wait(&ipa3_ctx->msg_waitq, &wait, TASK_INTERRUPTIBLE); - mutex_lock(&ipa3_ctx->msg_lock); - locked = 1; if (!list_empty(&ipa3_ctx->msg_list)) { msg = list_first_entry(&ipa3_ctx->msg_list, struct ipa3_push_msg, link); diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index a2fef45cc55f..f134852e046e 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -1271,11 +1271,13 @@ static int handle3_ingress_format(struct net_device *dev, ipa_wan_ep_cfg->ipa_ep_cfg.aggr.aggr_pkt_limit = in->u.ingress_format.agg_count; - ipa_wan_ep_cfg->recycle_enabled = true; - ep_cfg = (struct rmnet_phys_ep_conf_s *) - rcu_dereference(dev->rx_handler_data); - ep_cfg->recycle = ipa_recycle_wan_skb; - pr_info("Wan Recycle Enabled\n"); + if (ipa_wan_ep_cfg->napi_enabled) { + ipa_wan_ep_cfg->recycle_enabled = true; + ep_cfg = (struct rmnet_phys_ep_conf_s *) + rcu_dereference(dev->rx_handler_data); + ep_cfg->recycle = ipa_recycle_wan_skb; + pr_info("Wan Recycle Enabled\n"); + } } } @@ -1969,9 +1971,9 @@ static int get_ipa_rmnet_dts_configuration(struct platform_device *pdev, ipa_rmnet_drv_res->ipa_advertise_sg_support ? "True" : "False"); ipa_rmnet_drv_res->ipa_napi_enable = - of_property_read_bool(pdev->dev.of_node, - "qcom,napi"); - pr_info("IPA napi = %s\n", + of_property_read_bool(pdev->dev.of_node, + "qcom,ipa-napi-enable"); + pr_info("IPA Napi Enable = %s\n", ipa_rmnet_drv_res->ipa_napi_enable ? "True" : "False"); return 0; } diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 8af1eb66c699..0619b314b7de 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -267,6 +267,9 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(typec_power_role), POWER_SUPPLY_ATTR(pd_allowed), POWER_SUPPLY_ATTR(pd_active), + POWER_SUPPLY_ATTR(pd_in_hard_reset), + POWER_SUPPLY_ATTR(pd_current_max), + POWER_SUPPLY_ATTR(pd_usb_suspend_supported), POWER_SUPPLY_ATTR(charger_temp), POWER_SUPPLY_ATTR(charger_temp_max), POWER_SUPPLY_ATTR(parallel_disable), diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h index 7e08b71e3b6a..adc640c7afe1 100644 --- a/drivers/power/qcom-charger/fg-core.h +++ b/drivers/power/qcom-charger/fg-core.h @@ -23,6 +23,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/string_helpers.h> @@ -38,6 +39,7 @@ pr_debug(fmt, ##__VA_ARGS__); \ } while (0) +/* Awake votable reasons */ #define SRAM_READ "fg_sram_read" #define SRAM_WRITE "fg_sram_write" #define PROFILE_LOAD "fg_profile_load" @@ -59,6 +61,9 @@ #define BUCKET_COUNT 8 #define BUCKET_SOC_PCT (256 / BUCKET_COUNT) +#define KI_COEFF_MAX 62200 +#define KI_COEFF_SOC_LEVELS 3 + /* Debug flag definitions */ enum fg_debug_flag { FG_IRQ = BIT(0), /* Show interrupts */ @@ -137,6 +142,8 @@ enum fg_sram_param_id { FG_SRAM_CHG_TERM_CURR, FG_SRAM_DELTA_SOC_THR, FG_SRAM_RECHARGE_SOC_THR, + FG_SRAM_KI_COEFF_MED_DISCHG, + FG_SRAM_KI_COEFF_HI_DISCHG, FG_SRAM_MAX, }; @@ -173,6 +180,8 @@ struct fg_alg_flag { /* DT parameters for FG device */ struct fg_dt_props { + bool force_load_profile; + bool hold_soc_while_full; int cutoff_volt_mv; int empty_volt_mv; int vbatt_low_thr_mv; @@ -185,7 +194,6 @@ struct fg_dt_props { int esr_timer_charging; int esr_timer_awake; int esr_timer_asleep; - bool force_load_profile; int cl_start_soc; int cl_max_temp; int cl_min_temp; @@ -195,6 +203,9 @@ struct fg_dt_props { int cl_min_cap_limit; int jeita_hyst_temp; int batt_temp_delta; + int ki_coeff_soc[KI_COEFF_SOC_LEVELS]; + int ki_coeff_med_dischg[KI_COEFF_SOC_LEVELS]; + int ki_coeff_hi_dischg[KI_COEFF_SOC_LEVELS]; }; /* parameters from battery profile */ @@ -240,6 +251,8 @@ struct fg_chip { struct dentry *dfs_root; struct power_supply *fg_psy; struct power_supply *batt_psy; + struct power_supply *usb_psy; + struct power_supply *dc_psy; struct iio_channel *batt_id_chan; struct fg_memif *sram; struct fg_irq_info *irqs; @@ -268,6 +281,9 @@ struct fg_chip { bool profile_loaded; bool battery_missing; bool fg_restarting; + bool charge_full; + bool recharge_soc_adjusted; + bool ki_coeff_dischg_en; struct completion soc_update; struct completion soc_ready; struct delayed_work profile_load_work; @@ -321,4 +337,5 @@ extern int fg_debugfs_create(struct fg_chip *chip); extern void fill_string(char *str, size_t str_len, u8 *buf, int buf_len); extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos); extern s64 fg_float_decode(u16 val); +extern bool is_input_present(struct fg_chip *chip); #endif diff --git a/drivers/power/qcom-charger/fg-util.c b/drivers/power/qcom-charger/fg-util.c index 790e56bd3dae..bbdbe48896d7 100644 --- a/drivers/power/qcom-charger/fg-util.c +++ b/drivers/power/qcom-charger/fg-util.c @@ -29,6 +29,43 @@ static struct fg_dbgfs dbgfs_data = { }, }; +static bool is_usb_present(struct fg_chip *chip) +{ + union power_supply_propval pval = {0, }; + + if (!chip->usb_psy) + chip->usb_psy = power_supply_get_by_name("usb"); + + if (chip->usb_psy) + power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + else + return false; + + return pval.intval != 0; +} + +static bool is_dc_present(struct fg_chip *chip) +{ + union power_supply_propval pval = {0, }; + + if (!chip->dc_psy) + chip->dc_psy = power_supply_get_by_name("dc"); + + if (chip->dc_psy) + power_supply_get_property(chip->dc_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + else + return false; + + return pval.intval != 0; +} + +bool is_input_present(struct fg_chip *chip) +{ + return is_usb_present(chip) || is_dc_present(chip); +} + #define EXPONENT_SHIFT 11 #define EXPONENT_OFFSET -9 #define MANTISSA_SIGN_BIT 10 @@ -98,6 +135,7 @@ int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset, * This interrupt need to be enabled only when it is * required. It will be kept disabled other times. */ + reinit_completion(&chip->soc_update); enable_irq(chip->irqs[SOC_UPDATE_IRQ].irq); atomic_access = true; } else { diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c index f8c1ad5963af..30408218b7e7 100644 --- a/drivers/power/qcom-charger/qpnp-fg-gen3.c +++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c @@ -17,7 +17,6 @@ #include <linux/of_platform.h> #include <linux/of_batterydata.h> #include <linux/platform_device.h> -#include <linux/power_supply.h> #include <linux/iio/consumer.h> #include <linux/qpnp/qpnp-revid.h> #include "fg-core.h" @@ -37,6 +36,12 @@ #define SYS_TERM_CURR_OFFSET 0 #define VBATT_FULL_WORD 7 #define VBATT_FULL_OFFSET 0 +#define KI_COEFF_MED_DISCHG_WORD 9 +#define KI_COEFF_MED_DISCHG_OFFSET 3 +#define KI_COEFF_HI_DISCHG_WORD 10 +#define KI_COEFF_HI_DISCHG_OFFSET 0 +#define KI_COEFF_LOW_DISCHG_WORD 10 +#define KI_COEFF_LOW_DISCHG_OFFSET 2 #define DELTA_SOC_THR_WORD 12 #define DELTA_SOC_THR_OFFSET 3 #define RECHARGE_SOC_THR_WORD 14 @@ -65,6 +70,8 @@ #define PROFILE_INTEGRITY_OFFSET 3 #define BATT_SOC_WORD 91 #define BATT_SOC_OFFSET 0 +#define FULL_SOC_WORD 93 +#define FULL_SOC_OFFSET 2 #define MONOTONIC_SOC_WORD 94 #define MONOTONIC_SOC_OFFSET 2 #define CC_SOC_WORD 95 @@ -87,6 +94,12 @@ #define ALG_FLAGS_OFFSET 1 /* v2 SRAM address and offset in ascending order */ +#define KI_COEFF_LOW_DISCHG_v2_WORD 9 +#define KI_COEFF_LOW_DISCHG_v2_OFFSET 3 +#define KI_COEFF_MED_DISCHG_v2_WORD 10 +#define KI_COEFF_MED_DISCHG_v2_OFFSET 0 +#define KI_COEFF_HI_DISCHG_v2_WORD 10 +#define KI_COEFF_HI_DISCHG_v2_OFFSET 1 #define DELTA_SOC_THR_v2_WORD 13 #define DELTA_SOC_THR_v2_OFFSET 0 #define RECHARGE_SOC_THR_v2_WORD 14 @@ -106,8 +119,6 @@ static int fg_decode_value_16b(struct fg_sram_param *sp, enum fg_sram_param_id id, int val); static int fg_decode_default(struct fg_sram_param *sp, enum fg_sram_param_id id, int val); -static int fg_decode_batt_soc(struct fg_sram_param *sp, - enum fg_sram_param_id id, int val); static int fg_decode_cc_soc(struct fg_sram_param *sp, enum fg_sram_param_id id, int value); static void fg_encode_voltage(struct fg_sram_param *sp, @@ -132,7 +143,7 @@ static void fg_encode_default(struct fg_sram_param *sp, static struct fg_sram_param pmicobalt_v1_sram_params[] = { PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, 0, NULL, - fg_decode_batt_soc), + fg_decode_default), PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141, 1000, 0, NULL, fg_decode_voltage_15b), PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL, @@ -174,11 +185,17 @@ static struct fg_sram_param pmicobalt_v1_sram_params[] = { ESR_TIMER_CHG_MAX_OFFSET, 2, 1, 1, 0, fg_encode_default, NULL), PARAM(ESR_TIMER_CHG_INIT, ESR_TIMER_CHG_INIT_WORD, ESR_TIMER_CHG_INIT_OFFSET, 2, 1, 1, 0, fg_encode_default, NULL), + PARAM(KI_COEFF_MED_DISCHG, KI_COEFF_MED_DISCHG_WORD, + KI_COEFF_MED_DISCHG_OFFSET, 1, 1000, 244141, 0, + fg_encode_default, NULL), + PARAM(KI_COEFF_HI_DISCHG, KI_COEFF_HI_DISCHG_WORD, + KI_COEFF_HI_DISCHG_OFFSET, 1, 1000, 244141, 0, + fg_encode_default, NULL), }; static struct fg_sram_param pmicobalt_v2_sram_params[] = { PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, 0, NULL, - fg_decode_batt_soc), + fg_decode_default), PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141, 1000, 0, NULL, fg_decode_voltage_15b), PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL, @@ -223,6 +240,12 @@ static struct fg_sram_param pmicobalt_v2_sram_params[] = { ESR_TIMER_CHG_MAX_OFFSET, 2, 1, 1, 0, fg_encode_default, NULL), PARAM(ESR_TIMER_CHG_INIT, ESR_TIMER_CHG_INIT_WORD, ESR_TIMER_CHG_INIT_OFFSET, 2, 1, 1, 0, fg_encode_default, NULL), + PARAM(KI_COEFF_MED_DISCHG, KI_COEFF_MED_DISCHG_v2_WORD, + KI_COEFF_MED_DISCHG_v2_OFFSET, 1, 1000, 244141, 0, + fg_encode_default, NULL), + PARAM(KI_COEFF_HI_DISCHG, KI_COEFF_HI_DISCHG_v2_WORD, + KI_COEFF_HI_DISCHG_v2_OFFSET, 1, 1000, 244141, 0, + fg_encode_default, NULL), }; static struct fg_alg_flag pmicobalt_v1_alg_flags[] = { @@ -335,19 +358,11 @@ static int fg_decode_value_16b(struct fg_sram_param *sp, return sp[id].value; } -static int fg_decode_batt_soc(struct fg_sram_param *sp, - enum fg_sram_param_id id, int value) -{ - sp[id].value = (u32)value >> 24; - pr_debug("id: %d raw value: %x decoded value: %x\n", id, value, - sp[id].value); - return sp[id].value; -} - static int fg_decode_default(struct fg_sram_param *sp, enum fg_sram_param_id id, int value) { - return value; + sp[id].value = value; + return sp[id].value; } static int fg_decode(struct fg_sram_param *sp, enum fg_sram_param_id id, @@ -645,6 +660,11 @@ static int fg_get_prop_capacity(struct fg_chip *chip, int *val) { int rc, msoc; + if (chip->charge_full) { + *val = FULL_CAPACITY; + return 0; + } + rc = fg_get_msoc_raw(chip, &msoc); if (rc < 0) return rc; @@ -1059,7 +1079,7 @@ static int fg_cap_learning_done(struct fg_chip *chip) cc_soc_sw = CC_SOC_30BIT; rc = fg_sram_write(chip, chip->sp[FG_SRAM_CC_SOC_SW].addr_word, chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw, - chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_DEFAULT); + chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC); if (rc < 0) { pr_err("Error in writing cc_soc_sw, rc=%d\n", rc); goto out; @@ -1092,6 +1112,9 @@ static void fg_cap_learning_update(struct fg_chip *chip) goto out; } + /* We need only the most significant byte here */ + batt_soc = (u32)batt_soc >> 24; + fg_dbg(chip, FG_CAP_LEARN, "Chg_status: %d cl_active: %d batt_soc: %d\n", chip->status, chip->cl.active, batt_soc); @@ -1103,8 +1126,7 @@ static void fg_cap_learning_update(struct fg_chip *chip) } } else { - if (chip->status == POWER_SUPPLY_STATUS_FULL && - chip->charge_done) { + if (chip->charge_done) { rc = fg_cap_learning_done(chip); if (rc < 0) pr_err("Error in completing capacity learning, rc=%d\n", @@ -1126,19 +1148,211 @@ out: mutex_unlock(&chip->cl.lock); } +#define KI_COEFF_MED_DISCHG_DEFAULT 1500 +#define KI_COEFF_HI_DISCHG_DEFAULT 2200 +static int fg_adjust_ki_coeff_dischg(struct fg_chip *chip) +{ + int rc, i, msoc; + int ki_coeff_med = KI_COEFF_MED_DISCHG_DEFAULT; + int ki_coeff_hi = KI_COEFF_HI_DISCHG_DEFAULT; + u8 val; + + if (!chip->ki_coeff_dischg_en) + return 0; + + rc = fg_get_prop_capacity(chip, &msoc); + if (rc < 0) { + pr_err("Error in getting capacity, rc=%d\n", rc); + return rc; + } + + if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) { + for (i = KI_COEFF_SOC_LEVELS - 1; i >= 0; i--) { + if (msoc < chip->dt.ki_coeff_soc[i]) { + ki_coeff_med = chip->dt.ki_coeff_med_dischg[i]; + ki_coeff_hi = chip->dt.ki_coeff_hi_dischg[i]; + } + } + } + + fg_encode(chip->sp, FG_SRAM_KI_COEFF_MED_DISCHG, ki_coeff_med, &val); + rc = fg_sram_write(chip, + chip->sp[FG_SRAM_KI_COEFF_MED_DISCHG].addr_word, + chip->sp[FG_SRAM_KI_COEFF_MED_DISCHG].addr_byte, &val, + chip->sp[FG_SRAM_KI_COEFF_MED_DISCHG].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing ki_coeff_med, rc=%d\n", rc); + return rc; + } + + fg_encode(chip->sp, FG_SRAM_KI_COEFF_HI_DISCHG, ki_coeff_hi, &val); + rc = fg_sram_write(chip, + chip->sp[FG_SRAM_KI_COEFF_HI_DISCHG].addr_word, + chip->sp[FG_SRAM_KI_COEFF_HI_DISCHG].addr_byte, &val, + chip->sp[FG_SRAM_KI_COEFF_HI_DISCHG].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing ki_coeff_hi, rc=%d\n", rc); + return rc; + } + + fg_dbg(chip, FG_STATUS, "Wrote ki_coeff_med %d ki_coeff_hi %d\n", + ki_coeff_med, ki_coeff_hi); + return 0; +} + +static int fg_charge_full_update(struct fg_chip *chip) +{ + union power_supply_propval prop = {0, }; + int rc, msoc, bsoc, recharge_soc; + u8 full_soc[2] = {0xFF, 0xFF}; + + if (!chip->dt.hold_soc_while_full) + return 0; + + if (!is_charger_available(chip)) + return 0; + + rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_HEALTH, + &prop); + if (rc < 0) { + pr_err("Error in getting battery health, rc=%d\n", rc); + return rc; + } + + chip->health = prop.intval; + recharge_soc = chip->dt.recharge_soc_thr; + recharge_soc = DIV_ROUND_CLOSEST(recharge_soc * FULL_SOC_RAW, + FULL_CAPACITY); + rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &bsoc); + if (rc < 0) { + pr_err("Error in getting BATT_SOC, rc=%d\n", rc); + return rc; + } + + /* We need 2 most significant bytes here */ + bsoc = (u32)bsoc >> 16; + rc = fg_get_prop_capacity(chip, &msoc); + if (rc < 0) { + pr_err("Error in getting capacity, rc=%d\n", rc); + return rc; + } + + fg_dbg(chip, FG_STATUS, "msoc: %d health: %d status: %d\n", msoc, + chip->health, chip->status); + if (chip->charge_done) { + if (msoc >= 99 && chip->health == POWER_SUPPLY_HEALTH_GOOD) + chip->charge_full = true; + else + fg_dbg(chip, FG_STATUS, "Terminated charging @ SOC%d\n", + msoc); + } else if ((bsoc >> 8) <= recharge_soc) { + fg_dbg(chip, FG_STATUS, "bsoc: %d recharge_soc: %d\n", + bsoc >> 8, recharge_soc); + chip->charge_full = false; + } + + if (!chip->charge_full) + return 0; + + /* + * During JEITA conditions, charge_full can happen early. FULL_SOC + * and MONOTONIC_SOC needs to be updated to reflect the same. Write + * battery SOC to FULL_SOC and write a full value to MONOTONIC_SOC. + */ + rc = fg_sram_write(chip, FULL_SOC_WORD, FULL_SOC_OFFSET, (u8 *)&bsoc, 2, + FG_IMA_ATOMIC); + if (rc < 0) { + pr_err("failed to write full_soc rc=%d\n", rc); + return rc; + } + + rc = fg_sram_write(chip, MONOTONIC_SOC_WORD, MONOTONIC_SOC_OFFSET, + full_soc, 2, FG_IMA_ATOMIC); + if (rc < 0) { + pr_err("failed to write monotonic_soc rc=%d\n", rc); + return rc; + } + + fg_dbg(chip, FG_STATUS, "Set charge_full to true @ soc %d\n", msoc); + return 0; +} + +static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc) +{ + u8 buf[4]; + int rc; + + fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, recharge_soc, buf); + rc = fg_sram_write(chip, + chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_word, + chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_byte, buf, + chip->sp[FG_SRAM_RECHARGE_SOC_THR].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing recharge_soc_thr, rc=%d\n", rc); + return rc; + } + + return 0; +} + +static int fg_adjust_recharge_soc(struct fg_chip *chip) +{ + int rc, msoc, recharge_soc, new_recharge_soc = 0; + + recharge_soc = chip->dt.recharge_soc_thr; + /* + * If the input is present and charging had been terminated, adjust + * the recharge SOC threshold based on the monotonic SOC at which + * the charge termination had happened. + */ + if (is_input_present(chip) && !chip->recharge_soc_adjusted + && chip->charge_done) { + /* Get raw monotonic SOC for calculation */ + rc = fg_get_msoc_raw(chip, &msoc); + if (rc < 0) { + pr_err("Error in getting msoc, rc=%d\n", rc); + return rc; + } + + msoc = DIV_ROUND_CLOSEST(msoc * FULL_CAPACITY, FULL_SOC_RAW); + /* Adjust the recharge_soc threshold */ + new_recharge_soc = msoc - (FULL_CAPACITY - recharge_soc); + } else if (chip->recharge_soc_adjusted && (!is_input_present(chip) + || chip->health == POWER_SUPPLY_HEALTH_GOOD)) { + /* Restore the default value */ + new_recharge_soc = recharge_soc; + } + + if (new_recharge_soc > 0 && new_recharge_soc < FULL_CAPACITY) { + rc = fg_set_recharge_soc(chip, new_recharge_soc); + if (rc) { + pr_err("Couldn't set resume SOC for FG, rc=%d\n", rc); + return rc; + } + + chip->recharge_soc_adjusted = (new_recharge_soc != + recharge_soc); + fg_dbg(chip, FG_STATUS, "resume soc set to %d\n", + new_recharge_soc); + } + + return 0; +} + static void status_change_work(struct work_struct *work) { struct fg_chip *chip = container_of(work, struct fg_chip, status_change_work); union power_supply_propval prop = {0, }; - int prev_status, rc; + int rc; if (!is_charger_available(chip)) { fg_dbg(chip, FG_STATUS, "Charger not available?!\n"); goto out; } - prev_status = chip->status; rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS, &prop); if (rc < 0) { @@ -1155,14 +1369,25 @@ static void status_change_work(struct work_struct *work) } chip->charge_done = prop.intval; - fg_dbg(chip, FG_POWER_SUPPLY, "prev_status: %d curr_status:%d charge_done: %d\n", - prev_status, chip->status, chip->charge_done); - if (prev_status != chip->status) { - if (chip->cyc_ctr.en) - schedule_work(&chip->cycle_count_work); - fg_cap_learning_update(chip); - } + fg_dbg(chip, FG_POWER_SUPPLY, "curr_status:%d charge_done: %d\n", + chip->status, chip->charge_done); + if (chip->cyc_ctr.en) + schedule_work(&chip->cycle_count_work); + + fg_cap_learning_update(chip); + + rc = fg_charge_full_update(chip); + if (rc < 0) + pr_err("Error in charge_full_update, rc=%d\n", rc); + + rc = fg_adjust_recharge_soc(chip); + if (rc < 0) + pr_err("Error in adjusting recharge_soc, rc=%d\n", rc); + + rc = fg_adjust_ki_coeff_dischg(chip); + if (rc < 0) + pr_err("Error in adjusting ki_coeff_dischg, rc=%d\n", rc); out: pm_relax(chip->dev); } @@ -1247,6 +1472,9 @@ static void cycle_count_work(struct work_struct *work) goto out; } + /* We need only the most significant byte here */ + batt_soc = (u32)batt_soc >> 24; + if (chip->status == POWER_SUPPLY_STATUS_CHARGING) { /* Find out which bucket the SOC falls in */ bucket = batt_soc / BUCKET_SOC_PCT; @@ -1787,16 +2015,9 @@ static int fg_hw_init(struct fg_chip *chip) } if (chip->dt.recharge_soc_thr > 0 && chip->dt.recharge_soc_thr < 100) { - fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, - chip->dt.recharge_soc_thr, buf); - rc = fg_sram_write(chip, - chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_word, - chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_byte, - buf, chip->sp[FG_SRAM_RECHARGE_SOC_THR].len, - FG_IMA_DEFAULT); + rc = fg_set_recharge_soc(chip, chip->dt.recharge_soc_thr); if (rc < 0) { - pr_err("Error in writing recharge_soc_thr, rc=%d\n", - rc); + pr_err("Error in setting recharge_soc, rc=%d\n", rc); return rc; } } @@ -1982,6 +2203,7 @@ static irqreturn_t fg_soc_update_irq_handler(int irq, void *data) static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data) { struct fg_chip *chip = data; + int rc; if (chip->cyc_ctr.en) schedule_work(&chip->cycle_count_work); @@ -1994,6 +2216,14 @@ static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data) if (chip->cl.active) fg_cap_learning_update(chip); + rc = fg_charge_full_update(chip); + if (rc < 0) + pr_err("Error in charge_full_update, rc=%d\n", rc); + + rc = fg_adjust_ki_coeff_dischg(chip); + if (rc < 0) + pr_err("Error in adjusting ki_coeff_dischg, rc=%d\n", rc); + return IRQ_HANDLED; } @@ -2154,6 +2384,73 @@ static int fg_register_interrupts(struct fg_chip *chip) return 0; } +static int fg_parse_ki_coefficients(struct fg_chip *chip) +{ + struct device_node *node = chip->dev->of_node; + int rc, i; + + rc = of_property_count_elems_of_size(node, "qcom,ki-coeff-soc-dischg", + sizeof(u32)); + if (rc != KI_COEFF_SOC_LEVELS) + return 0; + + rc = of_property_read_u32_array(node, "qcom,ki-coeff-soc-dischg", + chip->dt.ki_coeff_soc, KI_COEFF_SOC_LEVELS); + if (rc < 0) { + pr_err("Error in reading ki-coeff-soc-dischg, rc=%d\n", + rc); + return rc; + } + + rc = of_property_count_elems_of_size(node, "qcom,ki-coeff-med-dischg", + sizeof(u32)); + if (rc != KI_COEFF_SOC_LEVELS) + return 0; + + rc = of_property_read_u32_array(node, "qcom,ki-coeff-med-dischg", + chip->dt.ki_coeff_med_dischg, KI_COEFF_SOC_LEVELS); + if (rc < 0) { + pr_err("Error in reading ki-coeff-med-dischg, rc=%d\n", + rc); + return rc; + } + + rc = of_property_count_elems_of_size(node, "qcom,ki-coeff-hi-dischg", + sizeof(u32)); + if (rc != KI_COEFF_SOC_LEVELS) + return 0; + + rc = of_property_read_u32_array(node, "qcom,ki-coeff-hi-dischg", + chip->dt.ki_coeff_hi_dischg, KI_COEFF_SOC_LEVELS); + if (rc < 0) { + pr_err("Error in reading ki-coeff-hi-dischg, rc=%d\n", + rc); + return rc; + } + + for (i = 0; i < KI_COEFF_SOC_LEVELS; i++) { + if (chip->dt.ki_coeff_soc[i] < 0 || + chip->dt.ki_coeff_soc[i] > FULL_CAPACITY) { + pr_err("Error in ki_coeff_soc_dischg values\n"); + return -EINVAL; + } + + if (chip->dt.ki_coeff_med_dischg[i] < 0 || + chip->dt.ki_coeff_med_dischg[i] > KI_COEFF_MAX) { + pr_err("Error in ki_coeff_med_dischg values\n"); + return -EINVAL; + } + + if (chip->dt.ki_coeff_med_dischg[i] < 0 || + chip->dt.ki_coeff_med_dischg[i] > KI_COEFF_MAX) { + pr_err("Error in ki_coeff_med_dischg values\n"); + return -EINVAL; + } + } + chip->ki_coeff_dischg_en = true; + return 0; +} + #define DEFAULT_CUTOFF_VOLT_MV 3200 #define DEFAULT_EMPTY_VOLT_MV 3100 #define DEFAULT_CHG_TERM_CURR_MA 100 @@ -2416,6 +2713,13 @@ static int fg_parse_dt(struct fg_chip *chip) else if (temp > BTEMP_DELTA_LOW && temp <= BTEMP_DELTA_HIGH) chip->dt.batt_temp_delta = temp; + chip->dt.hold_soc_while_full = of_property_read_bool(node, + "qcom,hold-soc-while-full"); + + rc = fg_parse_ki_coefficients(chip); + if (rc < 0) + pr_err("Error in parsing Ki coefficients, rc=%d\n", rc); + return 0; } diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index ad4b6ffef36e..873b4615d4a9 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1498,10 +1498,14 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, /* M-PHY RMMI interface clocks can be turned off */ ufs_qcom_phy_disable_iface_clk(host->generic_phy); - if (!ufs_qcom_is_link_active(hba)) { - if (!is_gating_context) - /* turn off UFS local PHY ref_clk */ - ufs_qcom_phy_disable_ref_clk(host->generic_phy); + /* + * If auto hibern8 is supported then the link will already + * be in hibern8 state and the ref clock can be gated. + */ + if (ufshcd_is_auto_hibern8_supported(hba) || + !ufs_qcom_is_link_active(hba)) { + /* turn off UFS local PHY ref_clk */ + ufs_qcom_phy_disable_ref_clk(host->generic_phy); /* disable device ref_clk */ ufs_qcom_dev_ref_clk_ctrl(host, false); } @@ -1956,13 +1960,6 @@ static int ufs_qcom_init(struct ufs_hba *hba) host->hba = hba; ufshcd_set_variant(hba, host); - /* - * voting/devoting device ref_clk source is time consuming hence - * skip devoting it during aggressive clock gating. This clock - * will still be gated off during runtime suspend. - */ - hba->no_ref_clk_gating = true; - err = ufs_qcom_ice_get_dev(host); if (err == -EPROBE_DEFER) { /* @@ -2570,16 +2567,19 @@ static void ufs_qcom_print_unipro_testbus(struct ufs_hba *hba) kfree(testbus); } -static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba) +static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba, bool no_sleep) { struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct phy *phy = host->generic_phy; ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16, "HCI Vendor Specific Registers "); + ufs_qcom_print_hw_debug_reg_all(hba, NULL, ufs_qcom_dump_regs_wrapper); + + if (no_sleep) + return; /* sleep a bit intermittently as we are dumping too much data */ - ufs_qcom_print_hw_debug_reg_all(hba, NULL, ufs_qcom_dump_regs_wrapper); usleep_range(1000, 1100); ufs_qcom_testbus_read(hba); usleep_range(1000, 1100); diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index d478767ad3dd..862d56e78086 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -539,7 +539,7 @@ static void ufshcd_print_uic_err_hist(struct ufs_hba *hba, } } -static void ufshcd_print_host_regs(struct ufs_hba *hba) +static inline void __ufshcd_print_host_regs(struct ufs_hba *hba, bool no_sleep) { if (!(hba->ufshcd_dbg_print & UFSHCD_DBG_PRINT_HOST_REGS_EN)) return; @@ -571,7 +571,12 @@ static void ufshcd_print_host_regs(struct ufs_hba *hba) ufshcd_print_clk_freqs(hba); - ufshcd_vops_dbg_register_dump(hba); + ufshcd_vops_dbg_register_dump(hba, no_sleep); +} + +static void ufshcd_print_host_regs(struct ufs_hba *hba) +{ + __ufshcd_print_host_regs(hba, false); } static @@ -1176,6 +1181,12 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up) return ret; } +static inline void ufshcd_cancel_gate_work(struct ufs_hba *hba) +{ + hrtimer_cancel(&hba->clk_gating.gate_hrtimer); + cancel_work_sync(&hba->clk_gating.gate_work); +} + static void ufshcd_ungate_work(struct work_struct *work) { int ret; @@ -1183,7 +1194,7 @@ static void ufshcd_ungate_work(struct work_struct *work) struct ufs_hba *hba = container_of(work, struct ufs_hba, clk_gating.ungate_work); - cancel_delayed_work_sync(&hba->clk_gating.gate_work); + ufshcd_cancel_gate_work(hba); spin_lock_irqsave(hba->host->host_lock, flags); if (hba->clk_gating.state == CLKS_ON) { @@ -1254,14 +1265,18 @@ start: } break; case REQ_CLKS_OFF: - if (cancel_delayed_work(&hba->clk_gating.gate_work)) { + /* + * If the timer was active but the callback was not running + * we have nothing to do, just change state and return. + */ + if (hrtimer_try_to_cancel(&hba->clk_gating.gate_hrtimer) == 1) { hba->clk_gating.state = CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); break; } /* - * If we here, it means gating work is either done or + * If we are here, it means gating work is either done or * currently running. Hence, fall through to cancel gating * work and to enable clocks. */ @@ -1301,7 +1316,7 @@ EXPORT_SYMBOL_GPL(ufshcd_hold); static void ufshcd_gate_work(struct work_struct *work) { struct ufs_hba *hba = container_of(work, struct ufs_hba, - clk_gating.gate_work.work); + clk_gating.gate_work); unsigned long flags; spin_lock_irqsave(hba->host->host_lock, flags); @@ -1346,7 +1361,12 @@ static void ufshcd_gate_work(struct work_struct *work) ufshcd_set_link_hibern8(hba); } - if (!ufshcd_is_link_active(hba) && !hba->no_ref_clk_gating) + /* + * If auto hibern8 is supported then the link will already + * be in hibern8 state and the ref clock can be gated. + */ + if ((ufshcd_is_auto_hibern8_supported(hba) || + !ufshcd_is_link_active(hba)) && !hba->no_ref_clk_gating) ufshcd_disable_clocks(hba, true); else /* If link is active, device ref_clk can't be switched off */ @@ -1394,8 +1414,9 @@ static void __ufshcd_release(struct ufs_hba *hba, bool no_sched) hba->clk_gating.state = REQ_CLKS_OFF; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); - schedule_delayed_work(&hba->clk_gating.gate_work, - msecs_to_jiffies(hba->clk_gating.delay_ms)); + hrtimer_start(&hba->clk_gating.gate_hrtimer, + ms_to_ktime(hba->clk_gating.delay_ms), + HRTIMER_MODE_REL); } void ufshcd_release(struct ufs_hba *hba, bool no_sched) @@ -1523,6 +1544,17 @@ out: return count; } +static enum hrtimer_restart ufshcd_clkgate_hrtimer_handler( + struct hrtimer *timer) +{ + struct ufs_hba *hba = container_of(timer, struct ufs_hba, + clk_gating.gate_hrtimer); + + schedule_work(&hba->clk_gating.gate_work); + + return HRTIMER_NORESTART; +} + static void ufshcd_init_clk_gating(struct ufs_hba *hba) { struct ufs_clk_gating *gating = &hba->clk_gating; @@ -1539,27 +1571,25 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba) if (ufshcd_is_auto_hibern8_supported(hba)) hba->caps &= ~UFSHCD_CAP_HIBERN8_WITH_CLK_GATING; - INIT_DELAYED_WORK(&gating->gate_work, ufshcd_gate_work); + INIT_WORK(&gating->gate_work, ufshcd_gate_work); INIT_WORK(&gating->ungate_work, ufshcd_ungate_work); + /* + * Clock gating work must be executed only after auto hibern8 + * timeout has expired in the hardware or after aggressive + * hibern8 on idle software timeout. Using jiffy based low + * resolution delayed work is not reliable to guarantee this, + * hence use a high resolution timer to make sure we schedule + * the gate work precisely more than hibern8 timeout. + * + * Always make sure gating->delay_ms > hibern8_on_idle->delay_ms + */ + hrtimer_init(&gating->gate_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + gating->gate_hrtimer.function = ufshcd_clkgate_hrtimer_handler; gating->is_enabled = true; - /* - * Scheduling the delayed work after 1 jiffies will make the work to - * get schedule any time from 0ms to 1000/HZ ms which is not desirable - * for hibern8 enter work as it may impact the performance if it gets - * scheduled almost immediately. Hence make sure that hibern8 enter - * work gets scheduled atleast after 2 jiffies (any time between - * 1000/HZ ms to 2000/HZ ms). - */ - gating->delay_ms_pwr_save = jiffies_to_msecs( - max_t(unsigned long, - msecs_to_jiffies(UFSHCD_CLK_GATING_DELAY_MS_PWR_SAVE), - 2)); - gating->delay_ms_perf = jiffies_to_msecs( - max_t(unsigned long, - msecs_to_jiffies(UFSHCD_CLK_GATING_DELAY_MS_PERF), - 2)); + gating->delay_ms_pwr_save = UFSHCD_CLK_GATING_DELAY_MS_PWR_SAVE; + gating->delay_ms_perf = UFSHCD_CLK_GATING_DELAY_MS_PERF; /* start with performance mode */ gating->delay_ms = gating->delay_ms_perf; @@ -1616,8 +1646,8 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) device_remove_file(hba->dev, &hba->clk_gating.delay_attr); } device_remove_file(hba->dev, &hba->clk_gating.enable_attr); + ufshcd_cancel_gate_work(hba); cancel_work_sync(&hba->clk_gating.ungate_work); - cancel_delayed_work_sync(&hba->clk_gating.gate_work); } static void ufshcd_set_auto_hibern8_timer(struct ufs_hba *hba, u32 delay) @@ -1928,6 +1958,7 @@ static void ufshcd_init_hibern8_on_idle(struct ufs_hba *hba) return; if (ufshcd_is_auto_hibern8_supported(hba)) { + hba->hibern8_on_idle.delay_ms = 1; hba->hibern8_on_idle.state = AUTO_HIBERN8; /* * Disable SW hibern8 enter on idle in case @@ -1935,13 +1966,13 @@ static void ufshcd_init_hibern8_on_idle(struct ufs_hba *hba) */ hba->caps &= ~UFSHCD_CAP_HIBERN8_ENTER_ON_IDLE; } else { + hba->hibern8_on_idle.delay_ms = 10; INIT_DELAYED_WORK(&hba->hibern8_on_idle.enter_work, ufshcd_hibern8_enter_work); INIT_WORK(&hba->hibern8_on_idle.exit_work, ufshcd_hibern8_exit_work); } - hba->hibern8_on_idle.delay_ms = 10; hba->hibern8_on_idle.is_enabled = true; hba->hibern8_on_idle.delay_attr.show = @@ -5029,7 +5060,12 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) dev_err(hba->dev, "OCS error from controller = %x for tag %d\n", ocs, lrbp->task_tag); - ufshcd_print_host_regs(hba); + /* + * This is called in interrupt context, hence avoid sleep + * while printing debug registers. Also print only the minimum + * debug registers needed to debug OCS failure. + */ + __ufshcd_print_host_regs(hba, true); ufshcd_print_host_state(hba); break; } /* end of switch */ diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index a6298f614a0b..c0714b7bea72 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -39,6 +39,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/hrtimer.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -334,7 +335,7 @@ struct ufs_hba_variant_ops { int (*suspend)(struct ufs_hba *, enum ufs_pm_op); int (*resume)(struct ufs_hba *, enum ufs_pm_op); int (*full_reset)(struct ufs_hba *); - void (*dbg_register_dump)(struct ufs_hba *hba); + void (*dbg_register_dump)(struct ufs_hba *hba, bool no_sleep); int (*update_sec_cfg)(struct ufs_hba *hba, bool restore_sec_cfg); u32 (*get_scale_down_gear)(struct ufs_hba *); int (*set_bus_vote)(struct ufs_hba *, bool); @@ -396,8 +397,9 @@ enum clk_gating_state { /** * struct ufs_clk_gating - UFS clock gating related info - * @gate_work: worker to turn off clocks after some delay as specified in - * delay_ms + * @gate_hrtimer: hrtimer to invoke @gate_work after some delay as + * specified in @delay_ms + * @gate_work: worker to turn off clocks * @ungate_work: worker to turn on clocks that will be used in case of * interrupt context * @state: the current clocks state @@ -415,7 +417,8 @@ enum clk_gating_state { * completion before gating clocks. */ struct ufs_clk_gating { - struct delayed_work gate_work; + struct hrtimer gate_hrtimer; + struct work_struct gate_work; struct work_struct ungate_work; enum clk_gating_state state; unsigned long delay_ms; @@ -1241,10 +1244,11 @@ static inline int ufshcd_vops_full_reset(struct ufs_hba *hba) } -static inline void ufshcd_vops_dbg_register_dump(struct ufs_hba *hba) +static inline void ufshcd_vops_dbg_register_dump(struct ufs_hba *hba, + bool no_sleep) { if (hba->var && hba->var->vops && hba->var->vops->dbg_register_dump) - hba->var->vops->dbg_register_dump(hba); + hba->var->vops->dbg_register_dump(hba, no_sleep); } static inline int ufshcd_vops_update_sec_cfg(struct ufs_hba *hba, diff --git a/drivers/soc/qcom/core_hang_detect.c b/drivers/soc/qcom/core_hang_detect.c index e9b7f612dccc..c88d4c34eecf 100644 --- a/drivers/soc/qcom/core_hang_detect.c +++ b/drivers/soc/qcom/core_hang_detect.c @@ -245,7 +245,9 @@ static int msm_hang_detect_probe(struct platform_device *pdev) struct device_node *node = pdev->dev.of_node; struct hang_detect *hang_det = NULL; int cpu, ret, cpu_count = 0; - u32 treg[NR_CPUS], creg[NR_CPUS]; + const char *name; + u32 treg[NR_CPUS] = {0}, creg[NR_CPUS] = {0}; + u32 num_reg = 0; if (!pdev->dev.of_node || !enable) return -ENODEV; @@ -258,15 +260,28 @@ static int msm_hang_detect_probe(struct platform_device *pdev) return -ENOMEM; } + name = of_get_property(node, "label", NULL); + if (!name) { + pr_err("Can't get label property\n"); + return -EINVAL; + } + + num_reg = of_property_count_u32_elems(node, + "qcom,threshold-arr"); + if (num_reg < 0) { + pr_err("Can't get threshold-arr property\n"); + return -EINVAL; + } + ret = of_property_read_u32_array(node, "qcom,threshold-arr", - treg, num_possible_cpus()); + treg, num_reg); if (ret) { pr_err("Can't get threshold-arr property\n"); return -EINVAL; } ret = of_property_read_u32_array(node, "qcom,config-arr", - creg, num_possible_cpus()); + creg, num_reg); if (ret) { pr_err("Can't get config-arr property\n"); return -EINVAL; @@ -289,7 +304,8 @@ static int msm_hang_detect_probe(struct platform_device *pdev) } ret = kobject_init_and_add(&hang_det->kobj, &core_ktype, - &cpu_subsys.dev_root->kobj, "%s", "hang_detect"); + &cpu_subsys.dev_root->kobj, "%s_%s", + "hang_detect", name); if (ret) { pr_err("%s:Error in creation kobject_add\n", __func__); goto out_put_kobj; diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c index b7fa71dd0695..9cfca014c8ad 100644 --- a/drivers/soc/qcom/glink.c +++ b/drivers/soc/qcom/glink.c @@ -2583,6 +2583,7 @@ void *glink_open(const struct glink_open_config *cfg) ctx->notify_tx_abort = cfg->notify_tx_abort; ctx->notify_rx_tracer_pkt = cfg->notify_rx_tracer_pkt; ctx->notify_remote_rx_intent = cfg->notify_remote_rx_intent; + ctx->magic_number = GLINK_CTX_CANARY; if (!ctx->notify_rx_intent_req) ctx->notify_rx_intent_req = glink_dummy_notify_rx_intent_req; @@ -2618,7 +2619,6 @@ void *glink_open(const struct glink_open_config *cfg) GLINK_INFO_CH(ctx, "%s: Created channel, sent OPEN command. ctx %p\n", __func__, ctx); - ctx->magic_number = GLINK_CTX_CANARY; return ctx; } EXPORT_SYMBOL(glink_open); @@ -5380,7 +5380,7 @@ static int glink_scheduler_tx(struct channel_ctx *ctx, size_t txd_len = 0; size_t tx_len = 0; uint32_t num_pkts = 0; - int ret; + int ret = 0; spin_lock_irqsave(&ctx->tx_lists_lock_lhc3, flags); while (txd_len < xprt_ctx->mtu && diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 2aa588ba610b..f47d4a51fccd 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -33,7 +33,6 @@ #include <linux/dma-mapping.h> #include <linux/qmi_encdec.h> #include <linux/ipc_logging.h> -#include <linux/msm-bus.h> #include <linux/thread_info.h> #include <linux/uaccess.h> #include <linux/qpnp/qpnp-adc.h> @@ -411,8 +410,6 @@ static struct icnss_priv { size_t smmu_iova_len; dma_addr_t smmu_iova_ipa_start; size_t smmu_iova_ipa_len; - struct msm_bus_scale_pdata *bus_scale_table; - uint32_t bus_client; struct qmi_handle *wlfw_clnt; struct list_head event_list; spinlock_t event_lock; @@ -3410,62 +3407,6 @@ unsigned int icnss_socinfo_get_serial_number(struct device *dev) } EXPORT_SYMBOL(icnss_socinfo_get_serial_number); -static int icnss_bw_vote(struct icnss_priv *priv, int index) -{ - int ret = 0; - - icnss_pr_dbg("Vote %d for msm_bus, state 0x%lx\n", - index, priv->state); - ret = msm_bus_scale_client_update_request(priv->bus_client, index); - if (ret) - icnss_pr_err("Fail to vote %d: ret %d, state 0x%lx\n", - index, ret, priv->state); - - return ret; -} - -static int icnss_bw_init(struct icnss_priv *priv) -{ - int ret = 0; - - priv->bus_scale_table = msm_bus_cl_get_pdata(priv->pdev); - if (!priv->bus_scale_table) { - icnss_pr_err("Missing entry for msm_bus scale table\n"); - return -EINVAL; - } - - priv->bus_client = msm_bus_scale_register_client(priv->bus_scale_table); - if (!priv->bus_client) { - icnss_pr_err("Fail to register with bus_scale client\n"); - ret = -EINVAL; - goto out; - } - - ret = icnss_bw_vote(priv, 1); - if (ret) - goto out; - - return 0; - -out: - msm_bus_cl_clear_pdata(priv->bus_scale_table); - return ret; -} - -static void icnss_bw_deinit(struct icnss_priv *priv) -{ - if (!priv) - return; - - if (priv->bus_client) { - icnss_bw_vote(priv, 0); - msm_bus_scale_unregister_client(priv->bus_client); - } - - if (priv->bus_scale_table) - msm_bus_cl_clear_pdata(priv->bus_scale_table); -} - static int icnss_smmu_init(struct icnss_priv *priv) { struct dma_iommu_mapping *mapping; @@ -4419,10 +4360,6 @@ static int icnss_probe(struct platform_device *pdev) priv->smmu_iova_len); goto out; } - - ret = icnss_bw_init(priv); - if (ret) - goto out_smmu_deinit; } spin_lock_init(&priv->event_lock); @@ -4432,7 +4369,7 @@ static int icnss_probe(struct platform_device *pdev) if (!priv->event_wq) { icnss_pr_err("Workqueue creation failed\n"); ret = -EFAULT; - goto out_bw_deinit; + goto out_smmu_deinit; } INIT_WORK(&priv->event_work, icnss_driver_event_work); @@ -4460,8 +4397,6 @@ static int icnss_probe(struct platform_device *pdev) out_destroy_wq: destroy_workqueue(priv->event_wq); -out_bw_deinit: - icnss_bw_deinit(priv); out_smmu_deinit: icnss_smmu_deinit(priv); out: @@ -4487,8 +4422,6 @@ static int icnss_remove(struct platform_device *pdev) if (penv->event_wq) destroy_workqueue(penv->event_wq); - icnss_bw_deinit(penv); - icnss_hw_power_off(penv); dev_set_drvdata(&pdev->dev, NULL); diff --git a/drivers/soc/qcom/msm_smem.c b/drivers/soc/qcom/msm_smem.c index 881359d444fc..cd3d387645fd 100644 --- a/drivers/soc/qcom/msm_smem.c +++ b/drivers/soc/qcom/msm_smem.c @@ -79,6 +79,7 @@ static int spinlocks_initialized; static void *smem_ramdump_dev; static DEFINE_MUTEX(spinlock_init_lock); static DEFINE_SPINLOCK(smem_init_check_lock); +static struct device *smem_dev; static int smem_module_inited; static RAW_NOTIFIER_HEAD(smem_module_init_notifier_list); static DEFINE_MUTEX(smem_module_init_notifier_lock); @@ -1047,7 +1048,8 @@ static __init int modem_restart_late_init(void) void *handle; struct restart_notifier_block *nb; - smem_ramdump_dev = create_ramdump_device("smem", NULL); + if (smem_dev) + smem_ramdump_dev = create_ramdump_device("smem", smem_dev); if (IS_ERR_OR_NULL(smem_ramdump_dev)) { LOG_ERR("%s: Unable to create smem ramdump device.\n", __func__); @@ -1444,7 +1446,7 @@ smem_targ_info_done: SMEM_INFO("smem security enabled\n"); smem_init_security(); } - + smem_dev = &pdev->dev; probe_done = true; ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); diff --git a/drivers/soc/qcom/qdsp6v2/voice_svc.c b/drivers/soc/qcom/qdsp6v2/voice_svc.c index 67c58d1e6d4c..50dd9256b270 100644 --- a/drivers/soc/qcom/qdsp6v2/voice_svc.c +++ b/drivers/soc/qcom/qdsp6v2/voice_svc.c @@ -223,8 +223,8 @@ static int voice_svc_send_req(struct voice_svc_cmd_request *apr_request, } else if (!strcmp(apr_request->svc_name, VOICE_SVC_MVM_STR)) { apr_handle = prtd->apr_q6_mvm; } else { - pr_err("%s: Invalid service %s\n", __func__, - apr_request->svc_name); + pr_err("%s: Invalid service %.*s\n", __func__, + MAX_APR_SERVICE_NAME_LEN, apr_request->svc_name); ret = -EINVAL; goto done; @@ -338,8 +338,8 @@ static int process_reg_cmd(struct voice_svc_register *apr_reg_svc, svc = VOICE_SVC_CVS_STR; handle = &prtd->apr_q6_cvs; } else { - pr_err("%s: Invalid Service: %s\n", __func__, - apr_reg_svc->svc_name); + pr_err("%s: Invalid Service: %.*s\n", __func__, + MAX_APR_SERVICE_NAME_LEN, apr_reg_svc->svc_name); ret = -EINVAL; goto done; } @@ -365,7 +365,17 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf, pr_debug("%s\n", __func__); - data = kmalloc(count, GFP_KERNEL); + /* + * Check if enough memory is allocated to parse the message type. + * Will check there is enough to hold the payload later. + */ + if (count >= sizeof(struct voice_svc_write_msg)) { + data = kmalloc(count, GFP_KERNEL); + } else { + pr_debug("%s: invalid data size\n", __func__); + ret = -EINVAL; + goto done; + } if (data == NULL) { pr_err("%s: data kmalloc failed.\n", __func__); @@ -383,7 +393,7 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf, } cmd = data->msg_type; - prtd = (struct voice_svc_prvt *)file->private_data; + prtd = (struct voice_svc_prvt *) file->private_data; if (prtd == NULL) { pr_err("%s: prtd is NULL\n", __func__); @@ -393,9 +403,13 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf, switch (cmd) { case MSG_REGISTER: - if (count >= - (sizeof(struct voice_svc_register) + - sizeof(*data))) { + /* + * Check that count reflects the expected size to ensure + * sufficient memory was allocated. Since voice_svc_register + * has a static size, this should be exact. + */ + if (count == (sizeof(struct voice_svc_write_msg) + + sizeof(struct voice_svc_register))) { ret = process_reg_cmd( (struct voice_svc_register *)data->payload, prtd); if (!ret) @@ -407,8 +421,13 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf, } break; case MSG_REQUEST: - if (count >= (sizeof(struct voice_svc_cmd_request) + - sizeof(*data))) { + /* + * Check that count reflects the expected size to ensure + * sufficient memory was allocated. Since voice_svc_cmd_request + * has a variable size, check the minimum value count must be. + */ + if (count >= (sizeof(struct voice_svc_write_msg) + + sizeof(struct voice_svc_cmd_request))) { ret = voice_svc_send_req( (struct voice_svc_cmd_request *)data->payload, prtd); if (!ret) diff --git a/drivers/soc/qcom/qsee_ipc_irq_bridge.c b/drivers/soc/qcom/qsee_ipc_irq_bridge.c index ab43bbb7e86a..eee42d7ba314 100644 --- a/drivers/soc/qcom/qsee_ipc_irq_bridge.c +++ b/drivers/soc/qcom/qsee_ipc_irq_bridge.c @@ -115,10 +115,8 @@ static struct qiib_driver_data *qiib_info; static int qiib_driver_data_init(void) { qiib_info = kzalloc(sizeof(*qiib_info), GFP_KERNEL); - if (!qiib_info) { - QIIB_ERR("Unable to allocate info pointer\n"); + if (!qiib_info) return -ENOMEM; - } INIT_LIST_HEAD(&qiib_info->list); mutex_init(&qiib_info->list_lock); @@ -356,6 +354,7 @@ static int qiib_parse_node(struct device_node *node, struct qiib_dev *devp) const char *dev_name; uint32_t irqtype; uint32_t irq_clear[2]; + struct irq_data *irqtype_data; int ret = -ENODEV; key = "qcom,dev-name"; @@ -374,7 +373,12 @@ static int qiib_parse_node(struct device_node *node, struct qiib_dev *devp) } QIIB_DBG("%s: %s = %d\n", __func__, key, devp->irq_line); - irqtype = irqd_get_trigger_type(irq_get_irq_data(devp->irq_line)); + irqtype_data = irq_get_irq_data(devp->irq_line); + if (!irqtype_data) { + QIIB_ERR("%s: get irqdata fail:%d\n", __func__, devp->irq_line); + goto missing_key; + } + irqtype = irqd_get_trigger_type(irqtype_data); QIIB_DBG("%s: irqtype = %d\n", __func__, irqtype); key = "label"; diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 8a0e7f988d25..0121bf7ca4ac 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1111,7 +1111,7 @@ static struct usb_function_instance *f_midi_alloc_inst(void) opts->func_inst.free_func_inst = f_midi_free_inst; opts->index = SNDRV_DEFAULT_IDX1; opts->id = SNDRV_DEFAULT_STR1; - opts->buflen = 256; + opts->buflen = 1024; opts->qlen = 32; opts->in_ports = 1; opts->out_ports = 1; diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 12b98017beb2..26c91e0ce163 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -59,6 +59,7 @@ enum usbpd_state { PE_PRS_SRC_SNK_SEND_SWAP, PE_PRS_SRC_SNK_TRANSITION_TO_OFF, PE_PRS_SRC_SNK_WAIT_SOURCE_ON, + PE_VCS_WAIT_FOR_VCONN, }; static const char * const usbpd_state_strings[] = { @@ -94,6 +95,7 @@ static const char * const usbpd_state_strings[] = { "PRS_SRC_SNK_Send_Swap", "PRS_SRC_SNK_Transition_to_off", "PRS_SRC_SNK_Wait_Source_on", + "VCS_Wait_for_VCONN", }; enum usbpd_control_msg_type { @@ -168,7 +170,12 @@ static void *usbpd_ipc_log; #define PS_HARD_RESET_TIME 25 #define PS_SOURCE_ON 400 #define PS_SOURCE_OFF 750 +#define SWAP_SOURCE_START_TIME 20 #define VDM_BUSY_TIME 50 +#define VCONN_ON_TIME 100 + +/* tPSHardReset + tSafe0V + tSrcRecover + tSrcTurnOn */ +#define SNK_HARD_RESET_RECOVER_TIME (35 + 650 + 1000 + 275) #define PD_CAPS_COUNT 50 @@ -191,7 +198,7 @@ static void *usbpd_ipc_log; #define PD_RDO_MISMATCH(rdo) ((rdo) >> 26 & 1) #define PD_RDO_USB_COMM(rdo) ((rdo) >> 25 & 1) #define PD_RDO_NO_USB_SUSP(rdo) ((rdo) >> 24 & 1) -#define PD_RDO_FIXED_CURR(rdo) ((rdo) >> 19 & 0x3FF) +#define PD_RDO_FIXED_CURR(rdo) ((rdo) >> 10 & 0x3FF) #define PD_RDO_FIXED_CURR_MINMAX(rdo) ((rdo) & 0x3FF) #define PD_SRC_PDO_TYPE(pdo) (((pdo) >> 30) & 3) @@ -219,9 +226,10 @@ static void *usbpd_ipc_log; /* VDM header is the first 32-bit object following the 16-bit PD header */ #define VDM_HDR_SVID(hdr) ((hdr) >> 16) -#define VDM_HDR_TYPE(hdr) ((hdr) & 0x8000) -#define VDM_HDR_CMD_TYPE(hdr) (((hdr) >> 6) & 0x3) -#define VDM_HDR_CMD(hdr) ((hdr) & 0x1f) +#define VDM_IS_SVDM(hdr) ((hdr) & 0x8000) +#define SVDM_HDR_OBJ_POS(hdr) (((hdr) >> 8) & 0x7) +#define SVDM_HDR_CMD_TYPE(hdr) (((hdr) >> 6) & 0x3) +#define SVDM_HDR_CMD(hdr) ((hdr) & 0x1f) #define SVDM_HDR(svid, ver, obj, cmd_type, cmd) \ (((svid) << 16) | (1 << 15) | ((ver) << 13) \ @@ -249,15 +257,11 @@ static bool ss_dev = true; module_param(ss_dev, bool, S_IRUSR | S_IWUSR); static const u32 default_src_caps[] = { 0x36019096 }; /* VSafe5V @ 1.5A */ - -static const u32 default_snk_caps[] = { 0x2601905A, /* 5V @ 900mA */ - 0x0002D096, /* 9V @ 1.5A */ - 0x0003C064 }; /* 12V @ 1A */ +static const u32 default_snk_caps[] = { 0x2601905A }; /* 5V @ 900mA */ struct vdm_tx { u32 data[7]; int size; - struct list_head entry; }; struct usbpd { @@ -265,6 +269,7 @@ struct usbpd { struct workqueue_struct *wq; struct work_struct sm_work; struct hrtimer timer; + bool sm_queued; struct extcon_dev *extcon; @@ -294,6 +299,7 @@ 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; @@ -303,6 +309,7 @@ struct usbpd { struct regulator *vbus; struct regulator *vconn; bool vconn_enabled; + bool vconn_is_external; u8 tx_msgid; u8 rx_msgid; @@ -311,8 +318,9 @@ struct usbpd { enum vdm_state vdm_state; u16 *discovered_svids; + int num_svids; + struct vdm_tx *vdm_tx; struct vdm_tx *vdm_tx_retry; - struct list_head vdm_tx_queue; struct list_head svid_handlers; struct list_head instance; @@ -436,6 +444,12 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos) } pd->requested_voltage = PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50 * 1000; + + /* Can't sink more than 5V if VCONN is sourced from the VBUS input */ + if (pd->vconn_enabled && !pd->vconn_is_external && + pd->requested_voltage > 5000000) + return -ENOTSUPP; + pd->requested_current = curr; pd->requested_pdo = pdo_pos; pd->rdo = PD_RDO_FIXED(pdo_pos, 0, mismatch, 1, 1, curr / 10, @@ -446,6 +460,7 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos) static int pd_eval_src_caps(struct usbpd *pd, const u32 *src_caps) { + union power_supply_propval val; u32 first_pdo = src_caps[0]; /* save the PDOs so userspace can further evaluate */ @@ -461,6 +476,10 @@ static int pd_eval_src_caps(struct usbpd *pd, const u32 *src_caps) pd->peer_pr_swap = PD_SRC_PDO_FIXED_PR_SWAP(first_pdo); pd->peer_dr_swap = PD_SRC_PDO_FIXED_DR_SWAP(first_pdo); + val.intval = PD_SRC_PDO_FIXED_USB_SUSP(first_pdo); + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, &val); + /* Select the first PDO (vSafe5V) immediately. */ pd_select_pdo(pd, 1); @@ -481,6 +500,17 @@ static void pd_send_hard_reset(struct usbpd *pd) pd->hard_reset = true; } +static void kick_sm(struct usbpd *pd, int ms) +{ + pm_stay_awake(&pd->dev); + pd->sm_queued = true; + + if (ms) + hrtimer_start(&pd->timer, ms_to_ktime(ms), HRTIMER_MODE_REL); + else + queue_work(pd->wq, &pd->sm_work); +} + static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) { if (type != HARD_RESET_SIG) { @@ -493,7 +523,7 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) /* Force CC logic to source/sink to keep Rp/Rd unchanged */ set_power_role(pd, pd->current_pr); pd->hard_reset = true; - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); } static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, @@ -530,6 +560,10 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, pd->rx_msgid = PD_MSG_HDR_ID(header); + /* discard Pings */ + if (PD_MSG_HDR_TYPE(header) == MSG_PING && !len) + return; + /* check header's count field to see if it matches len */ if (PD_MSG_HDR_COUNT(header) != (len / 4)) { usbpd_err(&pd->dev, "header count (%d) mismatch, len=%ld\n", @@ -537,11 +571,18 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, return; } + /* block until previous message has been consumed by usbpd_sm */ + if (pd->rx_msg_type) + flush_work(&pd->sm_work); + pd->rx_msg_type = PD_MSG_HDR_TYPE(header); pd->rx_msg_len = PD_MSG_HDR_COUNT(header); memcpy(&pd->rx_payload, buf, len); - queue_work(pd->wq, &pd->sm_work); + usbpd_dbg(&pd->dev, "received message: type(%d) len(%d)\n", + pd->rx_msg_type, pd->rx_msg_len); + + kick_sm(pd, 0); } static void phy_shutdown(struct usbpd *pd) @@ -583,16 +624,24 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->in_pr_swap = false; set_power_role(pd, PR_NONE); pd->typec_mode = POWER_SUPPLY_TYPEC_NONE; - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); break; /* Source states */ case PE_SRC_STARTUP: if (pd->current_dr == DR_NONE) { pd->current_dr = DR_DFP; - /* Defer starting USB host mode until after PD */ + /* + * Defer starting USB host mode until PE_SRC_READY or + * when PE_SRC_SEND_CAPABILITIES fails + */ } + /* Set CC back to DRP toggle for the next disconnect */ + val.intval = POWER_SUPPLY_TYPEC_PR_DUAL; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); + pd->rx_msg_len = 0; pd->rx_msg_type = 0; pd->rx_msgid = -1; @@ -618,22 +667,24 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->pd_phy_opened = true; } - val.intval = 1; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PD_ACTIVE, &val); - - pd->in_pr_swap = false; pd->current_state = PE_SRC_SEND_CAPABILITIES; - usbpd_dbg(&pd->dev, "Enter %s\n", - usbpd_state_strings[pd->current_state]); + if (pd->in_pr_swap) { + kick_sm(pd, SWAP_SOURCE_START_TIME); + break; + } + /* fall-through */ case PE_SRC_SEND_CAPABILITIES: - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); break; case PE_SRC_NEGOTIATE_CAPABILITY: - if (PD_RDO_OBJ_POS(pd->rdo) != 1) { + if (PD_RDO_OBJ_POS(pd->rdo) != 1 || + PD_RDO_FIXED_CURR(pd->rdo) > + PD_SRC_PDO_FIXED_MAX_CURR(*default_src_caps) || + PD_RDO_FIXED_CURR_MINMAX(pd->rdo) > + PD_SRC_PDO_FIXED_MAX_CURR(*default_src_caps)) { /* send Reject */ ret = pd_send_msg(pd, MSG_REJECT, NULL, 0, SOP_MSG); if (ret) { @@ -703,6 +754,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) break; case PE_SRC_TRANSITION_TO_DEFAULT: + pd->hard_reset = false; + if (pd->vconn_enabled) regulator_disable(pd->vconn); regulator_disable(pd->vbus); @@ -727,13 +780,17 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) } } + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); + usbpd_set_state(pd, PE_SRC_STARTUP); break; case PE_SRC_HARD_RESET: case PE_SNK_HARD_RESET: /* hard reset may sleep; handle it in the workqueue */ - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); break; case PE_SRC_SEND_SOFT_RESET: @@ -751,8 +808,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) } /* wait for ACCEPT */ - hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, SENDER_RESPONSE_TIME); break; /* Sink states */ @@ -772,9 +828,14 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) } } + if (!pd->pd_allowed) + break; + + /* Reset protocol layer */ + pd->tx_msgid = 0; + pd->rx_msgid = -1; pd->rx_msg_len = 0; pd->rx_msg_type = 0; - pd->rx_msgid = -1; if (!pd->in_pr_swap) { if (pd->pd_phy_opened) { @@ -797,31 +858,16 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->pd_phy_opened = true; } - pd->in_pr_swap = false; pd->current_voltage = 5000000; - if (!pd->vbus_present) { - /* can get here during a hard reset and we lost vbus */ - pd->current_state = PE_SNK_DISCOVERY; - hrtimer_start(&pd->timer, ms_to_ktime(2000), - HRTIMER_MODE_REL); - break; - } - - /* - * If VBUS is already present go and skip ahead to - * PE_SNK_WAIT_FOR_CAPABILITIES. - */ pd->current_state = PE_SNK_WAIT_FOR_CAPABILITIES; /* fall-through */ case PE_SNK_WAIT_FOR_CAPABILITIES: if (pd->rx_msg_len && pd->rx_msg_type) - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); else - hrtimer_start(&pd->timer, - ms_to_ktime(SINK_WAIT_CAP_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, SINK_WAIT_CAP_TIME); break; case PE_SNK_EVALUATE_CAPABILITY: @@ -839,18 +885,19 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) case PE_SNK_SELECT_CAPABILITY: ret = pd_send_msg(pd, MSG_REQUEST, &pd->rdo, 1, SOP_MSG); - if (ret) + if (ret) { usbpd_err(&pd->dev, "Error sending Request\n"); + usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); + break; + } /* wait for ACCEPT */ - hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, SENDER_RESPONSE_TIME); break; case PE_SNK_TRANSITION_SINK: /* wait for PS_RDY */ - hrtimer_start(&pd->timer, ms_to_ktime(PS_TRANSITION_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, PS_TRANSITION_TIME); break; case PE_SNK_READY: @@ -859,6 +906,8 @@ 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); @@ -875,15 +924,30 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->vconn_enabled = false; } - pd->tx_msgid = 0; - val.intval = pd->requested_voltage; /* set range back to 5V */ power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_VOLTAGE_MAX, &val); pd->current_voltage = pd->requested_voltage; - /* recursive call; go back to beginning state */ - usbpd_set_state(pd, PE_SNK_STARTUP); + /* max time for hard reset to toggle vbus off/on */ + kick_sm(pd, SNK_HARD_RESET_RECOVER_TIME); + break; + + case PE_PRS_SNK_SRC_TRANSITION_TO_OFF: + val.intval = pd->requested_current = 0; /* suspend charging */ + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, &val); + + pd->in_explicit_contract = false; + + /* + * need to update PR bit in message header so that + * proper GoodCRC is sent when receiving next PS_RDY + */ + pd_phy_update_roles(pd->current_dr, PR_SRC); + + /* wait for PS_RDY */ + kick_sm(pd, PS_SOURCE_OFF); break; default: @@ -907,10 +971,10 @@ int usbpd_register_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr) /* already connected with this SVID discovered? */ if (pd->vdm_state >= DISCOVERED_SVIDS) { - u16 *psvid; + int i; - for (psvid = pd->discovered_svids; *psvid; psvid++) { - if (*psvid == hdlr->svid) { + for (i = 0; i < pd->num_svids; i++) { + if (pd->discovered_svids[i] == hdlr->svid) { if (hdlr->connect) hdlr->connect(hdlr); break; @@ -932,7 +996,7 @@ int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos) { struct vdm_tx *vdm_tx; - if (!pd->in_explicit_contract) + if (!pd->in_explicit_contract || pd->vdm_tx) return -EBUSY; vdm_tx = kzalloc(sizeof(*vdm_tx), GFP_KERNEL); @@ -945,8 +1009,10 @@ int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos) vdm_tx->size = num_vdos + 1; /* include the header */ /* VDM will get sent in PE_SRC/SNK_READY state handling */ - list_add_tail(&vdm_tx->entry, &pd->vdm_tx_queue); - queue_work(pd->wq, &pd->sm_work); + pd->vdm_tx = vdm_tx; + + /* slight delay before queuing to prioritize handling of incoming VDM */ + kick_sm(pd, 5); return 0; } @@ -972,8 +1038,8 @@ static void handle_vdm_rx(struct usbpd *pd) u16 svid = VDM_HDR_SVID(vdm_hdr); u16 *psvid; u8 i, num_vdos = pd->rx_msg_len - 1; /* num objects minus header */ - u8 cmd = VDM_HDR_CMD(vdm_hdr); - u8 cmd_type = VDM_HDR_CMD_TYPE(vdm_hdr); + u8 cmd = SVDM_HDR_CMD(vdm_hdr); + u8 cmd_type = SVDM_HDR_CMD_TYPE(vdm_hdr); struct usbpd_svid_handler *handler; usbpd_dbg(&pd->dev, "VDM rx: svid:%x cmd:%x cmd_type:%x vdm_hdr:%x\n", @@ -983,7 +1049,7 @@ static void handle_vdm_rx(struct usbpd *pd) handler = find_svid_handler(pd, svid); /* Unstructured VDM */ - if (!VDM_HDR_TYPE(vdm_hdr)) { + if (!VDM_IS_SVDM(vdm_hdr)) { if (handler && handler->vdm_received) handler->vdm_received(handler, vdm_hdr, vdos, num_vdos); return; @@ -994,7 +1060,19 @@ static void handle_vdm_rx(struct usbpd *pd) switch (cmd_type) { case SVDM_CMD_TYPE_INITIATOR: - if (cmd == USBPD_SVDM_DISCOVER_IDENTITY) { + /* + * if this interrupts a previous exchange, abort the previous + * outgoing response + */ + if (pd->vdm_tx) { + usbpd_dbg(&pd->dev, "Discarding previously queued SVDM tx (SVID:0x%04x)\n", + VDM_HDR_SVID(pd->vdm_tx->data[0])); + + kfree(pd->vdm_tx); + pd->vdm_tx = NULL; + } + + if (svid == USBPD_SID && cmd == USBPD_SVDM_DISCOVER_IDENTITY) { u32 tx_vdos[3] = { ID_HDR_USB_HOST | ID_HDR_USB_DEVICE | ID_HDR_PRODUCT_PER_MASK | ID_HDR_VID, @@ -1005,9 +1083,9 @@ static void handle_vdm_rx(struct usbpd *pd) usbpd_send_svdm(pd, USBPD_SID, cmd, SVDM_CMD_TYPE_RESP_ACK, 0, tx_vdos, 3); - } else { - usbpd_send_svdm(pd, USBPD_SID, cmd, - SVDM_CMD_TYPE_RESP_NAK, 0, NULL, 0); + } else if (cmd != USBPD_SVDM_ATTENTION) { + usbpd_send_svdm(pd, svid, cmd, SVDM_CMD_TYPE_RESP_NAK, + SVDM_HDR_OBJ_POS(vdm_hdr), NULL, 0); } break; @@ -1039,19 +1117,37 @@ static void handle_vdm_rx(struct usbpd *pd) kfree(pd->vdm_tx_retry); pd->vdm_tx_retry = NULL; - kfree(pd->discovered_svids); - - /* TODO: handle > 12 SVIDs */ - pd->discovered_svids = kzalloc((2 * num_vdos + 1) * - sizeof(u16), - GFP_KERNEL); if (!pd->discovered_svids) { - usbpd_err(&pd->dev, "unable to allocate SVIDs\n"); - break; + pd->num_svids = 2 * num_vdos; + pd->discovered_svids = kcalloc(pd->num_svids, + sizeof(u16), + GFP_KERNEL); + if (!pd->discovered_svids) { + usbpd_err(&pd->dev, "unable to allocate SVIDs\n"); + break; + } + + psvid = pd->discovered_svids; + } else { /* handle > 12 SVIDs */ + void *ptr; + size_t oldsize = pd->num_svids * sizeof(u16); + size_t newsize = oldsize + + (2 * num_vdos * sizeof(u16)); + + ptr = krealloc(pd->discovered_svids, newsize, + GFP_KERNEL); + if (!ptr) { + usbpd_err(&pd->dev, "unable to realloc SVIDs\n"); + break; + } + + pd->discovered_svids = ptr; + psvid = pd->discovered_svids + pd->num_svids; + memset(psvid, 0, (2 * num_vdos)); + pd->num_svids += 2 * num_vdos; } /* convert 32-bit VDOs to list of 16-bit SVIDs */ - psvid = pd->discovered_svids; for (i = 0; i < num_vdos * 2; i++) { /* * Within each 32-bit VDO, @@ -1075,8 +1171,22 @@ static void handle_vdm_rx(struct usbpd *pd) usbpd_dbg(&pd->dev, "Discovered SVID: 0x%04x\n", svid); *psvid++ = svid; + } + } + + /* if more than 12 SVIDs, resend the request */ + if (num_vdos == 6 && vdos[5] != 0) { + usbpd_send_svdm(pd, USBPD_SID, + USBPD_SVDM_DISCOVER_SVIDS, + SVDM_CMD_TYPE_INITIATOR, 0, + NULL, 0); + break; + } - /* if SVID supported notify handler */ + /* now that all SVIDs are discovered, notify handlers */ + for (i = 0; i < pd->num_svids; i++) { + svid = pd->discovered_svids[i]; + if (svid) { handler = find_svid_handler(pd, svid); if (handler && handler->connect) handler->connect(handler); @@ -1126,10 +1236,9 @@ static void handle_vdm_rx(struct usbpd *pd) } /* wait tVDMBusy, then retry */ - list_move(&pd->vdm_tx_retry->entry, &pd->vdm_tx_queue); + pd->vdm_tx = pd->vdm_tx_retry; pd->vdm_tx_retry = NULL; - hrtimer_start(&pd->timer, ms_to_ktime(VDM_BUSY_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, VDM_BUSY_TIME); break; default: break; @@ -1143,16 +1252,14 @@ static void handle_vdm_tx(struct usbpd *pd) int ret; /* only send one VDM at a time */ - if (!list_empty(&pd->vdm_tx_queue)) { - struct vdm_tx *vdm_tx = list_first_entry(&pd->vdm_tx_queue, - struct vdm_tx, entry); - u32 vdm_hdr = vdm_tx->data[0]; + if (pd->vdm_tx) { + u32 vdm_hdr = pd->vdm_tx->data[0]; - ret = pd_send_msg(pd, MSG_VDM, vdm_tx->data, vdm_tx->size, - SOP_MSG); + ret = pd_send_msg(pd, MSG_VDM, pd->vdm_tx->data, + pd->vdm_tx->size, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending VDM command %d\n", - VDM_HDR_CMD(vdm_tx->data[0])); + SVDM_HDR_CMD(pd->vdm_tx->data[0])); usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_SEND_SOFT_RESET : PE_SNK_SEND_SOFT_RESET); @@ -1161,24 +1268,25 @@ static void handle_vdm_tx(struct usbpd *pd) return; } - list_del(&vdm_tx->entry); - /* * special case: keep initiated Discover ID/SVIDs * around in case we need to re-try when receiving BUSY */ - if (VDM_HDR_TYPE(vdm_hdr) && - VDM_HDR_CMD_TYPE(vdm_hdr) == SVDM_CMD_TYPE_INITIATOR && - VDM_HDR_CMD(vdm_hdr) <= USBPD_SVDM_DISCOVER_SVIDS) { + if (VDM_IS_SVDM(vdm_hdr) && + SVDM_HDR_CMD_TYPE(vdm_hdr) == SVDM_CMD_TYPE_INITIATOR && + SVDM_HDR_CMD(vdm_hdr) <= USBPD_SVDM_DISCOVER_SVIDS) { if (pd->vdm_tx_retry) { usbpd_err(&pd->dev, "Previous Discover VDM command %d not ACKed/NAKed\n", - VDM_HDR_CMD(pd->vdm_tx_retry->data[0])); + SVDM_HDR_CMD( + pd->vdm_tx_retry->data[0])); kfree(pd->vdm_tx_retry); } - pd->vdm_tx_retry = vdm_tx; + pd->vdm_tx_retry = pd->vdm_tx; } else { - kfree(vdm_tx); + kfree(pd->vdm_tx); } + + pd->vdm_tx = NULL; } } @@ -1194,13 +1302,9 @@ static void reset_vdm_state(struct usbpd *pd) pd->vdm_tx_retry = NULL; kfree(pd->discovered_svids); pd->discovered_svids = NULL; - while (!list_empty(&pd->vdm_tx_queue)) { - struct vdm_tx *vdm_tx = - list_first_entry(&pd->vdm_tx_queue, - struct vdm_tx, entry); - list_del(&vdm_tx->entry); - kfree(vdm_tx); - } + pd->num_svids = 0; + kfree(pd->vdm_tx); + pd->vdm_tx = NULL; } static void dr_swap(struct usbpd *pd) @@ -1230,6 +1334,34 @@ static void dr_swap(struct usbpd *pd) pd_phy_update_roles(pd->current_dr, pd->current_pr); } + +static void vconn_swap(struct usbpd *pd) +{ + int ret; + + if (pd->vconn_enabled) { + pd->current_state = PE_VCS_WAIT_FOR_VCONN; + kick_sm(pd, VCONN_ON_TIME); + } else { + ret = regulator_enable(pd->vconn); + if (ret) { + usbpd_err(&pd->dev, "Unable to enable vconn\n"); + return; + } + + pd->vconn_enabled = true; + + ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG); + if (ret) { + usbpd_err(&pd->dev, "Error sending PS_RDY\n"); + usbpd_set_state(pd, pd->current_pr == PR_SRC ? + PE_SRC_SEND_SOFT_RESET : + PE_SNK_SEND_SOFT_RESET); + return; + } + } +} + /* Handles current state and determines transitions */ static void usbpd_sm(struct work_struct *w) { @@ -1243,6 +1375,7 @@ static void usbpd_sm(struct work_struct *w) usbpd_state_strings[pd->current_state]); hrtimer_cancel(&pd->timer); + pd->sm_queued = false; if (pd->rx_msg_len) data_recvd = pd->rx_msg_type; @@ -1250,11 +1383,11 @@ static void usbpd_sm(struct work_struct *w) ctrl_recvd = pd->rx_msg_type; /* Disconnect? */ - if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE) { + if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE && !pd->in_pr_swap) { if (pd->current_state == PE_UNKNOWN) - return; + goto sm_done; - usbpd_info(&pd->dev, "USB PD disconnect\n"); + usbpd_info(&pd->dev, "USB Type-C disconnect\n"); if (pd->pd_phy_opened) { pd_phy_close(); @@ -1274,14 +1407,21 @@ static void usbpd_sm(struct work_struct *w) val.intval = 0; power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); + + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, + &val); + + power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); - if (pd->current_pr == PR_SRC) { + if (pd->current_pr == PR_SRC) regulator_disable(pd->vbus); - if (pd->vconn_enabled) { - regulator_disable(pd->vconn); - pd->vconn_enabled = false; - } + + if (pd->vconn_enabled) { + regulator_disable(pd->vconn); + pd->vconn_enabled = false; } if (pd->current_dr == DR_UFP) @@ -1306,18 +1446,23 @@ static void usbpd_sm(struct work_struct *w) pd->current_state = PE_UNKNOWN; - return; + goto sm_done; } /* Hard reset? */ if (pd->hard_reset) { + val.intval = 1; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); + reset_vdm_state(pd); if (pd->current_pr == PR_SINK) usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT); else usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT); - pd->hard_reset = false; + + goto sm_done; } /* Soft reset? */ @@ -1353,6 +1498,10 @@ static void usbpd_sm(struct work_struct *w) } break; + case PE_SRC_STARTUP: + usbpd_set_state(pd, PE_SRC_STARTUP); + break; + case PE_SRC_SEND_CAPABILITIES: ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, default_src_caps, ARRAY_SIZE(default_src_caps), SOP_MSG); @@ -1378,30 +1527,31 @@ static void usbpd_sm(struct work_struct *w) break; } - hrtimer_start(&pd->timer, ms_to_ktime(SRC_CAP_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, SRC_CAP_TIME); break; } + val.intval = 1; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_ACTIVE, &val); + /* transmit was successful if GoodCRC was received */ pd->caps_count = 0; pd->hard_reset_count = 0; pd->pd_connected = true; /* we know peer is PD capable */ - val.intval = POWER_SUPPLY_TYPE_USB_PD; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_TYPE, &val); - /* wait for REQUEST */ pd->current_state = PE_SRC_SEND_CAPABILITIES_WAIT; - hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, SENDER_RESPONSE_TIME); break; case PE_SRC_SEND_CAPABILITIES_WAIT: if (data_recvd == MSG_REQUEST) { pd->rdo = pd->rx_payload[0]; usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY); + } else if (data_recvd || ctrl_recvd) { + usbpd_err(&pd->dev, "Unexpected message received\n"); + usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); } else { usbpd_set_state(pd, PE_SRC_HARD_RESET); } @@ -1417,6 +1567,14 @@ static void usbpd_sm(struct work_struct *w) usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); break; } + } else if (ctrl_recvd == MSG_GET_SINK_CAP) { + ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES, + default_snk_caps, + ARRAY_SIZE(default_snk_caps), SOP_MSG); + if (ret) { + usbpd_err(&pd->dev, "Error sending Sink Caps\n"); + usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); + } } else if (data_recvd == MSG_REQUEST) { pd->rdo = pd->rx_payload[0]; usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY); @@ -1436,6 +1594,9 @@ static void usbpd_sm(struct work_struct *w) dr_swap(pd); kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); } else if (ctrl_recvd == MSG_PR_SWAP) { + /* lock in current mode */ + set_power_role(pd, pd->current_pr); + /* we'll happily accept Src->Sink requests anytime */ ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { @@ -1445,8 +1606,17 @@ static void usbpd_sm(struct work_struct *w) } pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF; - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, SRC_TRANSITION_TIME); break; + } else if (ctrl_recvd == MSG_VCONN_SWAP) { + ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); + if (ret) { + usbpd_err(&pd->dev, "Error sending Accept\n"); + usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); + break; + } + + vconn_swap(pd); } else { if (data_recvd == MSG_VDM) handle_vdm_rx(pd); @@ -1456,6 +1626,10 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SRC_HARD_RESET: + val.intval = 1; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); + pd_send_hard_reset(pd); pd->in_explicit_contract = false; reset_vdm_state(pd); @@ -1465,39 +1639,41 @@ static void usbpd_sm(struct work_struct *w) usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT); break; - case PE_SNK_DISCOVERY: - if (!pd->vbus_present) { - /* Hard reset and VBUS didn't come back? */ - power_supply_get_property(pd->usb_psy, - POWER_SUPPLY_PROP_TYPE, &val); - if (val.intval == POWER_SUPPLY_TYPEC_NONE) { - pd->typec_mode = POWER_SUPPLY_TYPEC_NONE; - queue_work(pd->wq, &pd->sm_work); - } - break; - } - - usbpd_set_state(pd, PE_SNK_WAIT_FOR_CAPABILITIES); + case PE_SNK_STARTUP: + usbpd_set_state(pd, PE_SNK_STARTUP); break; case PE_SNK_WAIT_FOR_CAPABILITIES: if (data_recvd == MSG_SOURCE_CAPABILITIES) { - val.intval = 1; + val.intval = 0; power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PD_ACTIVE, &val); + POWER_SUPPLY_PROP_PD_IN_HARD_RESET, + &val); - val.intval = POWER_SUPPLY_TYPE_USB_PD; + val.intval = 1; power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_TYPE, &val); + POWER_SUPPLY_PROP_PD_ACTIVE, &val); usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY); } else if (pd->hard_reset_count < 3) { usbpd_set_state(pd, PE_SNK_HARD_RESET); } else if (pd->pd_connected) { usbpd_info(&pd->dev, "Sink hard reset count exceeded, forcing reconnect\n"); + + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_IN_HARD_RESET, + &val); + usbpd_set_state(pd, PE_ERROR_RECOVERY); } else { usbpd_dbg(&pd->dev, "Sink hard reset count exceeded, disabling PD\n"); + + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_IN_HARD_RESET, + &val); + val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); @@ -1514,9 +1690,14 @@ static void usbpd_sm(struct work_struct *w) POWER_SUPPLY_PROP_VOLTAGE_MIN, &val); - val.intval = 0; /* suspend charging */ + /* + * disable charging; technically we are allowed to + * charge up to pSnkStdby (2.5 W) during this + * transition, but disable it just for simplicity. + */ + val.intval = 0; power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_CURRENT_MAX, &val); + POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); pd->selected_pdo = pd->requested_pdo; usbpd_set_state(pd, PE_SNK_TRANSITION_SINK); @@ -1526,6 +1707,9 @@ static void usbpd_sm(struct work_struct *w) else usbpd_set_state(pd, PE_SNK_WAIT_FOR_CAPABILITIES); + } else if (pd->rx_msg_type) { + usbpd_err(&pd->dev, "Invalid response to sink request\n"); + usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); } else { /* timed out; go to hard reset */ usbpd_set_state(pd, PE_SNK_HARD_RESET); @@ -1544,7 +1728,7 @@ static void usbpd_sm(struct work_struct *w) /* resume charging */ val.intval = pd->requested_current * 1000; /* mA->uA */ power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_CURRENT_MAX, &val); + POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); usbpd_set_state(pd, PE_SNK_READY); } else { @@ -1554,9 +1738,9 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SNK_READY: - if (data_recvd == MSG_SOURCE_CAPABILITIES) + if (data_recvd == MSG_SOURCE_CAPABILITIES) { usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY); - else if (ctrl_recvd == MSG_GET_SINK_CAP) { + } else if (ctrl_recvd == MSG_GET_SINK_CAP) { ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES, default_snk_caps, ARRAY_SIZE(default_snk_caps), SOP_MSG); @@ -1564,6 +1748,15 @@ static void usbpd_sm(struct work_struct *w) usbpd_err(&pd->dev, "Error sending Sink Caps\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); } + } else if (ctrl_recvd == MSG_GET_SOURCE_CAP) { + ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, + default_src_caps, + ARRAY_SIZE(default_src_caps), SOP_MSG); + if (ret) { + usbpd_err(&pd->dev, "Error sending SRC CAPs\n"); + usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); + break; + } } else if (ctrl_recvd == MSG_DR_SWAP) { if (pd->vdm_state == MODE_ENTERED) { usbpd_set_state(pd, PE_SNK_HARD_RESET); @@ -1580,6 +1773,9 @@ static void usbpd_sm(struct work_struct *w) dr_swap(pd); kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); } else if (ctrl_recvd == MSG_PR_SWAP) { + /* lock in current mode */ + set_power_role(pd, pd->current_pr); + /* TODO: should we Reject in certain circumstances? */ ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { @@ -1589,20 +1785,34 @@ static void usbpd_sm(struct work_struct *w) } pd->in_pr_swap = true; - pd->current_state = PE_PRS_SNK_SRC_TRANSITION_TO_OFF; - /* turn off sink */ - pd->in_explicit_contract = false; - + usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); + break; + } else if (ctrl_recvd == MSG_VCONN_SWAP) { /* - * need to update PR bit in message header so that - * proper GoodCRC is sent when receiving next PS_RDY + * if VCONN is connected to VBUS, make sure we are + * not in high voltage contract, otherwise reject. */ - pd->current_pr = PR_SRC; - pd_phy_update_roles(pd->current_dr, pd->current_pr); + if (!pd->vconn_is_external && + (pd->requested_voltage > 5000000)) { + ret = pd_send_msg(pd, MSG_REJECT, NULL, 0, + SOP_MSG); + if (ret) { + usbpd_err(&pd->dev, "Error sending Reject\n"); + usbpd_set_state(pd, + PE_SNK_SEND_SOFT_RESET); + } - hrtimer_start(&pd->timer, ms_to_ktime(PS_SOURCE_OFF), - HRTIMER_MODE_REL); - break; + break; + } + + ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); + if (ret) { + usbpd_err(&pd->dev, "Error sending Accept\n"); + usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); + break; + } + + vconn_swap(pd); } else { if (data_recvd == MSG_VDM) handle_vdm_rx(pd); @@ -1611,6 +1821,20 @@ static void usbpd_sm(struct work_struct *w) } break; + case PE_SNK_TRANSITION_TO_DEFAULT: + if (pd->vbus_present) { + usbpd_set_state(pd, PE_SNK_STARTUP); + } else { + /* Hard reset and VBUS didn't come back? */ + power_supply_get_property(pd->usb_psy, + POWER_SUPPLY_PROP_TYPEC_MODE, &val); + if (val.intval == POWER_SUPPLY_TYPEC_NONE) { + pd->typec_mode = POWER_SUPPLY_TYPEC_NONE; + kick_sm(pd, 0); + } + } + break; + case PE_SRC_SOFT_RESET: case PE_SNK_SOFT_RESET: /* Reset protocol layer */ @@ -1649,14 +1873,14 @@ static void usbpd_sm(struct work_struct *w) /* prepare charger for VBUS change */ val.intval = 1; power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PD_ACTIVE, &val); + POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); pd->requested_voltage = 5000000; if (pd->requested_current) { val.intval = pd->requested_current = 0; power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_CURRENT_MAX, &val); + POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); } val.intval = pd->requested_voltage; @@ -1684,16 +1908,23 @@ static void usbpd_sm(struct work_struct *w) } pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF; - /* fall-through */ + kick_sm(pd, SRC_TRANSITION_TIME); + break; + case PE_PRS_SRC_SNK_TRANSITION_TO_OFF: pd->in_pr_swap = true; pd->in_explicit_contract = false; regulator_disable(pd->vbus); - set_power_role(pd, PR_SINK); /* switch Rp->Rd */ + + /* PE_PRS_SRC_SNK_Assert_Rd */ pd->current_pr = PR_SINK; + set_power_role(pd, pd->current_pr); pd_phy_update_roles(pd->current_dr, pd->current_pr); + /* allow time for Vbus discharge, must be < tSrcSwapStdby */ + msleep(500); + ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending PS_RDY\n"); @@ -1702,8 +1933,7 @@ static void usbpd_sm(struct work_struct *w) } pd->current_state = PE_PRS_SRC_SNK_WAIT_SOURCE_ON; - hrtimer_start(&pd->timer, ms_to_ktime(PS_SOURCE_ON), - HRTIMER_MODE_REL); + kick_sm(pd, PS_SOURCE_ON); break; case PE_PRS_SRC_SNK_WAIT_SOURCE_ON: @@ -1720,19 +1950,7 @@ static void usbpd_sm(struct work_struct *w) } pd->in_pr_swap = true; - pd->current_state = PE_PRS_SNK_SRC_TRANSITION_TO_OFF; - /* turn off sink */ - pd->in_explicit_contract = false; - - /* - * need to update PR bit in message header so that - * proper GoodCRC is sent when receiving next PS_RDY - */ - pd->current_pr = PR_SRC; - pd_phy_update_roles(pd->current_dr, pd->current_pr); - - hrtimer_start(&pd->timer, ms_to_ktime(PS_SOURCE_OFF), - HRTIMER_MODE_REL); + usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); break; case PE_PRS_SNK_SRC_TRANSITION_TO_OFF: @@ -1741,14 +1959,20 @@ static void usbpd_sm(struct work_struct *w) break; } + /* PE_PRS_SNK_SRC_Assert_Rp */ + pd->current_pr = PR_SRC; + set_power_role(pd, pd->current_pr); pd->current_state = PE_PRS_SNK_SRC_SOURCE_ON; + /* fall-through */ + case PE_PRS_SNK_SRC_SOURCE_ON: - set_power_role(pd, PR_SRC); ret = regulator_enable(pd->vbus); if (ret) usbpd_err(&pd->dev, "Unable to enable vbus\n"); + msleep(200); /* allow time VBUS ramp-up, must be < tNewSrc */ + ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending PS_RDY\n"); @@ -1759,6 +1983,26 @@ static void usbpd_sm(struct work_struct *w) usbpd_set_state(pd, PE_SRC_STARTUP); break; + case PE_VCS_WAIT_FOR_VCONN: + if (ctrl_recvd == MSG_PS_RDY) { + /* + * hopefully redundant check but in case not enabled + * avoids unbalanced regulator disable count + */ + if (pd->vconn_enabled) + regulator_disable(pd->vconn); + pd->vconn_enabled = false; + + pd->current_state = pd->current_pr == PR_SRC ? + PE_SRC_READY : PE_SNK_READY; + } else { + /* timed out; go to hard reset */ + usbpd_set_state(pd, pd->current_pr == PR_SRC ? + PE_SRC_HARD_RESET : PE_SNK_HARD_RESET); + } + + break; + default: usbpd_err(&pd->dev, "Unhandled state %s\n", usbpd_state_strings[pd->current_state]); @@ -1767,6 +2011,10 @@ static void usbpd_sm(struct work_struct *w) /* Rx message should have been consumed now */ pd->rx_msg_type = pd->rx_msg_len = 0; + +sm_done: + if (!pd->sm_queued) + pm_relax(&pd->dev); } static inline const char *src_current(enum power_supply_typec_mode typec_mode) @@ -1787,9 +2035,8 @@ 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; - bool pd_allowed; enum power_supply_typec_mode typec_mode; - enum power_supply_type psy_type; + bool do_work = false; int ret; if (ptr != pd->usb_psy || evt != PSY_EVENT_PROP_CHANGED) @@ -1803,7 +2050,9 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) return ret; } - pd_allowed = val.intval; + if (pd->pd_allowed != val.intval) + do_work = true; + pd->pd_allowed = val.intval; ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_PRESENT, &val); @@ -1823,37 +2072,6 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) typec_mode = val.intval; - /* - * Don't proceed if cable is connected but PD_ALLOWED is false. - * It means the PMIC may still be in the middle of performing - * charger type detection. - */ - if (!pd_allowed && typec_mode != POWER_SUPPLY_TYPEC_NONE) - return 0; - - /* - * Workaround for PMIC HW bug. - * - * During hard reset or PR swap (sink to source) 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->hard_reset || pd->in_pr_swap) && - typec_mode == POWER_SUPPLY_TYPEC_NONE && - pd->psy_type != POWER_SUPPLY_TYPE_USB) { - usbpd_dbg(&pd->dev, "Ignoring disconnect due to %s\n", - pd->hard_reset ? "hard reset" : "PR swap"); - return 0; - } - ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPE, &val); if (ret) { @@ -1861,59 +2079,91 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) return ret; } - psy_type = val.intval; + if (pd->psy_type != val.intval) + do_work = true; + pd->psy_type = val.intval; - usbpd_dbg(&pd->dev, "typec mode:%d present:%d type:%d\n", typec_mode, - pd->vbus_present, psy_type); + 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)); - /* any change? */ - if (pd->typec_mode == typec_mode && pd->psy_type == psy_type) - return 0; + if (pd->typec_mode != typec_mode) { + pd->typec_mode = typec_mode; + do_work = true; - pd->typec_mode = typec_mode; - pd->psy_type = psy_type; + 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: - queue_work(pd->wq, &pd->sm_work); - break; + /* + * 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; + } - /* 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)); - if (pd->current_pr != PR_SINK) { + 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; - queue_work(pd->wq, &pd->sm_work); - } - break; + 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)"); - if (pd->current_pr != PR_SRC) { + /* 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; - queue_work(pd->wq, &pd->sm_work); - } - break; + 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); + return 0; } @@ -2337,6 +2587,10 @@ struct usbpd *usbpd_create(struct device *parent) if (ret) goto free_pd; + ret = device_init_wakeup(&pd->dev, true); + if (ret) + goto free_pd; + ret = device_add(&pd->dev); if (ret) goto free_pd; @@ -2392,11 +2646,13 @@ struct usbpd *usbpd_create(struct device *parent) goto unreg_psy; } + pd->vconn_is_external = device_property_present(parent, + "qcom,vconn-uses-external-source"); + pd->current_pr = PR_NONE; pd->current_dr = DR_NONE; list_add_tail(&pd->instance, &_usbpd); - INIT_LIST_HEAD(&pd->vdm_tx_queue); INIT_LIST_HEAD(&pd->svid_handlers); /* force read initial power_supply values */ diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c index 6bf80e43cac5..021755f7f32d 100644 --- a/drivers/video/fbdev/core/fbcmap.c +++ b/drivers/video/fbdev/core/fbcmap.c @@ -187,8 +187,8 @@ int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to) int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to) { - int tooff = 0, fromoff = 0; - int size; + u32 tooff = 0, fromoff = 0; + u32 size; if (to->start > from->start) fromoff = to->start - from->start; @@ -198,10 +198,10 @@ int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to) return -EINVAL; size = to->len - tooff; - if (size > (int) (from->len - fromoff)) + if (size > (from->len - fromoff)) size = from->len - fromoff; size *= sizeof(u16); - if (!size) + if (size == 0) return -EINVAL; if (copy_to_user(to->red+tooff, from->red+fromoff, size)) diff --git a/include/dt-bindings/clock/qcom,gcc-msmfalcon.h b/include/dt-bindings/clock/qcom,gcc-msmfalcon.h index 609a20422ed1..aa76fbad5083 100644 --- a/include/dt-bindings/clock/qcom,gcc-msmfalcon.h +++ b/include/dt-bindings/clock/qcom,gcc-msmfalcon.h @@ -195,6 +195,8 @@ #define GCC_UFS_ICE_CORE_HW_CTL_CLK 180 #define GCC_UFS_PHY_AUX_HW_CTL_CLK 181 #define GCC_UFS_UNIPRO_CORE_HW_CTL_CLK 182 +#define HLOS1_VOTE_TURING_ADSP_SMMU_CLK 183 +#define HLOS2_VOTE_TURING_ADSP_SMMU_CLK 184 /* Block resets */ #define GCC_QUSB2PHY_PRIM_BCR 0 diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 23026ba6ff25..0de2d0c780d7 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -669,6 +669,9 @@ void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent); void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate, unsigned long max_rate); +unsigned long clk_aggregate_rate(struct clk_hw *hw, + const struct clk_core *parent); + static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw *src) { dst->clk = src->clk; diff --git a/include/linux/leds-qpnp-flash.h b/include/linux/leds-qpnp-flash.h index 3df370a9e6d3..4b5a339970fa 100644 --- a/include/linux/leds-qpnp-flash.h +++ b/include/linux/leds-qpnp-flash.h @@ -18,6 +18,9 @@ #define ENABLE_REGULATOR BIT(0) #define DISABLE_REGULATOR BIT(1) #define QUERY_MAX_CURRENT BIT(2) +#define PRE_FLASH BIT(3) + +#define FLASH_LED_PREPARE_OPTIONS_MASK GENMASK(3, 0) int qpnp_flash_led_prepare(struct led_trigger *trig, int options, int *max_current); diff --git a/include/linux/mfd/wcd934x/registers.h b/include/linux/mfd/wcd934x/registers.h index 085e16d66bc4..a824875096e4 100644 --- a/include/linux/mfd/wcd934x/registers.h +++ b/include/linux/mfd/wcd934x/registers.h @@ -800,9 +800,11 @@ enum { #define WCD934X_VBADC_NEW_ADC_DOUTLSB 0x0731 #define WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL 0x0732 #define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL 0x0733 +#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L 0x0733 #define WCD934X_HPH_NEW_INT_RDAC_VREF_CTL 0x0734 #define WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL 0x0735 #define WCD934X_HPH_NEW_INT_RDAC_MISC1 0x0736 +#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R 0x0736 #define WCD934X_HPH_NEW_INT_PA_MISC1 0x0737 #define WCD934X_HPH_NEW_INT_PA_MISC2 0x0738 #define WCD934X_HPH_NEW_INT_PA_RDAC_MISC 0x0739 diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index c477f60c3f01..218cd875ee5a 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -216,6 +216,9 @@ enum power_supply_property { POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, POWER_SUPPLY_PROP_PD_ALLOWED, POWER_SUPPLY_PROP_PD_ACTIVE, + POWER_SUPPLY_PROP_PD_IN_HARD_RESET, + POWER_SUPPLY_PROP_PD_CURRENT_MAX, + POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, POWER_SUPPLY_PROP_CHARGER_TEMP, POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, POWER_SUPPLY_PROP_PARALLEL_DISABLE, diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h index b5425dd7eaea..4117b0d47b0d 100644 --- a/include/soc/qcom/smem.h +++ b/include/soc/qcom/smem.h @@ -22,11 +22,11 @@ enum { SMEM_DSPS, SMEM_WCNSS, SMEM_MODEM_Q6_FW, + SMEM_CDSP = SMEM_MODEM_Q6_FW, SMEM_RPM, SMEM_TZ, SMEM_SPSS, SMEM_HYP, - SMEM_CDSP, NUM_SMEM_SUBSYSTEMS, }; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index ff7f6f35fc8f..024fb1007c78 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -8239,7 +8239,7 @@ void set_curr_task(int cpu, struct task_struct *p) /* task_group_lock serializes the addition/removal of task groups */ static DEFINE_SPINLOCK(task_group_lock); -static void free_sched_group(struct task_group *tg) +static void sched_free_group(struct task_group *tg) { free_fair_sched_group(tg); free_rt_sched_group(tg); @@ -8265,7 +8265,7 @@ struct task_group *sched_create_group(struct task_group *parent) return tg; err: - free_sched_group(tg); + sched_free_group(tg); return ERR_PTR(-ENOMEM); } @@ -8285,27 +8285,24 @@ void sched_online_group(struct task_group *tg, struct task_group *parent) } /* rcu callback to free various structures associated with a task group */ -static void free_sched_group_rcu(struct rcu_head *rhp) +static void sched_free_group_rcu(struct rcu_head *rhp) { /* now it should be safe to free those cfs_rqs */ - free_sched_group(container_of(rhp, struct task_group, rcu)); + sched_free_group(container_of(rhp, struct task_group, rcu)); } -/* Destroy runqueue etc associated with a task group */ void sched_destroy_group(struct task_group *tg) { /* wait for possible concurrent references to cfs_rqs complete */ - call_rcu(&tg->rcu, free_sched_group_rcu); + call_rcu(&tg->rcu, sched_free_group_rcu); } void sched_offline_group(struct task_group *tg) { unsigned long flags; - int i; /* end participation in shares distribution */ - for_each_possible_cpu(i) - unregister_fair_sched_group(tg, i); + unregister_fair_sched_group(tg); spin_lock_irqsave(&task_group_lock, flags); list_del_rcu(&tg->list); @@ -8756,31 +8753,26 @@ cpu_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) if (IS_ERR(tg)) return ERR_PTR(-ENOMEM); + sched_online_group(tg, parent); + return &tg->css; } -static int cpu_cgroup_css_online(struct cgroup_subsys_state *css) +static void cpu_cgroup_css_released(struct cgroup_subsys_state *css) { struct task_group *tg = css_tg(css); - struct task_group *parent = css_tg(css->parent); - if (parent) - sched_online_group(tg, parent); - return 0; + sched_offline_group(tg); } static void cpu_cgroup_css_free(struct cgroup_subsys_state *css) { struct task_group *tg = css_tg(css); - sched_destroy_group(tg); -} - -static void cpu_cgroup_css_offline(struct cgroup_subsys_state *css) -{ - struct task_group *tg = css_tg(css); - - sched_offline_group(tg); + /* + * Relies on the RCU grace period between css_released() and this. + */ + sched_free_group(tg); } static void cpu_cgroup_fork(struct task_struct *task, void *private) @@ -9147,9 +9139,8 @@ static struct cftype cpu_files[] = { struct cgroup_subsys cpu_cgrp_subsys = { .css_alloc = cpu_cgroup_css_alloc, + .css_released = cpu_cgroup_css_released, .css_free = cpu_cgroup_css_free, - .css_online = cpu_cgroup_css_online, - .css_offline = cpu_cgroup_css_offline, .fork = cpu_cgroup_fork, .can_attach = cpu_cgroup_can_attach, .attach = cpu_cgroup_attach, diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index df23b0365527..6362b864e2b1 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -2619,7 +2619,7 @@ struct cluster_cpu_stats { int best_idle_cpu, least_loaded_cpu; int best_capacity_cpu, best_cpu, best_sibling_cpu; int min_cost, best_sibling_cpu_cost; - int best_cpu_cstate; + int best_cpu_wakeup_latency; u64 min_load, best_load, best_sibling_cpu_load; s64 highest_spare_capacity; }; @@ -2827,19 +2827,19 @@ next_best_cluster(struct sched_cluster *cluster, struct cpu_select_env *env, static void __update_cluster_stats(int cpu, struct cluster_cpu_stats *stats, struct cpu_select_env *env, int cpu_cost) { - int cpu_cstate; + int wakeup_latency; int prev_cpu = env->prev_cpu; - cpu_cstate = cpu_rq(cpu)->cstate; + wakeup_latency = cpu_rq(cpu)->wakeup_latency; if (env->need_idle) { stats->min_cost = cpu_cost; if (idle_cpu(cpu)) { - if (cpu_cstate < stats->best_cpu_cstate || - (cpu_cstate == stats->best_cpu_cstate && - cpu == prev_cpu)) { + if (wakeup_latency < stats->best_cpu_wakeup_latency || + (wakeup_latency == stats->best_cpu_wakeup_latency && + cpu == prev_cpu)) { stats->best_idle_cpu = cpu; - stats->best_cpu_cstate = cpu_cstate; + stats->best_cpu_wakeup_latency = wakeup_latency; } } else { if (env->cpu_load < stats->min_load || @@ -2855,7 +2855,7 @@ static void __update_cluster_stats(int cpu, struct cluster_cpu_stats *stats, if (cpu_cost < stats->min_cost) { stats->min_cost = cpu_cost; - stats->best_cpu_cstate = cpu_cstate; + stats->best_cpu_wakeup_latency = wakeup_latency; stats->best_load = env->cpu_load; stats->best_cpu = cpu; env->sbc_best_flag = SBC_FLAG_CPU_COST; @@ -2864,11 +2864,11 @@ static void __update_cluster_stats(int cpu, struct cluster_cpu_stats *stats, /* CPU cost is the same. Start breaking the tie by C-state */ - if (cpu_cstate > stats->best_cpu_cstate) + if (wakeup_latency > stats->best_cpu_wakeup_latency) return; - if (cpu_cstate < stats->best_cpu_cstate) { - stats->best_cpu_cstate = cpu_cstate; + if (wakeup_latency < stats->best_cpu_wakeup_latency) { + stats->best_cpu_wakeup_latency = wakeup_latency; stats->best_load = env->cpu_load; stats->best_cpu = cpu; env->sbc_best_flag = SBC_FLAG_COST_CSTATE_TIE_BREAKER; @@ -2883,8 +2883,8 @@ static void __update_cluster_stats(int cpu, struct cluster_cpu_stats *stats, } if (stats->best_cpu != prev_cpu && - ((cpu_cstate == 0 && env->cpu_load < stats->best_load) || - (cpu_cstate > 0 && env->cpu_load > stats->best_load))) { + ((wakeup_latency == 0 && env->cpu_load < stats->best_load) || + (wakeup_latency > 0 && env->cpu_load > stats->best_load))) { stats->best_load = env->cpu_load; stats->best_cpu = cpu; env->sbc_best_flag = SBC_FLAG_CSTATE_LOAD; @@ -2979,7 +2979,7 @@ static inline void init_cluster_cpu_stats(struct cluster_cpu_stats *stats) stats->min_load = stats->best_sibling_cpu_load = ULLONG_MAX; stats->highest_spare_capacity = 0; stats->least_loaded_cpu = -1; - stats->best_cpu_cstate = INT_MAX; + stats->best_cpu_wakeup_latency = INT_MAX; /* No need to initialize stats->best_load */ } @@ -9653,11 +9653,8 @@ void free_fair_sched_group(struct task_group *tg) for_each_possible_cpu(i) { if (tg->cfs_rq) kfree(tg->cfs_rq[i]); - if (tg->se) { - if (tg->se[i]) - remove_entity_load_avg(tg->se[i]); + if (tg->se) kfree(tg->se[i]); - } } kfree(tg->cfs_rq); @@ -9705,21 +9702,29 @@ err: return 0; } -void unregister_fair_sched_group(struct task_group *tg, int cpu) +void unregister_fair_sched_group(struct task_group *tg) { - struct rq *rq = cpu_rq(cpu); unsigned long flags; + struct rq *rq; + int cpu; - /* - * Only empty task groups can be destroyed; so we can speculatively - * check on_list without danger of it being re-added. - */ - if (!tg->cfs_rq[cpu]->on_list) - return; + for_each_possible_cpu(cpu) { + if (tg->se[cpu]) + remove_entity_load_avg(tg->se[cpu]); - raw_spin_lock_irqsave(&rq->lock, flags); - list_del_leaf_cfs_rq(tg->cfs_rq[cpu]); - raw_spin_unlock_irqrestore(&rq->lock, flags); + /* + * Only empty task groups can be destroyed; so we can speculatively + * check on_list without danger of it being re-added. + */ + if (!tg->cfs_rq[cpu]->on_list) + continue; + + rq = cpu_rq(cpu); + + raw_spin_lock_irqsave(&rq->lock, flags); + list_del_leaf_cfs_rq(tg->cfs_rq[cpu]); + raw_spin_unlock_irqrestore(&rq->lock, flags); + } } void init_tg_cfs_entry(struct task_group *tg, struct cfs_rq *cfs_rq, @@ -9801,7 +9806,7 @@ int alloc_fair_sched_group(struct task_group *tg, struct task_group *parent) return 1; } -void unregister_fair_sched_group(struct task_group *tg, int cpu) { } +void unregister_fair_sched_group(struct task_group *tg) { } #endif /* CONFIG_FAIR_GROUP_SCHED */ diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c index a0686ea29243..3d5de8ba70a2 100644 --- a/kernel/sched/hmp.c +++ b/kernel/sched/hmp.c @@ -24,6 +24,8 @@ #include <trace/events/sched.h> +#define CSTATE_LATENCY_GRANULARITY_SHIFT (6) + const char *task_event_names[] = {"PUT_PREV_TASK", "PICK_NEXT_TASK", "TASK_WAKE", "TASK_MIGRATE", "TASK_UPDATE", "IRQ_UPDATE"}; @@ -99,7 +101,10 @@ sched_set_cpu_cstate(int cpu, int cstate, int wakeup_energy, int wakeup_latency) rq->cstate = cstate; /* C1, C2 etc */ rq->wakeup_energy = wakeup_energy; - rq->wakeup_latency = wakeup_latency; + /* disregard small latency delta (64 us). */ + rq->wakeup_latency = ((wakeup_latency >> + CSTATE_LATENCY_GRANULARITY_SHIFT) << + CSTATE_LATENCY_GRANULARITY_SHIFT); } /* diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index ada5e580e968..27b28369440d 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -313,7 +313,7 @@ extern int tg_nop(struct task_group *tg, void *data); extern void free_fair_sched_group(struct task_group *tg); extern int alloc_fair_sched_group(struct task_group *tg, struct task_group *parent); -extern void unregister_fair_sched_group(struct task_group *tg, int cpu); +extern void unregister_fair_sched_group(struct task_group *tg); extern void init_tg_cfs_entry(struct task_group *tg, struct cfs_rq *cfs_rq, struct sched_entity *se, int cpu, struct sched_entity *parent); diff --git a/kernel/sched/tune.c b/kernel/sched/tune.c index 3c964d6d3856..4f8182302e5e 100644 --- a/kernel/sched/tune.c +++ b/kernel/sched/tune.c @@ -29,7 +29,7 @@ struct schedtune { static inline struct schedtune *css_st(struct cgroup_subsys_state *css) { - return css ? container_of(css, struct schedtune, css) : NULL; + return container_of(css, struct schedtune, css); } static inline struct schedtune *task_schedtune(struct task_struct *tsk) diff --git a/net/core/dev.c b/net/core/dev.c index a299c3956daa..a4c647893e52 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3544,9 +3544,6 @@ static int netif_rx_internal(struct sk_buff *skb) trace_netif_rx(skb); #ifdef CONFIG_RPS - WARN_ONCE(skb_cloned(skb), "Cloned packet from dev %s\n", - skb->dev->name); - if (static_key_false(&rps_needed)) { struct rps_dev_flow voidflow, *rflow = &voidflow; int cpu; diff --git a/sound/soc/codecs/msm_hdmi_codec_rx.c b/sound/soc/codecs/msm_hdmi_codec_rx.c index dee66f231ceb..7d649ba2b505 100644 --- a/sound/soc/codecs/msm_hdmi_codec_rx.c +++ b/sound/soc/codecs/msm_hdmi_codec_rx.c @@ -318,8 +318,9 @@ static void msm_ext_disp_audio_codec_rx_dai_shutdown( struct msm_ext_disp_audio_codec_rx_data *codec_data = dev_get_drvdata(dai->codec->dev); - if (!codec_data || !codec_data->ext_disp_ops.cable_status) { - dev_err(dai->dev, "%s: codec data or cable_status is null\n", + if (!codec_data || !codec_data->ext_disp_ops.teardown_done || + !codec_data->ext_disp_ops.cable_status) { + dev_err(dai->dev, "%s: codec data or teardown_done or cable_status is null\n", __func__); return; } @@ -332,6 +333,8 @@ static void msm_ext_disp_audio_codec_rx_dai_shutdown( __func__); } + codec_data->ext_disp_ops.teardown_done( + codec_data->ext_disp_core_pdev); return; } diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c index 2763073ed056..0a9c283c250c 100644 --- a/sound/soc/codecs/wcd-spi.c +++ b/sound/soc/codecs/wcd-spi.c @@ -319,7 +319,7 @@ static int wcd_spi_transfer_split(struct spi_device *spi, u32 addr = data_msg->remote_addr; u8 *data = data_msg->data; int remain_size = data_msg->len; - int to_xfer, loop_cnt, ret; + int to_xfer, loop_cnt, ret = 0; /* Perform single writes until multi word alignment is met */ loop_cnt = 1; @@ -845,7 +845,7 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data, void *data) { struct spi_device *spi = to_spi_device(dev); - int ret; + int ret = 0; dev_dbg(&spi->dev, "%s: event type %d\n", __func__, event); diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 46b8e7f72eb8..9394ee52cad0 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -4596,9 +4596,11 @@ static int tasha_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, if (!ret) { wcd_clsh_imped_config(codec, impedl, false); set_bit(CLASSH_CONFIG, &tasha->status_mask); - } else + } else { dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n", __func__, ret); + ret = 0; + } break; diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.c b/sound/soc/codecs/wcd934x/wcd934x-dsd.c index 55072466af55..4e3e769585e6 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsd.c +++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.c @@ -28,6 +28,9 @@ #define DSD_VOLUME_STEP_DELAY_US ((1000 * DSD_VOLUME_UPDATE_DELAY_MS) / \ (2 * DSD_VOLUME_STEPS)) +#define TAVIL_VERSION_1_0 0 +#define TAVIL_VERSION_1_1 1 + static const DECLARE_TLV_DB_MINMAX(tavil_dsd_db_scale, DSD_VOLUME_MIN_M110dB, DSD_VOLUME_MAX_0dB); @@ -369,6 +372,14 @@ static void tavil_dsd_data_pull(struct snd_soc_codec *codec, int dsd_num, } } +static void tavil_dsd_update_volume(struct tavil_dsd_config *dsd_conf) +{ + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_TOP_TOP_CFG0, + 0x01, 0x01); + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_TOP_TOP_CFG0, + 0x01, 0x00); +} + static int tavil_enable_dsd(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -429,6 +440,8 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w, /* Apply Gain */ snd_soc_write(codec, WCD934X_CDC_DSD0_CFG1, dsd_conf->volume[DSD0]); + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); } else if (w->shift == DSD1) { snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL, @@ -440,6 +453,8 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w, /* Apply Gain */ snd_soc_write(codec, WCD934X_CDC_DSD1_CFG1, dsd_conf->volume[DSD1]); + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); } /* 10msec sleep required after DSD clock is set */ usleep_range(10000, 10100); @@ -538,16 +553,23 @@ static int tavil_dsd_vol_put(struct snd_kcontrol *kcontrol, snd_soc_write(codec, WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx, nv1); + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); + /* sleep required after each volume step */ usleep_range(DSD_VOLUME_STEP_DELAY_US, (DSD_VOLUME_STEP_DELAY_US + DSD_VOLUME_USLEEP_MARGIN_US)); } - if (nv1 != nv[dsd_idx]) + if (nv1 != nv[dsd_idx]) { snd_soc_write(codec, WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx, nv[dsd_idx]); + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); + } + dsd_conf->volume[dsd_idx] = nv[dsd_idx]; } @@ -629,9 +651,14 @@ struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec) dsd_conf->codec = codec; + /* Read version */ + dsd_conf->version = snd_soc_read(codec, + WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0); /* DSD registers init */ - snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00); - snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00); + if (dsd_conf->version == TAVIL_VERSION_1_0) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00); + } /* DSD0: Mute EN */ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04); /* DSD1: Mute EN */ diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.h b/sound/soc/codecs/wcd934x/wcd934x-dsd.h index c033795beb9b..21450c90a272 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsd.h +++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.h @@ -40,6 +40,7 @@ struct tavil_dsd_config { u32 base_sample_rate[DSD_MAX]; int volume[DSD_MAX]; struct mutex vol_mutex; + int version; }; #ifdef CONFIG_SND_SOC_WCD934X_DSD diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c index c3758620177d..8d2247176607 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -523,7 +523,9 @@ static int wcd_cntl_enable_memory(struct wcd_dsp_cntl *cntl) ARRAY_SIZE(mem_enable_values), mem_enable_values); - snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0x05); + /* Make sure Deep sleep of memories is enabled for all banks */ + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F); done: return ret; } @@ -533,6 +535,7 @@ static void wcd_cntl_disable_memory(struct wcd_dsp_cntl *cntl) struct snd_soc_codec *codec = cntl->codec; snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F); snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, 0xFF); snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, 0xFF); snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, 0x07); @@ -833,8 +836,7 @@ static int wcd_control_init(struct device *dev, void *priv_data) struct snd_soc_codec *codec = cntl->codec; struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; - char wcd_cntl_dir_name[WCD_CNTL_DIR_NAME_LEN_MAX]; - int ret, ret1; + int ret; bool err_irq_requested = false; ret = wcd9xxx_request_irq(core_res, @@ -876,25 +878,8 @@ static int wcd_control_init(struct device *dev, void *priv_data) } wcd_cntl_cpar_ctrl(cntl, true); - snprintf(wcd_cntl_dir_name, WCD_CNTL_DIR_NAME_LEN_MAX, - "%s%d", "wdsp", cntl->dsp_instance); - wcd_cntl_debugfs_init(wcd_cntl_dir_name, cntl); - ret = wcd_cntl_sysfs_init(wcd_cntl_dir_name, cntl); - if (IS_ERR_VALUE(ret)) { - dev_err(codec->dev, - "%s: Failed to init sysfs %d\n", - __func__, ret); - goto err_sysfs_init; - } - return 0; -err_sysfs_init: - wcd_cntl_cpar_ctrl(cntl, false); - ret1 = wcd_cntl_clocks_disable(cntl); - if (IS_ERR_VALUE(ret1)) - dev_err(codec->dev, "%s: Failed to disable clocks, err = %d\n", - __func__, ret1); err_clk_enable: /* Mask all error interrupts */ snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF); @@ -916,12 +901,6 @@ static int wcd_control_deinit(struct device *dev, void *priv_data) struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; - /* Remove the sysfs entries */ - wcd_cntl_sysfs_remove(cntl); - - /* Remove the debugfs entries */ - wcd_cntl_debugfs_remove(cntl); - wcd_cntl_clocks_disable(cntl); wcd_cntl_cpar_ctrl(cntl, false); @@ -951,6 +930,7 @@ static int wcd_ctrl_component_bind(struct device *dev, struct snd_card *card; struct snd_info_entry *entry; char proc_name[WCD_PROCFS_ENTRY_MAX_LEN]; + char wcd_cntl_dir_name[WCD_CNTL_DIR_NAME_LEN_MAX]; int ret = 0; if (!dev || !master || !data) { @@ -982,6 +962,17 @@ static int wcd_ctrl_component_bind(struct device *dev, goto done; } + snprintf(wcd_cntl_dir_name, WCD_CNTL_DIR_NAME_LEN_MAX, + "%s%d", "wdsp", cntl->dsp_instance); + ret = wcd_cntl_sysfs_init(wcd_cntl_dir_name, cntl); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "%s: sysfs_init failed, err = %d\n", + __func__, ret); + goto done; + } + + wcd_cntl_debugfs_init(wcd_cntl_dir_name, cntl); + codec = cntl->codec; card = codec->component.card->snd_card; snprintf(proc_name, WCD_PROCFS_ENTRY_MAX_LEN, "%s%d%s", "cpe", @@ -1032,6 +1023,13 @@ static void wcd_ctrl_component_unbind(struct device *dev, cntl->m_dev = NULL; cntl->m_ops = NULL; + + /* Remove the sysfs entries */ + wcd_cntl_sysfs_remove(cntl); + + /* Remove the debugfs entries */ + wcd_cntl_debugfs_remove(cntl); + } static const struct component_ops wcd_ctrl_component_ops = { @@ -1040,6 +1038,60 @@ static const struct component_ops wcd_ctrl_component_ops = { }; /* + * wcd_dsp_ssr_event: handle the SSR event raised by caller. + * @cntl: Handle to the wcd_dsp_cntl structure + * @event: The SSR event to be handled + * + * Notifies the manager driver about the SSR event. + * Returns 0 on success and negative error code on error. + */ +int wcd_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event) +{ + int ret = 0; + + if (!cntl) { + pr_err("%s: Invalid handle to control\n", __func__); + return -EINVAL; + } + + if (!cntl->m_dev || !cntl->m_ops || !cntl->m_ops->signal_handler) { + dev_err(cntl->codec->dev, + "%s: Invalid signal_handler callback\n", __func__); + return -EINVAL; + } + + switch (event) { + case WCD_CDC_DOWN_EVENT: + ret = cntl->m_ops->signal_handler(cntl->m_dev, + WDSP_CDC_DOWN_SIGNAL, + NULL); + if (IS_ERR_VALUE(ret)) + dev_err(cntl->codec->dev, + "%s: WDSP_CDC_DOWN_SIGNAL failed, err = %d\n", + __func__, ret); + wcd_cntl_change_online_state(cntl, 0); + break; + case WCD_CDC_UP_EVENT: + ret = cntl->m_ops->signal_handler(cntl->m_dev, + WDSP_CDC_UP_SIGNAL, + NULL); + if (IS_ERR_VALUE(ret)) + dev_err(cntl->codec->dev, + "%s: WDSP_CDC_UP_SIGNAL failed, err = %d\n", + __func__, ret); + break; + default: + dev_err(cntl->codec->dev, "%s: Invalid event %d\n", + __func__, event); + ret = -EINVAL; + break; + } + + return ret; +} +EXPORT_SYMBOL(wcd_dsp_ssr_event); + +/* * wcd_dsp_cntl_init: Initialize the wcd-dsp control * @codec: pointer to the codec handle * @params: Parameters required to initialize wcd-dsp control diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h index cd6697b3d641..83c59ed7b676 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h @@ -17,6 +17,11 @@ #include <sound/soc.h> #include <sound/wcd-dsp-mgr.h> +enum cdc_ssr_event { + WCD_CDC_DOWN_EVENT, + WCD_CDC_UP_EVENT, +}; + struct wcd_dsp_cdc_cb { /* Callback to enable codec clock */ int (*cdc_clk_en)(struct snd_soc_codec *, bool); @@ -106,5 +111,5 @@ void wcd_dsp_cntl_init(struct snd_soc_codec *codec, struct wcd_dsp_params *params, struct wcd_dsp_cntl **cntl); void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl); - +int wcd_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event); #endif /* end __WCD_DSP_CONTROL_H__ */ diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index 9e18c17d6f1c..30c23df444d6 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -358,6 +358,15 @@ enum { ASRC_MAX, }; +enum { + CONV_88P2K_TO_384K, + CONV_96K_TO_352P8K, + CONV_352P8K_TO_384K, + CONV_384K_TO_352P8K, + CONV_384K_TO_384K, + CONV_96K_TO_384K, +}; + static struct afe_param_slimbus_slave_port_cfg tavil_slimbus_slave_port_cfg = { .minor_version = 1, .slimbus_dev_id = AFE_SLIMBUS_DEVICE_1, @@ -577,7 +586,7 @@ struct tavil_priv { /* num of slim ports required */ struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS]; /* Port values for Rx and Tx codec_dai */ - unsigned int rx_port_value; + unsigned int rx_port_value[WCD934X_RX_MAX]; unsigned int tx_port_value; struct wcd9xxx_resmgr_v2 *resmgr; @@ -616,6 +625,7 @@ struct tavil_priv { int native_clk_users; /* ASRC users count */ int asrc_users[ASRC_MAX]; + int asrc_output_mode[ASRC_MAX]; /* Main path clock users count */ int main_clk_users[WCD934X_NUM_INTERPOLATORS]; struct tavil_dsd_config *dsd_config; @@ -1298,7 +1308,8 @@ static int slim_rx_mux_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); - ucontrol->value.enumerated.item[0] = tavil_p->rx_port_value; + ucontrol->value.enumerated.item[0] = + tavil_p->rx_port_value[widget->shift]; return 0; } @@ -1313,17 +1324,20 @@ static int slim_rx_mux_put(struct snd_kcontrol *kcontrol, struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct snd_soc_dapm_update *update = NULL; + unsigned int rx_port_value; u32 port_id = widget->shift; + tavil_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; + rx_port_value = tavil_p->rx_port_value[port_id]; + mutex_lock(&tavil_p->codec_mutex); - tavil_p->rx_port_value = ucontrol->value.enumerated.item[0]; dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", __func__, widget->name, ucontrol->id.name, - tavil_p->rx_port_value, widget->shift, + rx_port_value, widget->shift, ucontrol->value.integer.value[0]); /* value need to match the Virtual port and AIF number */ - switch (tavil_p->rx_port_value) { + switch (rx_port_value) { case 0: list_del_init(&core->rx_chs[port_id].list); break; @@ -1372,13 +1386,13 @@ static int slim_rx_mux_put(struct snd_kcontrol *kcontrol, &tavil_p->dai[AIF4_PB].wcd9xxx_ch_list); break; default: - dev_err(codec->dev, "Unknown AIF %d\n", tavil_p->rx_port_value); + dev_err(codec->dev, "Unknown AIF %d\n", rx_port_value); goto err; } rtn: mutex_unlock(&tavil_p->codec_mutex); snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, - tavil_p->rx_port_value, e, update); + rx_port_value, e, update); return 0; err: @@ -1859,8 +1873,9 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, - 0x06, (0x03 << 1)); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, (0x03 << 1)); set_bit(HPH_PA_DELAY, &tavil->status_mask); if (dsd_conf && (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) { @@ -1922,8 +1937,9 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, blocking_notifier_call_chain(&tavil->mbhc->notifier, WCD_EVENT_POST_HPHR_PA_OFF, &tavil->mbhc->wcd_mbhc); - snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, - 0x06, 0x0); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, 0x0); break; }; @@ -1942,8 +1958,9 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, - 0x06, (0x03 << 1)); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, (0x03 << 1)); set_bit(HPH_PA_DELAY, &tavil->status_mask); if (dsd_conf && (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) { @@ -2004,8 +2021,9 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, blocking_notifier_call_chain(&tavil->mbhc->notifier, WCD_EVENT_POST_HPHL_PA_OFF, &tavil->mbhc->wcd_mbhc); - snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, - 0x06, 0x0); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, 0x0); break; }; @@ -2130,9 +2148,10 @@ static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00); /* Set RDAC gain */ - snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, - 0xF0, 0x40); - + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x40); if (dsd_conf && (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) hph_mode = CLS_H_HIFI; @@ -2155,8 +2174,10 @@ static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, WCD934X_SIDO_NEW_VOUT_D_FREQ2, 0x01, 0x0); /* Re-set RDAC gain */ - snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, - 0xF0, 0x0); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x0); break; default: break; @@ -2199,8 +2220,10 @@ static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00); /* Set RDAC gain */ - snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, - 0xF0, 0x40); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x40); if (dsd_conf && (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) hph_mode = CLS_H_HIFI; @@ -2223,8 +2246,10 @@ static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, WCD934X_SIDO_NEW_VOUT_D_FREQ2, 0x01, 0x0); /* Re-set RDAC gain */ - snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, - 0xF0, 0x0); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x0); break; default: break; @@ -2581,6 +2606,45 @@ done: return rc; } +static int tavil_get_asrc_mode(struct tavil_priv *tavil, int asrc, + u8 main_sr, u8 mix_sr) +{ + u8 asrc_output_mode; + int asrc_mode = CONV_88P2K_TO_384K; + + if ((asrc < 0) || (asrc >= ASRC_MAX)) + return 0; + + asrc_output_mode = tavil->asrc_output_mode[asrc]; + + if (asrc_output_mode) { + /* + * If Mix sample rate is < 96KHz, use 96K to 352.8K + * conversion, or else use 384K to 352.8K conversion + */ + if (mix_sr < 5) + asrc_mode = CONV_96K_TO_352P8K; + else + asrc_mode = CONV_384K_TO_352P8K; + } else { + /* Integer main and Fractional mix path */ + if (main_sr < 8 && mix_sr > 9) { + asrc_mode = CONV_352P8K_TO_384K; + } else if (main_sr > 8 && mix_sr < 8) { + /* Fractional main and Integer mix path */ + if (mix_sr < 5) + asrc_mode = CONV_96K_TO_352P8K; + else + asrc_mode = CONV_384K_TO_352P8K; + } else if (main_sr < 8 && mix_sr < 8) { + /* Integer main and Integer mix path */ + asrc_mode = CONV_96K_TO_384K; + } + } + + return asrc_mode; +} + static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, int asrc_in, int event) { @@ -2647,19 +2711,8 @@ static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, main_sr = snd_soc_read(codec, ctl_reg) & 0x0F; mix_ctl_reg = ctl_reg + 5; mix_sr = snd_soc_read(codec, mix_ctl_reg) & 0x0F; - /* Integer main and Fractional mix path */ - if (main_sr < 8 && mix_sr > 9) { - asrc_mode = 2; - } else if (main_sr > 8 && mix_sr < 8) { - /* Fractional main and Integer mix path */ - if (mix_sr < 5) - asrc_mode = 1; - else - asrc_mode = 3; - } else if (main_sr < 8 && mix_sr < 8) { - /* Integer main and Integer mix path */ - asrc_mode = 5; - } + asrc_mode = tavil_get_asrc_mode(tavil, asrc, + main_sr, mix_sr); dev_dbg(codec->dev, "%s: main_sr:%d mix_sr:%d asrc_mode %d\n", __func__, main_sr, mix_sr, asrc_mode); snd_soc_update_bits(codec, asrc_ctl, 0x07, asrc_mode); @@ -2820,11 +2873,15 @@ static void tavil_codec_hphdelay_lutbypass(struct snd_soc_codec *codec, } } -static void tavil_codec_hd2_control(struct snd_soc_codec *codec, +static void tavil_codec_hd2_control(struct tavil_priv *priv, u16 interp_idx, int event) { u16 hd2_scale_reg; u16 hd2_enable_reg = 0; + struct snd_soc_codec *codec = priv->codec; + + if (TAVIL_IS_1_1(priv->wcd9xxx)) + return; switch (interp_idx) { case INTERP_HPHL: @@ -3002,7 +3059,7 @@ int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec, snd_soc_update_bits(codec, main_reg, 0x20, 0x20); tavil_codec_idle_detect_control(codec, interp_idx, event); - tavil_codec_hd2_control(codec, interp_idx, event); + tavil_codec_hd2_control(tavil, interp_idx, event); tavil_codec_hphdelay_lutbypass(codec, interp_idx, event); tavil_config_compander(codec, interp_idx, event); @@ -3017,7 +3074,7 @@ int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec, tavil_config_compander(codec, interp_idx, event); tavil_codec_hphdelay_lutbypass(codec, interp_idx, event); - tavil_codec_hd2_control(codec, interp_idx, event); + tavil_codec_hd2_control(tavil, interp_idx, event); tavil_codec_idle_detect_control(codec, interp_idx, event); /* Clk Disable */ @@ -4737,6 +4794,46 @@ static int tavil_compander_put(struct snd_kcontrol *kcontrol, return 0; } +static int tavil_hph_asrc_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int index = -EINVAL; + + if (!strcmp(kcontrol->id.name, "ASRC0 Output Mode")) + index = ASRC0; + if (!strcmp(kcontrol->id.name, "ASRC1 Output Mode")) + index = ASRC1; + + if (tavil && (index >= 0) && (index < ASRC_MAX)) + tavil->asrc_output_mode[index] = + ucontrol->value.integer.value[0]; + + return 0; +} + +static int tavil_hph_asrc_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int val = 0; + int index = -EINVAL; + + if (!strcmp(kcontrol->id.name, "ASRC0 Output Mode")) + index = ASRC0; + if (!strcmp(kcontrol->id.name, "ASRC1 Output Mode")) + index = ASRC1; + + if (tavil && (index >= 0) && (index < ASRC_MAX)) + val = tavil->asrc_output_mode[index]; + + ucontrol->value.integer.value[0] = val; + + return 0; +} + static int tavil_hph_idle_detect_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -5131,6 +5228,10 @@ static const char * const hph_idle_detect_text[] = { "OFF", "ON" }; +static const char * const asrc_mode_text[] = { + "INT", "FRAC" +}; + static const char * const tavil_ear_pa_gain_text[] = { "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB", "G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB" @@ -5146,6 +5247,7 @@ static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_spkr_pa_gain_enum, tavil_ear_spkr_pa_gain_text); static SOC_ENUM_SINGLE_EXT_DECL(amic_pwr_lvl_enum, amic_pwr_lvl_text); static SOC_ENUM_SINGLE_EXT_DECL(hph_idle_detect_enum, hph_idle_detect_text); +static SOC_ENUM_SINGLE_EXT_DECL(asrc_mode_enum, asrc_mode_text); static SOC_ENUM_SINGLE_DECL(cf_dec0_enum, WCD934X_CDC_TX0_TX_PATH_CFG0, 5, cf_text); static SOC_ENUM_SINGLE_DECL(cf_dec1_enum, WCD934X_CDC_TX1_TX_PATH_CFG0, 5, @@ -5380,6 +5482,11 @@ static const struct snd_kcontrol_new tavil_snd_controls[] = { SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, tavil_compander_get, tavil_compander_put), + SOC_ENUM_EXT("ASRC0 Output Mode", asrc_mode_enum, + tavil_hph_asrc_mode_get, tavil_hph_asrc_mode_put), + SOC_ENUM_EXT("ASRC1 Output Mode", asrc_mode_enum, + tavil_hph_asrc_mode_get, tavil_hph_asrc_mode_put), + SOC_ENUM_EXT("HPH Idle Detect", hph_idle_detect_enum, tavil_hph_idle_detect_get, tavil_hph_idle_detect_put), @@ -7837,7 +7944,11 @@ static const struct wcd_resmgr_cb tavil_resmgr_cb = { .cdc_rco_ctrl = __tavil_codec_internal_rco_ctrl, }; -static const struct tavil_reg_mask_val tavil_codec_mclk2_defaults[] = { +static const struct tavil_reg_mask_val tavil_codec_mclk2_1_1_defaults[] = { + {WCD934X_CLK_SYS_MCLK2_PRG1, 0x60, 0x20}, +}; + +static const struct tavil_reg_mask_val tavil_codec_mclk2_1_0_defaults[] = { /* * PLL Settings: * Clock Root: MCLK2, @@ -7896,6 +8007,13 @@ static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = { {WCD934X_HPH_R_TEST, 0x01, 0x01}, }; +static const struct tavil_reg_mask_val tavil_codec_reg_init_1_1_val[] = { + {WCD934X_CDC_COMPANDER1_CTL7, 0x1E, 0x06}, + {WCD934X_CDC_COMPANDER2_CTL7, 0x1E, 0x06}, + {WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0xFF, 0x84}, + {WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0xFF, 0x84}, +}; + static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = { {WCD934X_CDC_CLSH_K2_MSB, 0x0F, 0x00}, {WCD934X_CDC_CLSH_K2_LSB, 0xFF, 0x60}, @@ -7922,8 +8040,9 @@ static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = { {WCD934X_CPE_SS_SVA_CFG, 0x60, 0x00}, }; -static void tavil_codec_init_reg(struct snd_soc_codec *codec) +static void tavil_codec_init_reg(struct tavil_priv *priv) { + struct snd_soc_codec *codec = priv->codec; u32 i; for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_init_common_val); i++) @@ -7931,6 +8050,14 @@ static void tavil_codec_init_reg(struct snd_soc_codec *codec) tavil_codec_reg_init_common_val[i].reg, tavil_codec_reg_init_common_val[i].mask, tavil_codec_reg_init_common_val[i].val); + + if (TAVIL_IS_1_1(priv->wcd9xxx)) { + for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_init_1_1_val); i++) + snd_soc_update_bits(codec, + tavil_codec_reg_init_1_1_val[i].reg, + tavil_codec_reg_init_1_1_val[i].mask, + tavil_codec_reg_init_1_1_val[i].val); + } } static void tavil_update_reg_defaults(struct tavil_priv *tavil) @@ -8367,11 +8494,22 @@ static void tavil_mclk2_reg_defaults(struct tavil_priv *tavil) int i; struct snd_soc_codec *codec = tavil->codec; - /* MCLK2 configuration */ - for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_defaults); i++) - snd_soc_update_bits(codec, tavil_codec_mclk2_defaults[i].reg, - tavil_codec_mclk2_defaults[i].mask, - tavil_codec_mclk2_defaults[i].val); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) { + /* MCLK2 configuration */ + for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_1_0_defaults); i++) + snd_soc_update_bits(codec, + tavil_codec_mclk2_1_0_defaults[i].reg, + tavil_codec_mclk2_1_0_defaults[i].mask, + tavil_codec_mclk2_1_0_defaults[i].val); + } + if (TAVIL_IS_1_1(tavil->wcd9xxx)) { + /* MCLK2 configuration */ + for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_1_1_defaults); i++) + snd_soc_update_bits(codec, + tavil_codec_mclk2_1_1_defaults[i].reg, + tavil_codec_mclk2_1_1_defaults[i].mask, + tavil_codec_mclk2_1_1_defaults[i].val); + } } static int tavil_soc_codec_probe(struct snd_soc_codec *codec) @@ -8429,7 +8567,7 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec) for (i = 0; i < COMPANDER_MAX; i++) tavil->comp_enabled[i] = 0; - tavil_codec_init_reg(codec); + tavil_codec_init_reg(tavil); tavil_enable_sido_buck(codec); pdata = dev_get_platdata(codec->dev->parent); @@ -8749,6 +8887,9 @@ static int tavil_swrm_clock(void *handle, bool enable) if (enable) { tavil->swr.clk_users++; if (tavil->swr.clk_users == 1) { + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x00); __tavil_cdc_mclk_enable(tavil, true); regmap_update_bits(tavil->wcd9xxx->regmap, WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, @@ -8761,6 +8902,9 @@ static int tavil_swrm_clock(void *handle, bool enable) WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, 0x01, 0x00); __tavil_cdc_mclk_enable(tavil, false); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x10); } } dev_dbg(tavil->dev, "%s: swrm clock users %d\n", diff --git a/sound/soc/codecs/wcd9xxx-common-v2.c b/sound/soc/codecs/wcd9xxx-common-v2.c index 63872bbf540c..47518ec92661 100644 --- a/sound/soc/codecs/wcd9xxx-common-v2.c +++ b/sound/soc/codecs/wcd9xxx-common-v2.c @@ -369,8 +369,9 @@ static inline void wcd_clsh_gm3_boost_disable(struct snd_soc_codec *codec, if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || mode == CLS_AB_HIFI || mode == CLS_AB) { - snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL, - 0x80, 0x0); /* disable GM3 Boost */ + if (TAVIL_IS_1_0(wcd9xxx)) + snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL, + 0x80, 0x0); /* disable GM3 Boost */ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4, 0xF0, 0x80); } else { diff --git a/sound/soc/codecs/wcd9xxx-resmgr-v2.c b/sound/soc/codecs/wcd9xxx-resmgr-v2.c index 29718a8d7c04..39ca965e791e 100644 --- a/sound/soc/codecs/wcd9xxx-resmgr-v2.c +++ b/sound/soc/codecs/wcd9xxx-resmgr-v2.c @@ -307,7 +307,7 @@ static int wcd_resmgr_disable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr) WCD9335_ANA_CLK_TOP, 0x04, 0x00); wcd_resmgr_codec_reg_update_bits(resmgr, - WCD934X_CLK_SYS_MCLK_PRG, 0x01, 0x0); + WCD934X_CLK_SYS_MCLK_PRG, 0x81, 0x00); resmgr->clk_type = WCD_CLK_OFF; } diff --git a/sound/soc/msm/msmcobalt.c b/sound/soc/msm/msmcobalt.c index a2bd3be62175..524f737360d8 100644 --- a/sound/soc/msm/msmcobalt.c +++ b/sound/soc/msm/msmcobalt.c @@ -3180,8 +3180,10 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) * Send speaker configuration only for WSA8810. * Defalut configuration is for WSA8815. */ + pr_debug("%s: Number of aux devices: %d\n", + __func__, rtd->card->num_aux_devs); if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { - if (rtd_aux && rtd_aux->component) + if (rtd->card->num_aux_devs && rtd_aux && rtd_aux->component) if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) || !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) { tavil_set_spkr_mode(rtd->codec, SPKR_MODE_1); @@ -3200,7 +3202,7 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) pdata->codec_root = entry; tavil_codec_info_create_codec_entry(pdata->codec_root, codec); } else { - if (rtd_aux && rtd_aux->component) + if (rtd->card->num_aux_devs && rtd_aux && rtd_aux->component) if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) || !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) { tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1); |
